refactor(contracts): extract ironclaw_loop_contracts and flip agent_loop (WS1.2) - #6975
Conversation
…ire the turns shims (WS1.1) `ironclaw_host_api::turn` becomes the complete canonical turn vocabulary: it absorbs `TurnStatus` (with the inseparable `GateKind`/`BlockedReason` gate correspondence), `EventCursor`, and `RunOriginAdapter`. The three `ironclaw_turns` re-export shims named by CHECKLIST WS1.1 are deleted — `src/ids.rs`, `src/scope.rs`, and the whole `src/product_adapter/` module, whose `fakes.rs` moves beside the traits it implements in `host_api::product_adapter::test_support`. `ids.rs` carried `pub type GateRef = TurnGateRef`: a second name for a host_api type that collided with the unrelated `ironclaw_host_api::ids::GateRef` (an opaque uuid GateRecord key, versus turns' bounded `gate:`-prefixed routing string). The alias is retired rather than relocated, so the workspace now has exactly one `GateRef`. The six vocabulary-only consumers — auth, event_streams, outbound, telegram_extension, triggers, event_projections — import from `ironclaw_host_api::turn` and drop their `ironclaw_turns` dependency entirely. Five `*→turns` LAYER_MATRIX_EXCEPTIONS are therefore not waived but obsolete: the edges no longer exist. The §11.2.2 ratchet baseline moves 20 → 15. No behavior change. `RunOriginAdapter`'s validation error becomes `Result<_, String>` (matching every other bounded ref in `host_api::turn`) with a byte-identical message pinned by a test, so both production `e.to_string()` call sites are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WS1.1's box is ticked with what the change actually landed, including the three lead-sheet corrections it turned up: the row named `TurnStatus` but not `EventCursor`/`RunOriginAdapter` (which the six consumers genuinely needed), `GateKind`/`BlockedReason` could not be left behind without duplicating the single `GateKind -> TurnStatus` match table, and deleting `ids.rs` forced retiring its `GateRef` alias rather than relocating it. Two decisions confirmed outside the doc and never recorded: - Strategy B (family dirs + focused crates) — confirmed 2026-07-31 by the owner, recorded retroactively; it was made in practice at program start. - The `tools/` row's `default-members` trim — resolved as no trim. Also surfaces #6963 on the WS0 blocking-prerequisite row's first line (it was already cited mid-paragraph) and records the §11.2.2 exception ratchet moving 20 -> 15. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…TurnGateRef contract CodeRabbit review round on #6967. Import repoints (accepted): every `use` line this PR already rewrote now names `ironclaw_host_api::turn` directly instead of routing through `ironclaw_turns`' prelude — 57 files across extension_host, product, composition, runner, loop_host, conversations, the integration harness, and the stress tool, plus three inside `ironclaw_turns` itself so the crate stops consuming its own facade. Import lines this PR did not touch are left for their consumer's own repoint slot. TurnGateRef contract pinned (refutation): two review comments claimed `TurnGateRef::new` only accepts `gate:approval-`/`gate:auth-` prefixes and that fixtures like "gate-alpha" and "stress-gate:{run_id}" fail construction. They do not — `TurnGateRef` is `bounded_ref!` (non-empty, <= 256 bytes, no control characters); `LoopGateRef` is the prefix- validated family via `loop_ref!(.., "gate:")`. The misreading traces to this PR's own AGENTS.md wording ("bounded `gate:`-prefixed routing string"), which stated a minting convention as if it were validation. That wording is corrected and the distinction is now pinned by a test. Also: drop a stale cross-file line reference in a product test comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The `tools/` row resolved the `default-members` trim as "no trim" but kept a trailing "The `tools/`/`default-members` half is still open." from before that decision, so the row asserted both states. Drop the stale sentence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Finishes the remediation deferred last round, now that the settled run (job 91246493989) provides authoritative line numbers. Manifest-only — no .rs file changes, so the changed-line set the gate computes is unchanged and these numbers stay valid for the next run. Derived, not transcribed: the gate was replayed locally against its own merged lcov from that run, reproducing CI's failure byte-identically first (95.17%, 138/145, same 13 files, same 7 lines), then re-run after each entry. Final local result: 100.00% (138/138), branch 100% (4/4), exit 0. All 45 gate self-tests pass. Two classes, both verified rather than asserted: - 13 files x 20 lines - declaration lines (fn params, return types, struct fields) whose only edit is the type NAME: GateRef -> TurnGateRef, or ironclaw_turns::X -> ironclaw_host_api::turn::X. Declarations are not executable, so these files contribute a zero denominator and trip the fail-closed empty_denominator branch. - 7 lines x 3 files - executable, instrumented, and genuinely not exercised by the integration tier. Each checked against the base merged lcov (main @ 67088a4, the PR's own base sha): identical 0 hits before and after, so no coverage was lost. approval_prompt_ context_view is uncovered across its whole signature at base (lines 505-511); the background spawn-mode arm and the invalid-gate- ref error path likewise. This includes the two entries I refused to guess last round - turn_events.rs (three identical candidate lines by text; the settled run disambiguates it as 510) and await_edge/store.rs (no verbatim twin after the repoint; authoritatively 268-272). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oop (WS1.2) Carve the loop tier's neutral contracts out of the turn kernel into a new contracts-layer crate per PROPOSAL 6.1.4, and repoint every consumer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…new crate Enforcement, CI registration, and guidance for the WS1.2 extraction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
Important Review skippedToo many files! This PR contains 338 files, which is 38 over the limit of 300. To get a review, narrow the scope: Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (338)
You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🚅 Deployed to the ironclaw-pr-6975 environment in ironclaw-ci-preview
|
…he two crates The claim types moved to ironclaw_loop_contracts with WS1.2; the validator policy and the trusted applier stayed in the turn kernel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two HostManagedLoop*Port impls stayed in ironclaw_turns, so same-crate links to them no longer resolve; the TurnRunId link target became redundant when the import repoint fully qualified it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removing the ironclaw_runner re-export (required by the new port-location scan) left two workspace-root test-support files importing the turn kernel's evidence types through it. They now import from ironclaw_turns::loop_exit directly, which is the single sanctioned path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#6967 landed as a squash, so this branch carries the parent's original commits while main carries their collapsed equivalent. Merging reconciles the two shapes; the result must be main plus exactly the WS1.2 delta. # Conflicts: # crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs # crates/ironclaw_host_api/src/turn.rs # crates/ironclaw_host_runtime/tests/memory_prompt_context.rs # crates/ironclaw_loop_contracts/src/host/checkpoint.rs # crates/ironclaw_loop_contracts/src/memory_context.rs # crates/ironclaw_loop_contracts/tests/memory_prompt_context_service.rs # crates/ironclaw_loop_host/src/subagent_spawn_port.rs # crates/ironclaw_product/src/communication_context.rs # crates/ironclaw_product/src/projection/tests.rs # crates/ironclaw_product/src/projection/turn_events.rs # crates/ironclaw_product/src/reborn_services/types.rs # crates/ironclaw_reborn_composition/src/llm_admin/openai_compat_serve.rs # crates/ironclaw_reborn_composition/src/runtime.rs # crates/ironclaw_reborn_composition/src/runtime/capability_host/tests.rs # crates/ironclaw_reborn_composition/src/runtime/tests/core.rs # crates/ironclaw_runner/src/loop_exit_applier/tests/mod.rs # crates/ironclaw_runner/src/loop_exit_applier/tests/support.rs # crates/ironclaw_runner/src/subagent/await_edge/resolver.rs # crates/ironclaw_turns/src/agent_turn_runtime.rs # crates/ironclaw_turns/src/coordinator.rs # crates/ironclaw_turns/src/lib.rs # crates/ironclaw_turns/src/loop_exit.rs # crates/ironclaw_turns/src/loop_exit/tests/mod.rs # crates/ironclaw_turns/src/origin.rs # crates/ironclaw_turns/src/process_projection/runtime.rs # crates/ironclaw_turns/src/process_projection/tests.rs # crates/ironclaw_turns/src/request.rs # crates/ironclaw_turns/src/status.rs # crates/ironclaw_turns/tests/agent_loop_host_contract.rs # docs/reborn/target-architecture/CHECKLIST.md # tests/integration/support/comm_context.rs # tests/integration/support/harness/mod.rs # tests/integration/support/harness/recorder.rs # tests/integration/support/triggered_submit.rs # tests/support/reborn_parity_qa/binary_e2e.rs # tools/ironclaw_stress/src/user_turn.rs
…scan Two artifacts of collapsing onto main: - Cargo.lock pinned thiserror 2.0.18 for the new ironclaw_loop_contracts entry while main's dependency bump moved the workspace to 2.0.19. The auto-merge kept the stale pin because the bump predates the crate, so --locked builds failed. - ironclaw_product's failure-summary test reaches into another crate's source with include_str! and scans it for 'impl LoopFailureKind'. WS1.2 moved that impl to ironclaw_loop_contracts, so the include still resolved and matched nothing. Repointed to follow the code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
scripts/reborn-e2e-rust.sh pins exact test names for the deterministic gate. The capability-failure rehydration test moved from ironclaw_turns::run_profile::host::capability to ironclaw_loop_contracts::host::capability, so its selector matched zero tests and the gate failed closed. Swept all 10 pinned selectors in that script (4 lib + 6 integration target); this was the only stale one. Each now resolves to exactly one test, verified by running the selector. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coverage ratchetReborn integration-tier coverageLine coverage (Reborn crates): 85.87% — 323263 / 376442 lines Per-crate breakdown (61 crates, lowest-covered first)
This table itself is informational and never gates the PR on its own — not the percentage, not the per-crate holes, not the 0-coverage callout. A separate coverage ratchet (dry-run until enforce=true; see tests/integration/coverage-floor.toml) can fail the build on specific configured floors. Exemptions (18 entry/entries excluded from the accounting above)
|
The changed-coverage manifest carried a WS1.1 exemption for crates/ironclaw_turns/src/run_profile/runtime_context.rs:575-576. WS1.2 moved that file into ironclaw_loop_contracts, so the gate's fail-closed path validator rejected the manifest before reaching its line-level verdict. Deleted rather than repointed: WS1.1 merged, so those lines are baseline on main, and this PR's diff pairs the file as a 99%-similarity rename whose only changed lines are imports. Repointing would re-exempt lines the gate no longer flags. All 19 remaining entries verified to resolve. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tions Merge brings in tests/e2e/scenarios/test_reborn_webui_v2_custom_mcp.py, added on main after the last merge-down; the WebUI-smoke and E2E roll-up reds were purely the missing file. Exemptions derived by replaying scripts/ci/reborn_changed_coverage.py against this PR's own merged lcov artifact until it exits 0 (100% line 244/244, 100% branch 8/8) - never estimated. Three classes: - type-path repoints on declaration/expression fragments; - verbatim-moved bodies in the new crate. Explicitly NOT counter-attribution: those files are instrumented in this lcov and partially hit (loop_exit 195/109, model 86/52, checkpoint_payload 32/16), which proves the crate is measured. The same bodies were equally unexercised by the integration tier before the move, when they sat in ironclaw_turns and simply were not changed lines; - one crate-root inner attribute the uninstrumentable-line classifier does not recognise on a declaration-only facade. Also adds the #6524 declaration-only facade entry for the new crate's lib.rs to the informational per-crate coverage summary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Coordinator sign-off (Wave 1 slot 2 — WS1.2 Verified across the full lifecycle, on the final head with the complete 64-check set settled green:
Ready to merge. Stack note: #6977 ( |
…n record with shipped reality (#6995) * docs(target-architecture): Wave 1 truth audit — reconcile the decision record with shipped reality Wave 1 merged as seven PRs (#6967, #6975, #6977, #6979, #6980, #6981, #6982). This audits the north-star docs against merged `main` at `a50ad0638` and closes every gap where the decision record no longer matches what shipped. Docs-only: five `.md` files under `docs/reborn/target-architecture/`. House style throughout — dated ✎ amendments, prior text quoted where a clause is corrected, no silent rewrites (`git diff --word-diff` removes nothing but the words each amendment quotes back). Highlights: - §8.3's exception-dissolution proof corrected: `conversations → turns` is turn admission authority, not vocabulary; the wave's "20 → 12" milestone was wrong by construction and the true end-state is 13. - §6.1.1–§6.1.4 gain as-built module inventories; the #6930 amendment placing `hosted_mcp` in `host_api` is superseded by #6977's relocation. - §12.1a records the evidence-mint finding: the `host-auth-mint` feature seal was vacuous, replaced by witness grants — plus two residuals, one from the slice and one this audit verified against the ratchet source. - The coverage-governance gap (~14k lines now ungated by the floor file's opt-in design) moves from a sign-off comment onto the ratchet row. - Duplicate-type findings, the `llm_costs`/`ModelCostTable` deferral, the surviving `product → loop_host` sites, and issues #6945/#6978 all gain owning rows. Verification: docs-only diff; `cargo test -p ironclaw_architecture` green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(target-architecture): tighten the Wave 1 audit where review found it overstated Six review findings triaged against the built tree; four were real. - `families/contracts.md` landing marker claimed "everything else in this file was built as written". Three `ironclaw_loop_contracts` divergences contradict it and are now named at the marker and marked at the entry: the manifest holds `ironclaw_extension_contracts` and holds neither `ironclaw_common` nor `ironclaw_prompt_envelope`; the `tokio` carve-out; the embedded prompt asset. - The evidence-mint guarantee said "compile-time impossibility" and "enforced by constructor visibility plus a workspace string-scan pin" in one breath. Split: the compiler enforces no-mint-without-a-grant (so nothing outside a workspace crate can mint at all); an architecture test — a line-oriented substring scan with two named evasions — decides which workspace crate may hold one. - That deferral had no home. §12.1a said "hardening the scan is WS10 work, listed there"; it was not listed. Added to the WS10 guardrail row with both evasions and the call-site census that backstops them. - CHECKLIST WS8's `common/src/event.rs` row still closed with "stays in `common` as a deletion candidate" under an "executed by #6982" header. The file is deleted; the tail now says so. Plus two clarity fixes where a reader could reach a wrong number: the `(66)`/`(64)` figures in §9/§13 are that table's 2026-07-30 measurement and now say so beside the live 67, and the row-1 edge count now states that six of row 1's seven fell while the register moved by seven, because `auth → turns` is row 9. Dated amendments only; every replaced phrase is quoted in place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…oop (WS1.2) (nearai#6975) * refactor(contracts): complete the turn vocabulary in host_api and retire the turns shims (WS1.1) `ironclaw_host_api::turn` becomes the complete canonical turn vocabulary: it absorbs `TurnStatus` (with the inseparable `GateKind`/`BlockedReason` gate correspondence), `EventCursor`, and `RunOriginAdapter`. The three `ironclaw_turns` re-export shims named by CHECKLIST WS1.1 are deleted — `src/ids.rs`, `src/scope.rs`, and the whole `src/product_adapter/` module, whose `fakes.rs` moves beside the traits it implements in `host_api::product_adapter::test_support`. `ids.rs` carried `pub type GateRef = TurnGateRef`: a second name for a host_api type that collided with the unrelated `ironclaw_host_api::ids::GateRef` (an opaque uuid GateRecord key, versus turns' bounded `gate:`-prefixed routing string). The alias is retired rather than relocated, so the workspace now has exactly one `GateRef`. The six vocabulary-only consumers — auth, event_streams, outbound, telegram_extension, triggers, event_projections — import from `ironclaw_host_api::turn` and drop their `ironclaw_turns` dependency entirely. Five `*→turns` LAYER_MATRIX_EXCEPTIONS are therefore not waived but obsolete: the edges no longer exist. The §11.2.2 ratchet baseline moves 20 → 15. No behavior change. `RunOriginAdapter`'s validation error becomes `Result<_, String>` (matching every other bounded ref in `host_api::turn`) with a byte-identical message pinned by a test, so both production `e.to_string()` call sites are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(target-architecture): tick WS1.1 and close PLAN decision round #1 WS1.1's box is ticked with what the change actually landed, including the three lead-sheet corrections it turned up: the row named `TurnStatus` but not `EventCursor`/`RunOriginAdapter` (which the six consumers genuinely needed), `GateKind`/`BlockedReason` could not be left behind without duplicating the single `GateKind -> TurnStatus` match table, and deleting `ids.rs` forced retiring its `GateRef` alias rather than relocating it. Two decisions confirmed outside the doc and never recorded: - Strategy B (family dirs + focused crates) — confirmed 2026-07-31 by the owner, recorded retroactively; it was made in practice at program start. - The `tools/` row's `default-members` trim — resolved as no trim. Also surfaces nearai#6963 on the WS0 blocking-prerequisite row's first line (it was already cited mid-paragraph) and records the §11.2.2 exception ratchet moving 20 -> 15. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(contracts): repoint touched imports to host_api and pin the TurnGateRef contract CodeRabbit review round on nearai#6967. Import repoints (accepted): every `use` line this PR already rewrote now names `ironclaw_host_api::turn` directly instead of routing through `ironclaw_turns`' prelude — 57 files across extension_host, product, composition, runner, loop_host, conversations, the integration harness, and the stress tool, plus three inside `ironclaw_turns` itself so the crate stops consuming its own facade. Import lines this PR did not touch are left for their consumer's own repoint slot. TurnGateRef contract pinned (refutation): two review comments claimed `TurnGateRef::new` only accepts `gate:approval-`/`gate:auth-` prefixes and that fixtures like "gate-alpha" and "stress-gate:{run_id}" fail construction. They do not — `TurnGateRef` is `bounded_ref!` (non-empty, <= 256 bytes, no control characters); `LoopGateRef` is the prefix- validated family via `loop_ref!(.., "gate:")`. The misreading traces to this PR's own AGENTS.md wording ("bounded `gate:`-prefixed routing string"), which stated a minting convention as if it were validation. That wording is corrected and the distinction is now pinned by a test. Also: drop a stale cross-file line reference in a product test comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(target-architecture): remove the row-97 self-contradiction The `tools/` row resolved the `default-members` trim as "no trim" but kept a trailing "The `tools/`/`default-members` half is still open." from before that decision, so the row asserted both states. Drop the stale sentence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(coverage): complete the WS1.1 changed-coverage exemptions Finishes the remediation deferred last round, now that the settled run (job 91246493989) provides authoritative line numbers. Manifest-only — no .rs file changes, so the changed-line set the gate computes is unchanged and these numbers stay valid for the next run. Derived, not transcribed: the gate was replayed locally against its own merged lcov from that run, reproducing CI's failure byte-identically first (95.17%, 138/145, same 13 files, same 7 lines), then re-run after each entry. Final local result: 100.00% (138/138), branch 100% (4/4), exit 0. All 45 gate self-tests pass. Two classes, both verified rather than asserted: - 13 files x 20 lines - declaration lines (fn params, return types, struct fields) whose only edit is the type NAME: GateRef -> TurnGateRef, or ironclaw_turns::X -> ironclaw_host_api::turn::X. Declarations are not executable, so these files contribute a zero denominator and trip the fail-closed empty_denominator branch. - 7 lines x 3 files - executable, instrumented, and genuinely not exercised by the integration tier. Each checked against the base merged lcov (main @ 67088a4, the PR's own base sha): identical 0 hits before and after, so no coverage was lost. approval_prompt_ context_view is uncovered across its whole signature at base (lines 505-511); the background spawn-mode arm and the invalid-gate- ref error path likewise. This includes the two entries I refused to guess last round - turn_events.rs (three identical candidate lines by text; the settled run disambiguates it as 510) and await_edge/store.rs (no verbatim twin after the repoint; authoritatively 268-272). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(contracts): extract ironclaw_loop_contracts and flip agent_loop (WS1.2) Carve the loop tier's neutral contracts out of the turn kernel into a new contracts-layer crate per PROPOSAL 6.1.4, and repoint every consumer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(contracts): pin the loop-contract boundary and register the new crate Enforcement, CI registration, and guidance for the WS1.2 extraction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(reborn): split the loop-exit contract's ownership claim across the two crates The claim types moved to ironclaw_loop_contracts with WS1.2; the validator policy and the trusted applier stayed in the turn kernel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(contracts): repoint the three intra-doc links the crate split broke The two HostManagedLoop*Port impls stayed in ironclaw_turns, so same-crate links to them no longer resolve; the TurnRunId link target became redundant when the import repoint fully qualified it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(tests): repoint the loop-exit evidence imports the port rule moved Removing the ironclaw_runner re-export (required by the new port-location scan) left two workspace-root test-support files importing the turn kernel's evidence types through it. They now import from ironclaw_turns::loop_exit directly, which is the single sanctioned path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(collapse): reconcile the lock pin and the moved failure-category scan Two artifacts of collapsing onto main: - Cargo.lock pinned thiserror 2.0.18 for the new ironclaw_loop_contracts entry while main's dependency bump moved the workspace to 2.0.19. The auto-merge kept the stale pin because the bump predates the crate, so --locked builds failed. - ironclaw_product's failure-summary test reaches into another crate's source with include_str! and scans it for 'impl LoopFailureKind'. WS1.2 moved that impl to ironclaw_loop_contracts, so the include still resolved and matched nothing. Repointed to follow the code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): repoint the exact-test selector WS1.2 moved scripts/reborn-e2e-rust.sh pins exact test names for the deterministic gate. The capability-failure rehydration test moved from ironclaw_turns::run_profile::host::capability to ironclaw_loop_contracts::host::capability, so its selector matched zero tests and the gate failed closed. Swept all 10 pinned selectors in that script (4 lib + 6 integration target); this was the only stale one. Each now resolves to exactly one test, verified by running the selector. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(coverage): drop the exemption WS1.2 made stale The changed-coverage manifest carried a WS1.1 exemption for crates/ironclaw_turns/src/run_profile/runtime_context.rs:575-576. WS1.2 moved that file into ironclaw_loop_contracts, so the gate's fail-closed path validator rejected the manifest before reaching its line-level verdict. Deleted rather than repointed: WS1.1 merged, so those lines are baseline on main, and this PR's diff pairs the file as a 99%-similarity rename whose only changed lines are imports. Repointing would re-exempt lines the gate no longer flags. All 19 remaining entries verified to resolve. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(coverage): merge main and derive the WS1.2 changed-coverage exemptions Merge brings in tests/e2e/scenarios/test_reborn_webui_v2_custom_mcp.py, added on main after the last merge-down; the WebUI-smoke and E2E roll-up reds were purely the missing file. Exemptions derived by replaying scripts/ci/reborn_changed_coverage.py against this PR's own merged lcov artifact until it exits 0 (100% line 244/244, 100% branch 8/8) - never estimated. Three classes: - type-path repoints on declaration/expression fragments; - verbatim-moved bodies in the new crate. Explicitly NOT counter-attribution: those files are instrumented in this lcov and partially hit (loop_exit 195/109, model 86/52, checkpoint_payload 32/16), which proves the crate is measured. The same bodies were equally unexercised by the integration tier before the move, when they sat in ironclaw_turns and simply were not changed lines; - one crate-root inner attribute the uninstrumentable-line classifier does not recognise on a declaration-only facade. Also adds the nearai#6524 declaration-only facade entry for the new crate's lib.rs to the informational per-crate coverage summary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…n record with shipped reality (nearai#6995) * docs(target-architecture): Wave 1 truth audit — reconcile the decision record with shipped reality Wave 1 merged as seven PRs (nearai#6967, nearai#6975, nearai#6977, nearai#6979, nearai#6980, nearai#6981, nearai#6982). This audits the north-star docs against merged `main` at `a50ad0638` and closes every gap where the decision record no longer matches what shipped. Docs-only: five `.md` files under `docs/reborn/target-architecture/`. House style throughout — dated ✎ amendments, prior text quoted where a clause is corrected, no silent rewrites (`git diff --word-diff` removes nothing but the words each amendment quotes back). Highlights: - §8.3's exception-dissolution proof corrected: `conversations → turns` is turn admission authority, not vocabulary; the wave's "20 → 12" milestone was wrong by construction and the true end-state is 13. - §6.1.1–§6.1.4 gain as-built module inventories; the nearai#6930 amendment placing `hosted_mcp` in `host_api` is superseded by nearai#6977's relocation. - §12.1a records the evidence-mint finding: the `host-auth-mint` feature seal was vacuous, replaced by witness grants — plus two residuals, one from the slice and one this audit verified against the ratchet source. - The coverage-governance gap (~14k lines now ungated by the floor file's opt-in design) moves from a sign-off comment onto the ratchet row. - Duplicate-type findings, the `llm_costs`/`ModelCostTable` deferral, the surviving `product → loop_host` sites, and issues nearai#6945/nearai#6978 all gain owning rows. Verification: docs-only diff; `cargo test -p ironclaw_architecture` green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(target-architecture): tighten the Wave 1 audit where review found it overstated Six review findings triaged against the built tree; four were real. - `families/contracts.md` landing marker claimed "everything else in this file was built as written". Three `ironclaw_loop_contracts` divergences contradict it and are now named at the marker and marked at the entry: the manifest holds `ironclaw_extension_contracts` and holds neither `ironclaw_common` nor `ironclaw_prompt_envelope`; the `tokio` carve-out; the embedded prompt asset. - The evidence-mint guarantee said "compile-time impossibility" and "enforced by constructor visibility plus a workspace string-scan pin" in one breath. Split: the compiler enforces no-mint-without-a-grant (so nothing outside a workspace crate can mint at all); an architecture test — a line-oriented substring scan with two named evasions — decides which workspace crate may hold one. - That deferral had no home. §12.1a said "hardening the scan is WS10 work, listed there"; it was not listed. Added to the WS10 guardrail row with both evasions and the call-site census that backstops them. - CHECKLIST WS8's `common/src/event.rs` row still closed with "stays in `common` as a deletion candidate" under an "executed by nearai#6982" header. The file is deleted; the tail now says so. Plus two clarity fixes where a reader could reach a wrong number: the `(66)`/`(64)` figures in §9/§13 are that table's 2026-07-30 measurement and now say so beside the live 67, and the row-1 edge count now states that six of row 1's seven fell while the register moved by seven, because `auth → turns` is row 9. Dated amendments only; every replaced phrase is quoted in place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Stacked on #6967 — merges after it; diff shown here is against that branch.
WS1.2: carve the loop tier's neutral contracts out of the turn kernel into
crates/ironclaw_loop_contracts(PROPOSAL §6.1.4), flipironclaw_agent_looponto it, and land the enforcement + CI registration the new crate owes. Flat tree — thecontracts/family directory arrives in Wave 5.Exceptions: 15 → 13
ironclaw_agent_loop → ironclaw_turnsagent_loop's manifest is now{common, host_api, loop_contracts}and the contracts-only rule resolves with no exception to consume.ironclaw_hooks → ironclaw_turnshooksdropped the dependency outright.ironclaw_conversations → ironclaw_turnsWS0_LAYER_MATRIX_EXCEPTION_BASELINElowered 15 → 13. Both deletions were confirmed by the stale-exception detector before I touched the list, not asserted.The conversations straggler does not belong to this group
PROPOSAL §8.3 and PLAN both say
conversations → turnsfalls with the port repoints here. Re-verified against the live tree, it does not and cannot:InboundTurnServiceis generic overC: TurnCoordinator + ?Sized, holdsArc<C>, and callssubmit_turn(SubmitTurnRequest { … })(inbound.rs:37,245);trusted_trigger.rsclassifiesTurnError/AdmissionRejectionReason. That is turn admission authority, not vocabulary and not a loop port — no contracts crate dissolves it. Its exception now records that evidence and readsremoves_in = "WS5", matching §6.4.2, which already lists conversations' target deps asfilesystem/host_api/safety/triggers"+ turn vocabulary viahost_api", with no coordinator. The ≤ 12 target in the WS1 verify row needs WS5, not WS1.What moved, and four things the lead sheet did not predict
Moved to
ironclaw_loop_contracts(git mv, history preserved): all ofturns::run_profile/**except two implementations; theLoopExitclaim DTOs;RedactedCheckpointPayload+ its ceiling; the three contract test suites (run_profile_contract,skill_context_service_contract,memory_prompt_context_service).HostManagedLoop*Portimplementations did not move. §6.1.4 forbids a contracts crate from implementing its own ports; §6.7.2 assigns them toloop_host. Soprompt.rsand the impl half ofmodel.rsstayed inturns::host_managed_ports/— a module whose doc comment says exactly that and says nothing new belongs in it — awaiting the WS4loop_hostre-charter. The contract half ofmodel.rs(gateway / accountant / policy-guard traits + their request/error/outcome DTOs) moved.turns::originhad to move too, tohost_api::turn.run_profile/runtime_context.rscarriesOption<ProductTurnContext>onLoopRuntimeContext, so the type had to be reachable from a contracts crate. It is turn-scoped vocabulary consumed by kernel, substrates, loops and products — §6.1.1'sturncharter verbatim — so it went tohost_api, not toloop_contracts.GateResumeDispositionfollowed for the same reason (AgentLoopDriverResumeRequestandResumeTurnRequestboth carry it).CheckpointStateStorePortis only half-movable.RedactedCheckpointPayload+MAX_CHECKPOINT_STATE_PAYLOAD_BYTESmoved. TheLoopCheckpointStoretrait did not: its methods returnTurnErrorand its record carriesTurnTimestamp, neither of which §6.1.4 assigns to this crate. The ceiling is now a literal in contracts, with an equality pin againstironclaw_processes::MAX_PROCESS_CHECKPOINT_PAYLOAD_BYTESinironclaw_turns(which depends on both) so the two cannot drift.LoopExit::validatesplit from its DTO. The claim vocabulary moved; the validation (policy, violation taxonomy, applier) stayed — "aLoopExitis a claim, not truth" is kernel authority. Three private methods becamepubbecause the kernel now calls them across a crate boundary:LoopBlockedKind::to_blocked_reason,LoopFailureKind::to_sanitized_failure,LoopCompleted::has_durable_completion_ref.to_blocked_reasonreturnedResult<BlockedReason, ()>; it is nowOption<BlockedReason>— behavior-identical, and()is not a public error type.Because
LoopBlockedKindandLoopFailureKindare#[non_exhaustive], the kernel's exhaustive-match guards could no longer be exhaustive from another crate. Rather than weaken them to_arms, the compiler-checked variant guards now live beside the enums inloop_contracts(blocked_kind_gate_correspondence_is_exhaustive,failure_kind_category_strings_are_exhaustive) and the kernel's behavioral suites keep loud_arms pointing at them. The "a new variant breaks a test" property is preserved, in the crate where it can be.One documented conflict, recorded rather than papered over:
instruction_bundle.rsembeds one prompt asset viainclude_str!, and §6.1.4 says a contracts crate holds no prompt content. It moved anyway becausehooksconsumesInstructionMaterializationStoreand leaving it behind would have kept thehooks → turnsexception alive. The resolution §6.7.2 points at — hoistInstructionBundleBuildertoloop_host, leave the types here — is a WS4 item. It is called out in the new crate'sCLAUDE.mdunder "Known debt".New-crate disciplines (all in this PR)
ironclaw_loop_contractsinreborn_dependency_boundaries.rs— new crates are unruled by default.{host_api, common, prompt_envelope}), not a blocklist, so a future kernel dep cannot slip past a list of today's offenders. External: a newreborn_contracts_crates_hold_no_framework_dependenciescovering all four contracts crates — every existing metadata helper in that file filters toironclaw_*names, so frameworks were invisible to them. One documented carve-out:tokiowithrtonly, for the singleJoinHandleinCommunicationContextFetch::Spawned, pinned in the test and the manifest.reborn_loop_port_location_scan.rs: everyLoop*Porttrait is defined once, in the crate frozen inLOOP_PORT_OWNERS(11 inloop_contracts;LoopExitEvidencePortinturnsas kernel validation authority;LoopAttachmentReadPortinloop_hostas an internal seam), plus the re-export-path half. It found a real pre-existing violation:runner/src/loop_exit_applier.rsre-exportedLoopExitEvidencePort(and the applier + evidence requests) fromironclaw_turns, giving them two import paths. Converted to a plainuse; the three internal consumers repoint. Ships with positive + negative fixtures per §11.2.11 — the predicate is anchored soHookedLoopModelPort/HostManagedLoopPromptPortare correctly out of scope.members,[package.metadata.ironclaw] layer = "contracts",classify-test-scope.sh's shared arm,reborn-crate-test-buckets.sh→agent-runtime. Verified rather than assumed:discover-reborn-package-crates.shpicks the crate up through the shipped-binary closure and needs no allowlist entry; both CI self-tests pass and the bash/python crate inventories agree at 64. Note for a follow-up, not fixed here:ironclaw_libsql_runtimeandironclaw_memory_mem0are still missing fromclassify-test-scope.sh, so a diff touching only those crates classifieshas_reborn_tests=falsetoday — the trap in its live form, and the reason this row exists.#![warn(unreachable_pub)], directory-of-moduleslib.rs, crateCLAUDE.md. Ports are deliberately not sealed — they exist to be implemented above this crate; the sealed pattern stays withagent_loop's strategy slots, and the crate guide says so.reborn_extension_specificity.rs's threePATH_TERM_COLLISIONScarve-outs repointed with the files (the scanner caught the staleness itself).Un-masking
Unfiltered per-crate rosters before and after, every test classified:
ironclaw_turnsloop_contracts; 5 inhost_api(theoriginsuite); 11 stayed in turns under the renamed module path (run_profile::{model,prompt}::tests::*→host_managed_ports::…, identical names); +1 new (the checkpoint-ceiling pin).ironclaw_host_apiorigintests. Nothing lost.ironclaw_loop_contractsironclaw_agent_loopironclaw_hooksironclaw_loop_hostironclaw_runnerZero tests lost, zero unclassified, no surviving test edited for content. The only test-body edits are import repoints and the two
_arms described above.Wire-shape proof for the hand-split DTOs
The highest-severity risk in this PR is that hand-splitting ~350 lines of serde DTOs out of
loop_exit.rssilently changed a persisted wire shape (these ride journal rows). The proof it did not:crates/ironclaw_turns/src/loop_exit/tests/mod.rs— 1,206 lines, 86 tests, including the five that pin the JSON contract of exactly the moved types (loop_exit_wire_shape_rejects_raw_payload_fields_and_recovery_required_variant,loop_failed_accepts_retired_diagnostic_ref_but_does_not_serialize_it, the snake_case serialization pair, and the policy-minting deserialization guard) — stayed in the kernel and its diff against the parent is import repoints plus the two_arms, nothing else. Not one assertion, fixture, or expected-JSON literal was touched, and all 86 pass against the relocated types. A test suite that was not allowed to move is the only honest way to prove a move was faithful.The whole crate's public surface was also diffed against the old
ironclaw_turns::run_profileexport list, so nothing could fall out of the API silently during thelib.rsrewrite:HostManagedLoop*Portimpls that stayed in the kernel, plusCapabilityActivityIdandProductTurnContext—run_profilere-exported those two from the turn vocabulary, and this crate deliberately does not, so each type keeps exactly one import path. Callers now take them fromironclaw_host_api::turn(a scripted repoint, not a hand edit).LoopExitDTO half (9 types) plusRedactedCheckpointPayloadandMAX_CHECKPOINT_STATE_PAYLOAD_BYTES.Verification
cargo fmt --check;clippy --all-targets --all-features -D warningson all 16 touched crates; unfilteredcargo test -ponloop_contracts/host_api/turns/agent_loop/hooks/loop_host/runner; fullcargo test -p ironclaw_architecture(green, including the two new tests). Also run:check_no_panics.py --reborn-baseline(OK, 1157 files — keying unchanged, no baseline regeneration owed),check-include-str-paths.sh,check-composition-budget.sh, and both CI script self-tests. CI is the arbiter for the workspace lanes.Coverage manifests
tests/integration/coverage-floor.toml's[[crate]] ironclaw_turnsentry drops itsfloor_covered_lines = 9515numerator: 13,951 of the crate's 25,356 source lines moved out, so that absolute floor is structurally unreachable rather than regressed.floor_percentis retained unchanged rather than weakened. The recapture obligation — real numbers forironclaw_turnsand a new entry forironclaw_loop_contracts, both from this PR's own merged artifact — is written into the file so it cannot be forgotten. No changed-coverage exemption is pre-written: per #6963's recipe those line numbers come from this PR's own gate output, not from a guess.Checklist rows ticked: WS1.2 (with all four dispositions recorded), WS4's
agent_loopflip; WS1's verify row and the new-crate CI row annotated with what is and is not now true.