Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
cdf173d
feat(reborn): add ironclaw_hooks framework foundation (#3524)
zmanian May 13, 2026
1d7eb01
feat(reborn): wire HookDispatcher into LoopCapabilityPort/LoopPromptPort
zmanian May 13, 2026
118f130
feat(reborn): add HookedLoopModelPort/TranscriptPort/CheckpointPort o…
zmanian May 13, 2026
844a43e
test(reborn): end-to-end hooks integration through RebornLoopDriverHo…
zmanian May 13, 2026
9febe83
feat(reborn): add pass() + HookRegistrar + self-authored hooks scaffo…
zmanian May 13, 2026
7e677fa
Merge hooks-bia: pass() + HookRegistrar + self-authored hooks scaffol…
zmanian May 13, 2026
cc69f44
Merge hooks-observer-f: HookedLoopModelPort/TranscriptPort/Checkpoint…
zmanian May 13, 2026
1b2a15a
Merge hooks-reborn-test-h: end-to-end hooks integration through Rebor…
zmanian May 13, 2026
0e5c757
feat(reborn): real gate-ref plumbing for hook PauseApproval/PauseAuth…
zmanian May 13, 2026
9b1297f
feat(reborn): add NumericSum predicate evaluation with capability arg…
zmanian May 13, 2026
e7487de
fix(reborn): seal hook registration trust boundary + dispatcher harde…
zmanian May 13, 2026
44f154b
feat(reborn): emit hook telemetry milestones for audit/SSE observers
zmanian May 13, 2026
bbb8c1f
feat(reborn): extract shared prompt envelope; inject hook patches int…
zmanian May 13, 2026
5479aa7
Merge hooks-codex-fixes: address C1+C2+C5+C6 audit blockers
zmanian May 13, 2026
5bf4cbd
Merge hooks-e-numeric: NumericSum predicate evaluation with arg extra…
zmanian May 13, 2026
8c7f638
fix: align tenant-counter test with SanitizedArguments-extended conte…
zmanian May 13, 2026
9f05a8f
Merge hooks-d-gate-ref: real gate-ref plumbing for PauseApproval/Paus…
zmanian May 13, 2026
a391246
Merge hooks-c-telemetry: hook telemetry milestones
zmanian May 13, 2026
8d01555
Merge hooks-g-envelope: shared prompt envelope + hook patch injection
zmanian May 13, 2026
504d4ff
docs(reborn): document loader contract; pin HookId hex format
zmanian May 13, 2026
c485954
test(reborn): pin hook milestone JSON schema + assert pairing invariants
zmanian May 13, 2026
4003426
test(reborn): integration tests for observer middleware through Rebor…
zmanian May 13, 2026
38c7d6c
refactor(reborn): introduce HookDispatcherBuilder for type-enforced s…
zmanian May 13, 2026
2744aed
feat(reborn): production CapabilityInputResolver for NumericSum predi…
zmanian May 13, 2026
894d361
feat(reborn): per-build HookDispatcher for full per-run isolation (C2)
zmanian May 13, 2026
ecd0d46
feat(reborn): project hook telemetry milestones into RuntimeEvent for…
zmanian May 13, 2026
f42d0f2
feat(reborn): enforce manifest-declared hook scope at dispatch time (C3)
zmanian May 13, 2026
0479697
Merge hooks-fu2-loader-test: loader contract doc + HookId format pin
zmanian May 13, 2026
84bc76f
Merge hooks-fu4-snapshots: milestone schema snapshots + pairing matrix
zmanian May 13, 2026
359673e
Merge hooks-fu3-runtime-events: project hook telemetry into RuntimeEvent
zmanian May 13, 2026
709eb40
Merge hooks-fu6-input-resolver: observer middleware factory wiring + …
zmanian May 13, 2026
80e90c4
Merge hooks-fu5-pairing-id: production CapabilityInputResolver for Nu…
zmanian May 13, 2026
91f9e9c
Merge hooks-fu7-observer-integration: HookDispatcherBuilder for type-…
zmanian May 13, 2026
adcf167
Merge hooks-fu8-per-build: per-build HookDispatcher for full per-run …
zmanian May 13, 2026
3c66006
Merge hooks-fu1-c3-scope: manifest scope enforcement + provider in ho…
zmanian May 13, 2026
664f461
style: rustfmt dispatch.rs after FU1 merge
zmanian May 13, 2026
b0cae2e
docs(hooks): prior-art comparison against LSM/eBPF/Envoy/K8s/OPA/CRX/…
zmanian May 13, 2026
2d176d2
docs(hooks): STRIDE threat model for v1 framework
zmanian May 13, 2026
7350756
feat(hooks): close threat-model gaps S1 (gate-ref entropy) and D3/D4 …
zmanian May 13, 2026
f2555e8
test(hooks): three real hooks built against the public API + ergonomi…
zmanian May 13, 2026
434b547
feat(hooks): close all remaining threat-model and ergonomics gaps
zmanian May 13, 2026
d9b2000
merge: resolve conflicts with reborn-integration + address bot reviews
zmanian May 13, 2026
e6288a5
merge: integrate reborn-integration + address Firat's review findings
zmanian May 13, 2026
a49d558
fix(ci): collapse nested match in hooks_integration test for clippy -…
zmanian May 13, 2026
c991f30
feat(hooks): address henrypark133 review — Critical #1/#2/#3/#5, Conc…
zmanian May 14, 2026
7d714dd
feat(hooks): address remaining henrypark133 review — Critical #4, Con…
zmanian May 14, 2026
5793e4d
merge: integrate origin/reborn-integration
zmanian May 14, 2026
ac4c109
docs(hooks): scope DenyReasonCode closed-vocabulary enum (successor #6)
zmanian May 14, 2026
251ae78
feat(hooks): DenyReasonCode + PauseReasonCode closed-vocabulary enums
zmanian May 14, 2026
c4705aa
test(hooks): address codex review on #3636
zmanian May 14, 2026
829601c
fix(hooks): attenuate Installed-tier prompt patches to user role
zmanian May 14, 2026
61e6e80
fix(hooks): enforce scope filter on observer dispatch + reject incomp…
zmanian May 14, 2026
5c33c74
fix(hooks): preserve free-form audit reason alongside closed-vocab mo…
zmanian May 15, 2026
c5526e8
Merge remote-tracking branch 'origin/reborn-integration' into hooks-f…
zmanian May 15, 2026
4035822
fix(hooks): remove unused model_request helper (CI clippy fix)
zmanian May 15, 2026
564ff9c
Merge remote-tracking branch 'origin/reborn-integration' into hooks-f…
zmanian May 15, 2026
b911223
Merge remote-tracking branch 'origin/reborn-integration' into hooks-f…
zmanian May 15, 2026
496d0ac
fix(hooks): address serrrfirat P1/P2 findings on PR #3573
zmanian May 15, 2026
e9038f9
Merge remote-tracking branch 'origin/reborn-integration' into hooks-f…
zmanian May 15, 2026
86d411d
fix(hooks): address serrrfirat 3573-2026-05-15 review findings
zmanian May 15, 2026
d6c4d79
Merge remote-tracking branch 'origin/reborn-integration' into hooks-f…
zmanian May 15, 2026
8809814
Merge pull request #3636 from nearai/hooks-fu-deny-reason-code
zmanian May 17, 2026
106a409
Merge remote-tracking branch 'origin/reborn-integration' into hooks-f…
zmanian May 17, 2026
05a1355
fix(hooks): address PR #3573 review feedback round 3
zmanian May 22, 2026
427a2fd
test(hooks): batch deferred test coverage from #3573 review (#3914)
zmanian May 23, 2026
a233c55
perf(hooks): defer capability input resolution until a predicate need…
zmanian May 23, 2026
ea78d4e
fix(rebase): adapt hooks tests + middleware to upstream API additions
zmanian May 23, 2026
30378d1
perf(hooks): restore batched capability dispatch when hooks active (#…
zmanian May 23, 2026
19599ed
fix(rebase): add StaticTurnStateStore to hooks_integration tests
zmanian May 23, 2026
f07aa37
refactor(hooks): enforce identity newtype validation at construction …
zmanian May 23, 2026
ae8b25a
style: rustfmt after #3911 / #3912 cherry-picks
zmanian May 23, 2026
ae245c5
fix(rebase): use ExtensionId::new / HookLocalId::new after #3912
zmanian May 23, 2026
69b31b7
Merge remote-tracking branch 'origin/reborn-integration' into hooks-f…
zmanian May 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 113 additions & 100 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = [".", "crates/ironclaw_common", "crates/ironclaw_host_api", "crates/ironclaw_filesystem", "crates/ironclaw_memory", "crates/ironclaw_events", "crates/ironclaw_event_projections", "crates/ironclaw_event_streams", "crates/ironclaw_reborn_event_store", "crates/ironclaw_extensions", "crates/ironclaw_processes", "crates/ironclaw_dispatcher", "crates/ironclaw_scripts", "crates/ironclaw_mcp", "crates/ironclaw_wasm", "crates/ironclaw_wasm_sandbox_core", "crates/ironclaw_capabilities", "crates/ironclaw_secrets", "crates/ironclaw_network", "crates/ironclaw_host_runtime", "crates/ironclaw_runtime_policy", "crates/ironclaw_authorization", "crates/ironclaw_run_state", "crates/ironclaw_approvals", "crates/ironclaw_resources", "crates/ironclaw_auth", "crates/ironclaw_trust", "crates/ironclaw_turns", "crates/ironclaw_agent_loop", "crates/ironclaw_threads", "crates/ironclaw_loop_support", "crates/ironclaw_reborn", "crates/ironclaw_reborn_config", "crates/ironclaw_reborn_composition", "crates/ironclaw_first_party_extensions", "crates/ironclaw_reborn_cli", "crates/ironclaw_reborn_traces", "crates/ironclaw_reborn_webui_ingress", "crates/ironclaw_conversations", "crates/ironclaw_product_adapters", "crates/ironclaw_product_workflow", "crates/ironclaw_product_workflow_storage", "crates/ironclaw_product_adapter_registry", "crates/ironclaw_wasm_product_adapters", "crates/ironclaw_telegram_v2_adapter", "crates/ironclaw_outbound", "crates/ironclaw_architecture", "crates/ironclaw_safety", "crates/ironclaw_skills", "crates/ironclaw_llm", "crates/ironclaw_engine", "crates/ironclaw_gateway", "crates/ironclaw_tui", "crates/ironclaw_webui_v2"]
members = [".", "crates/ironclaw_common", "crates/ironclaw_host_api", "crates/ironclaw_filesystem", "crates/ironclaw_memory", "crates/ironclaw_events", "crates/ironclaw_event_projections", "crates/ironclaw_event_streams", "crates/ironclaw_reborn_event_store", "crates/ironclaw_extensions", "crates/ironclaw_processes", "crates/ironclaw_dispatcher", "crates/ironclaw_scripts", "crates/ironclaw_mcp", "crates/ironclaw_wasm", "crates/ironclaw_wasm_sandbox_core", "crates/ironclaw_capabilities", "crates/ironclaw_secrets", "crates/ironclaw_network", "crates/ironclaw_host_runtime", "crates/ironclaw_runtime_policy", "crates/ironclaw_authorization", "crates/ironclaw_run_state", "crates/ironclaw_approvals", "crates/ironclaw_resources", "crates/ironclaw_auth", "crates/ironclaw_trust", "crates/ironclaw_turns", "crates/ironclaw_agent_loop", "crates/ironclaw_threads", "crates/ironclaw_prompt_envelope", "crates/ironclaw_hooks", "crates/ironclaw_loop_support", "crates/ironclaw_reborn", "crates/ironclaw_reborn_config", "crates/ironclaw_reborn_composition", "crates/ironclaw_first_party_extensions", "crates/ironclaw_reborn_cli", "crates/ironclaw_reborn_traces", "crates/ironclaw_reborn_webui_ingress", "crates/ironclaw_conversations", "crates/ironclaw_product_adapters", "crates/ironclaw_product_workflow", "crates/ironclaw_product_workflow_storage", "crates/ironclaw_product_adapter_registry", "crates/ironclaw_wasm_product_adapters", "crates/ironclaw_telegram_v2_adapter", "crates/ironclaw_outbound", "crates/ironclaw_architecture", "crates/ironclaw_safety", "crates/ironclaw_skills", "crates/ironclaw_llm", "crates/ironclaw_engine", "crates/ironclaw_gateway", "crates/ironclaw_tui", "crates/ironclaw_webui_v2"]
exclude = [
"channels-src/discord",
"channels-src/feishu",
Expand Down
26 changes: 26 additions & 0 deletions crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1957,6 +1957,7 @@ fn boundary_rules() -> Vec<BoundaryRule> {
// routes turn-coordination persistence through ScopedFilesystem
// under the universal-fs-dispatch rework (plan
// 2026-05-14-universal-fs-dispatch).
"ironclaw_hooks",
"ironclaw_host_runtime",
"ironclaw_mcp",
"ironclaw_memory",
Expand All @@ -1968,6 +1969,31 @@ fn boundary_rules() -> Vec<BoundaryRule> {
"ironclaw_wasm",
],
},
// The hooks framework depends on `ironclaw_turns` and host primitives
// but must not pull in runtime adapters or dispatcher concretions.
// This keeps the contract surface narrow and prevents the framework
// from acquiring authority it should not have.
BoundaryRule {
crate_name: "ironclaw_hooks",
forbidden: vec![
"ironclaw_approvals",
"ironclaw_authorization",
"ironclaw_capabilities",
"ironclaw_dispatcher",
"ironclaw_extensions",
"ironclaw_filesystem",
"ironclaw_host_runtime",
"ironclaw_mcp",
"ironclaw_memory",
"ironclaw_network",
"ironclaw_processes",
"ironclaw_reborn",
"ironclaw_run_state",
"ironclaw_scripts",
"ironclaw_secrets",
"ironclaw_wasm",
],
},
BoundaryRule {
crate_name: "ironclaw_capabilities",
forbidden: vec![
Expand Down
39 changes: 39 additions & 0 deletions crates/ironclaw_event_projections/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,24 @@ pub struct TimelineEntry {
pub process_id: Option<ProcessId>,
pub output_bytes: Option<u64>,
pub error_kind: Option<String>,
/// Sanitized hook metadata. Populated only when `kind` is one of the
/// `Hook*` variants — for other kinds these fields are `None`.
/// Each field is a *closed-vocabulary* label (no free-form text), so
/// replay consumers can pattern-match on the actual hook that
/// fired/failed without burning audit budget on operator-supplied
/// reason strings (henrypark133 Concerning #6).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hook_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hook_point: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hook_trust_class: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hook_decision: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hook_failure_category: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hook_failure_disposition: Option<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -198,6 +216,9 @@ pub enum TimelineEntryKind {
ProcessCompleted,
ProcessFailed,
ProcessKilled,
HookDispatched,
HookDecisionEmitted,
HookFailed,
}

impl From<RuntimeEventKind> for TimelineEntryKind {
Expand All @@ -218,6 +239,9 @@ impl From<RuntimeEventKind> for TimelineEntryKind {
RuntimeEventKind::ProcessCompleted => Self::ProcessCompleted,
RuntimeEventKind::ProcessFailed => Self::ProcessFailed,
RuntimeEventKind::ProcessKilled => Self::ProcessKilled,
RuntimeEventKind::HookDispatched => Self::HookDispatched,
RuntimeEventKind::HookDecisionEmitted => Self::HookDecisionEmitted,
RuntimeEventKind::HookFailed => Self::HookFailed,
}
}
}
Expand Down Expand Up @@ -1543,6 +1567,12 @@ fn project_timeline_entry(entry: &EventLogEntry<RuntimeEvent>) -> TimelineEntry
process_id: event.process_id,
output_bytes: event.output_bytes,
error_kind: event.error_kind.clone().map(sanitize_error_kind),
hook_id: event.hook_id.clone(),
hook_point: event.hook_point.clone(),
hook_trust_class: event.hook_trust_class.clone(),
hook_decision: event.hook_decision.clone(),
hook_failure_category: event.hook_failure_category.clone(),
hook_failure_disposition: event.hook_failure_disposition.clone(),
}
}

Expand Down Expand Up @@ -1664,6 +1694,15 @@ fn run_status_for_event(
| RuntimeEventKind::LoopFailed
| RuntimeEventKind::ProcessFailed => RunProjectionStatus::Failed,
RuntimeEventKind::ProcessKilled => RunProjectionStatus::Killed,
// Hook events are pure observability telemetry. They never change the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium] Hook events run-status behavior is untested

run_status_for_event now treats hook telemetry as non-terminal and preserves the current run status, defaulting to Running when hook telemetry is first. Adjacent replay tests assert hook metadata projection but do not assert run status for hook-only streams or hook events after terminal events.

Fix: tests::replay_projection_contract::hook_runtime_events_do_not_change_run_status covering hook-only and post-terminal status preservation

Reviewer: tests; confidence: 100.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred. run_status_for_event is in ironclaw_event_projections, outside the surface I touched for the install/dispatch hardening in 57a6429. Adding the requested hook_runtime_events_do_not_change_run_status test is straightforward but scoped to the projections review pass.

// run's lifecycle status — a hook firing or even failing inside the
// dispatcher does not by itself end the run (capability/model/process
// events do). Preserve the current status, defaulting to `Running` for
// the boundary case where the first event seen for a run is a hook
// milestone.
RuntimeEventKind::HookDispatched
| RuntimeEventKind::HookDecisionEmitted
| RuntimeEventKind::HookFailed => current_status.unwrap_or(RunProjectionStatus::Running),
}
}

Expand Down
Loading
Loading