Skip to content

refactor(contracts): complete the turn vocabulary in host_api and retire the turns shims (WS1.1) - #6967

Merged
BenKurrek merged 7 commits into
mainfrom
ws1/turn-vocabulary
Jul 31, 2026
Merged

BenKurrek merged 7 commits into
mainfrom
ws1/turn-vocabulary

Conversation

@BenKurrek

Copy link
Copy Markdown
Collaborator

⚠ Base CI status — read first

This branch is cut from 31f42b790, whose check suite has 6 pre-existing failures that are repo-wide queue blockers, not defects in this PR:

  • the stale ironclaw_events integration coverage floor inherited from refactor: delete verified-dead modules across seven crates (WS0) #6943 (denominator fell 1545 → 1486 lines when the dead parse_jsonl/replay_jsonl modules went, so the recorded floor is no longer meetable) — surfaces as Reborn integration-tier coverage report + its Tests (Reborn) roll-up;
  • two Wasmtime RustSec advisories published 2026-07-31, failing cargo-deny;
  • a myers phantom-diff defect in the changed-coverage gate.

All three are fixed by #6966 (ws0/changed-coverage-diff-algorithm), which is signed off and in CI; #6964 follows it.

Merge order: this PR merges AFTER #6966 and #6964. Its coverage and deny lanes are expected red until those land; once they do, a re-run against the fresh merge ref should go green without needing a push here.


ironclaw_host_api::turn becomes the complete canonical turn vocabulary, the three ironclaw_turns re-export shims named by CHECKLIST WS1.1 are deleted, and the six vocabulary-only consumers drop their ironclaw_turns dependency entirely. Five LAYER_MATRIX_EXCEPTIONS fall out as a consequence: 20 → 15.

No behavior change. 142 files, +1148/−1120.


🔍 Headline finding — the workspace had two different types named GateRef

Start here; it is the largest single piece of the diff and nothing in the WS1.1 row predicted it.

turns/src/ids.rs could not be deleted without resolving its last line: pub type GateRef = ironclaw_host_api::turn::TurnGateRef. That alias gave a second name to a host_api type — and that name already belonged to an unrelated type in the same workspace:

Name Actually is Used for
ironclaw_host_api::ids::GateRef opaque uuid (uuid_id!), minted by for_approval_request / for_auth_gate the GateRecord storage key
ironclaw_turns::GateRef alias for host_api::turn::TurnGateRef, a bounded gate:-prefixed string the loop-facing routing ref

host_api/src/ids.rs already carried a doc comment warning that the two are "deliberately distinct" — the codebase knew about the collision and lived with it. The alias is now retired rather than relocated, so TurnGateRef is the one and only name for the routing ref: 336 occurrences across 62 files.

A mechanical find-and-replace would have been silently wrong. Every occurrence was classified by its import source first, and 14 files were excluded as kernel-GateRef users — approvals ×2, capabilities::host, host_api ×4, composition ×3, runner::turn_run_executor, the webui contract test, loop_host::capability_port, and the trap: turns::run_profile::resolution, a file inside ironclaw_turns itself that imports the kernel GateRef from host_api::ids. Renaming it would have swapped a uuid key for a bounded string. Two further files use both types and were hand-edited.

The rename is compiler-verified end to end: because the two are genuinely different types, any misclassification is a type error rather than a silent swap. Detail in §3.


1. Vocabulary moved into host_api::turn

Symbol From Why it had to move
TurnStatus turns::status Named by the WS1.1 row.
GateKind turns::status Not in the lead sheet. TurnStatus::is_blocked is defined as GateKind::from_status(self).is_some(), and GateKind documents itself as the single GateKind -> TurnStatus correspondence that ~6 other blocked-gate representations map through. Splitting it from TurnStatus would have required duplicating that match table — the exact vocabulary pooling this workstream exists to remove.
BlockedReason turns::status Not in the lead sheet. GateKind::into_blocked_reason returns it; you cannot move GateKind without it (no inherent impls on foreign types). Depends only on TurnGateRef + host_api::decision::RuntimeCredentialAuthRequirement, both already in host_api.
EventCursor turns::events Not in the lead sheet, but required. event_projections (production), outbound (production), and event_streams (tests) all need it; without it three of the five exceptions could not fall. Self-contained 5-line newtype.
RunOriginAdapter (+ MAX_RUN_ORIGIN_ADAPTER_BYTES, now pub) turns::origin Not in the lead sheet, but required. outbound::run_delivery_cleanup carries it in production struct fields.

is_recoverability_critical deliberately stayed in turns::status: it is write-behind durability policy over TurnStatus, not vocabulary. Same for TurnRunState, TurnError, TurnRunProfile, and the admission-rejection types.

turns re-exports the moved names from its prelude (joining the SanitizedFailure/TurnOwner/… line that was already there) so the ~15 crates that legitimately keep the turns dependency are untouched. That re-export is explicitly documented as not an ownership claim; §11.2.4 (WS10) is the row that retires it.

2. Shim-deletion inventory

Shim Disposition
turns/src/ids.rs (8 lines) Deleted. Its pub use half moved to lib.rs pointing at ironclaw_host_api::turn directly. Its pub type GateRef = TurnGateRef half is retired, not relocated — see §3.
turns/src/scope.rs (1 line) Deleted. Same treatment. The ironclaw_turns::scope::… module-path consumers (product::communication_context, host_runtime doc links + its test, turns' own test, the integration harness, and runner's await-edge resolver — the only TurnThreadOwner consumer) now import from ironclaw_host_api::turn.
turns/src/product_adapter/ Deleted whole. mod.rs was pub use ironclaw_host_api::product_adapter::* plus a test_support wildcard passthrough — pure shim. fakes.rs (261 lines) is real code, so it moved (git mv) to crates/ironclaw_host_api/src/product_adapter/test_support/fakes.rs, beside the four traits it implements. Its only consumer, ironclaw_product's #[cfg(test-support)] re-export, points there now.

3. The one rename: GateRef → TurnGateRef (336 occurrences, 62 files)

See the headline finding above for the full rationale, the two colliding types, and the exclusion list. Mechanically, what changed here:

  • turns/src/ids.rs's pub type GateRef = TurnGateRef is deleted, not relocated to lib.rs — relocating it would have kept the shim at a new address and kept the collision.
  • Consumers that named ironclaw_turns::GateRef now name TurnGateRef (still reachable as ironclaw_turns::TurnGateRef via the prelude re-export, or ironclaw_host_api::turn::TurnGateRef directly for the six repointed crates).
  • The moved BlockedReason / GateKind::into_blocked_reason signatures in host_api::turn use TurnGateRef natively, so the rename was forced by the vocabulary move regardless of the alias question.
  • host_api/src/ids.rs's doc comment, which pointed readers at "ironclaw_turns::GateRef", now links crate::turn::TurnGateRef.

Nothing outside this rename changed in the 14 excluded kernel-GateRef files, and the historical design records under docs/reborn/subagent-spawn/ keep the old spelling (see §8.6).

4. Per-consumer repoint table

All six turned out to be vocabulary-only exactly as the lead sheet said, so all six dropped ironclaw_turns from Cargo.toml.

Consumer What it imported from ironclaw_turns Now imports from Disposition
ironclaw_auth TurnRunId, TurnScope ironclaw_host_api::turn Repointed; dep dropped
ironclaw_event_streams TurnActor, ReplyTargetBindingRef, TurnRunId, TurnScope, EventCursor (tests) ironclaw_host_api::turn Repointed; dep dropped
ironclaw_outbound ReplyTargetBindingRef, TurnActor, TurnRunId, TurnScope, EventCursor, RunOriginAdapter ironclaw_host_api::turn Repointed; dep dropped
ironclaw_telegram_extension ReplyTargetBindingRef ironclaw_host_api::turn Repointed; dep dropped
ironclaw_triggers TurnRunId, TurnScope ironclaw_host_api::turn Repointed; dep dropped
ironclaw_event_projections EventCursor ironclaw_host_api::turn Repointed; dep dropped

76 ironclaw_turns:: references rewritten across 38 files in those six crates.

5. Exceptions delta — 20 → 15

WS0_LAYER_MATRIX_EXCEPTION_BASELINE lowered 20 → 15 with the rationale inline. These five are removed because their edges no longer exist, not because they were waived — the stale-exception detector in reborn_workspace_crates_declare_layers_and_follow_layer_matrix would fail on them if they stayed.

Removed Why it is satisfied
ironclaw_event_projections → ironclaw_turns EventCursor is in host_api::turn; manifest dep gone
ironclaw_triggers → ironclaw_turns TurnRunId/TurnScope from host_api::turn; manifest dep gone
ironclaw_outbound → ironclaw_turns Vocabulary + EventCursor + RunOriginAdapter from host_api::turn; manifest dep gone
ironclaw_event_streams → ironclaw_turns Vocabulary + EventCursor from host_api::turn; manifest dep gone
ironclaw_auth → ironclaw_turns TurnRunId/TurnScope from host_api::turn; manifest dep gone. Its removes_in read "follow-up: neutral auth/turn gate host API port" — this is that follow-up

Deliberately still standing (3 of the 8 *→turns entries): conversations, hooks, agent_loop. Each reaches turns for more than vocabulary (SubmitTurnRequest/TurnCoordinator/TurnError, hook payload contracts, loop ports). They fall with the port repoints in later WS1 slots — agent_loop's with loop_contracts.

6. Un-masking evidence

Full unfiltered cargo test -p <crate> for every touched crate, before on 31f42b790 in a pristine worktree, after on this branch.

Crate Before After Δ
ironclaw_host_api 347 366 +19
ironclaw_turns 380 363 −17
ironclaw_auth 251 251 0
ironclaw_event_streams 71 71 0
ironclaw_outbound 139 139 0
ironclaw_telegram_extension 38 38 0
ironclaw_triggers 165 165 0
ironclaw_event_projections 50 50 0
ironclaw_product 1032 1032 0
ironclaw_conversations 83 83 0
ironclaw_extension_host 382 382 0
ironclaw_host_runtime 1240 1240 0
ironclaw_loop_host 572 572 0
ironclaw_runner 467 467 0
ironclaw_reborn_composition 914 (2 ignored) 914 (2 ignored) 0
ironclaw_stress 81 81 0
Total 6212 6214 +2

Zero failures on both sides. Every one of the 17 tests turns lost is accounted for — all 17 moved verbatim into host_api::turn, none deleted, none weakened:

  • 14 from turns/src/status.rs's test module. Notably 11 of those 14 were already testing host_api types (ModelInvalidOutputDetailReason ×4, SanitizedFailure ×7) from inside turns — host_api::turn had no test module at all before this PR. The other 3 cover TurnStatus/BlockedReason, which moved.
  • 3 from turns/src/origin.rs (run_origin_adapter_rejects_empty, ..._accepts_at_max_bytes, ..._rejects_over_512_bytes), which moved with RunOriginAdapter. The three ProductTurnContext serde tests in that file stayed in turns (they test ProductTurnContext, which stayed) and now import the newly-pub MAX_RUN_ORIGIN_ADAPTER_BYTES.

The +2 net is two genuinely new host_api tests, both pinning the one API change:

  • run_origin_adapter_rejection_message_is_the_one_callers_render — pins the rejection string byte-for-byte;
  • run_origin_adapter_round_trips_as_a_plain_string_and_validates_on_read — pins the #[serde(try_from)] boundary, which now yields a String error.

7. The one API change, and why it is behavior-preserving

RunOriginAdapter::new / TryFrom<String> returned Result<_, TurnError> (specifically TurnError::InvalidRunOriginAdapter). TurnError is a turn-kernel type and could not follow the value into host_api. It now returns Result<_, String>, matching every other bounded ref in host_api::turn (AcceptedMessageRef, TurnGateRef, IdempotencyKey, …).

Both production callers — product/src/inbound_turn.rs and conversations/src/inbound.rs — only ever did e.to_string(), and the message is unchanged ("invalid run-origin adapter: must be 1..=512 bytes", now exposed as RunOriginAdapter::INVALID_MESSAGE and pinned by a test). So the rendered errors are byte-identical.

8. Lead-sheet corrections and findings for later slots

  1. The row named only TurnStatus. EventCursor and RunOriginAdapter were also required — without them event_projections, outbound, and event_streams could not drop the dep, and three of the five exceptions could not fall. GateKind/BlockedReason were required to move TurnStatus coherently.
  2. turns::product_adapter is not purely a re-export shim. It also held 261 lines of live test-support fakes. They were relocated to the trait owner rather than deleted.
  3. ids.rs is not just a re-export — its GateRef alias collided with an unrelated host_api::ids::GateRef. This is the largest single piece of the diff and was not visible in the row text.
  4. turns::run_profile::resolution imports the kernel GateRef, not turns' alias — a same-crate false positive a mechanical rename would have broken.
  5. Follow-up (not fixed here, out of scope): TurnError::InvalidRunOriginAdapter is now constructed only by tests. Its four production match arms (conversations::inbound, conversations::trusted_trigger, composition::trigger_poller_trusted_submit, runner::loop_driver_host) are exhaustive-match completeness, and three conversations tests construct it through a fake coordinator to pin submit-key-rotation behavior. Retiring the variant means editing those tests, so it belongs with the conversations port repoint — same class as the WS8 ProjectionError::TurnEventRebaseRequired row.
  6. Not touched, deliberately: docs/reborn/contracts/host-api.md §3 "Proposed module layout" lists 15 of ~48 modules and did not gain turn.rs; the full list is an explicit WS11 row. The historical design records under docs/reborn/subagent-spawn/ still show the old GateRef spelling — they are dated design documents, not live guidance.

9. Guidance updated in the same diff

  • crates/ironclaw_host_api/CLAUDE.md + AGENTS.md — turn is now listed as an owned module and described as the complete vocabulary; adds the "turn::TurnGateRef and ids::GateRef are different types, never re-alias one to the other" rule.
  • crates/ironclaw_turns/CLAUDE.md + AGENTS.md — the two claims this PR falsified are corrected: the crate no longer owns "canonical typed IDs and references … turn scope/actor" or "Status/error vocabulary: TurnStatus…". Both now say where the vocabulary actually lives and that the prelude re-export is not an ownership claim.
  • crates/Architecture.md — the cross-boundary record table's Owner column for TurnScope, TurnActor, AcceptedMessageRef, the binding refs, TurnId, TurnRunId, and EventCursor (several of which were already wrong, since the ids lived in host_api behind the shim), plus the ironclaw_turns crate row.
  • crates/ironclaw_architecture/tests/reborn_restructure_baselines.rs — the WS0 baseline table records "WS0 20, now 15".

10. Verification

  • cargo fmt --check — clean
  • cargo clippy -p <each touched crate> --all-targets --all-features -- -D warnings — clean; also cargo clippy --all --benches --tests --examples --all-features clean
  • cargo test -p <each of the 16 touched crates>, unfiltered — table in §6, zero failures
  • cargo test -p ironclaw_architecture — all suites green, including the exception ratchet at the new baseline of 15 and the stale-exception detector with the five entries removed
  • python3 scripts/check_no_panics.py --reborn-baseline — OK (1154 files, 51 reviewed invariants); the fakes.rs move needed no baseline change (the module stays #[cfg(any(test, feature = "test-support"))]-gated)
  • cargo build --workspace --all-features --tests --benches --examples — clean

Per the standing local-verification policy, the full cargo test --workspace was not run locally; CI runs the full matrix.

11. Trailing docs commit

Separate docs: commit ticks the WS1.1 row with the corrections above, and closes PLAN decision round #1 per the owner: Strategy B confirmed (retroactive record), and the tools/ row's default-members trim resolved as no trim. It also surfaces #6963 on the WS0 blocking-prerequisite row's first line (it was already cited mid-paragraph) and records the §11.2.2 ratchet moving 20 → 15.

🤖 Generated with Claude Code

BenKurrek and others added 2 commits July 31, 2026 11:59
…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>
@railway-app

railway-app Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the ironclaw-pr-6967 environment in ironclaw-ci-preview

Service Status Web Updated (UTC)
ironclaw 🕒 Building (View Logs) Web Jul 31, 2026 at 8:07 pm

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6967 July 31, 2026 17:19 Destroyed
@github-actions github-actions Bot added scope: docs Documentation scope: dependencies Dependency updates size: XL 500+ changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Jul 31, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0fb87e6a-a26b-4655-bfa4-a2f1f1c1693a

📥 Commits

Reviewing files that changed from the base of the PR and between bad94d6 and 1cafd61.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/Cargo.lock
📒 Files selected for processing (3)
  • crates/ironclaw_auth/Cargo.toml
  • crates/ironclaw_triggers/Cargo.toml
  • crates/ironclaw_triggers/src/lib.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added shared support for event cursors, run origins, lifecycle statuses, gate types, and external-tool blocking.
    • Added validation and serialization for run-origin identifiers and blocked-turn details.
    • Added public test fakes for product adapter integrations.
  • Refactor

    • Standardized turn and gate references across authentication, approvals, delivery, events, triggers, and integrations.
    • Reduced dependency-boundary exceptions.
  • Documentation

    • Updated architecture guidance and implementation checklists to reflect revised turn vocabulary and ownership.

Walkthrough

The PR centralizes canonical turn vocabulary in ironclaw_host_api::turn, removes obsolete ownership and dependencies from ironclaw_turns, migrates GateRef to TurnGateRef, and updates affected consumers, runtime code, tests, and architecture records.

Changes

Canonical turn vocabulary

Layer / File(s) Summary
Host API vocabulary and validation
crates/ironclaw_host_api/src/turn.rs, crates/ironclaw_host_api/AGENTS.md, crates/ironclaw_host_api/CLAUDE.md
Adds cursors, run-origin validation, lifecycle statuses, gate mappings, blocked reasons, and validation tests.
Turn crate ownership boundary
crates/ironclaw_turns/src/*, crates/ironclaw_turns/AGENTS.md, crates/ironclaw_turns/CLAUDE.md
Removes local vocabulary declarations and compatibility modules. Re-exports canonical types from ironclaw_host_api::turn.
Consumer migration
crates/ironclaw_auth/*, crates/ironclaw_event_*/*, crates/ironclaw_outbound/*, crates/ironclaw_triggers/*
Updates imports, public field types, cursor APIs, and dependency manifests.
Gate reference migration
crates/ironclaw_product/*, crates/ironclaw_reborn_composition/*, crates/ironclaw_runner/*, tests/*, tools/*
Replaces GateRef with TurnGateRef across approval, authentication, workflow, subagent, persisted-gate, integration, and stress-test contracts.
Architecture and validation records
crates/ironclaw_architecture/*, docs/reborn/target-architecture/CHECKLIST.md, tests/integration/changed-coverage-exemptions.toml
Updates ownership documentation, dependency exception baselines, migration status, and coverage exemptions.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: henrypark133

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description gives detailed change and validation evidence but omits several required template sections, including change type, linked issue, security, database, rollback, and review follow-through. Add all missing template sections and explicitly complete each required field, using “None” or “N/A” with a reason where applicable.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits style and clearly describes the turn vocabulary refactor.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ironloopai

ironloopai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Review · PR #6967

🟢 Completed · Review submitted

Submitted review →

No actionable findings. The complete trusted base-to-head comparison consistently relocates turn vocabulary into ironclaw_host_api, preserves serialized representations and validation limits, distinguishes routing TurnGateRef from the unrelated kernel GateRef, relocates test-support fakes without losing behavior, removes only obsolete dependency exceptions, and updates consumers and manifests coherently.

Automatic · PR opened + CI failed · attempt 1 of 3 · completed in 1m 59s

Run details
  • Repository: nearai/ironclaw
  • Base: main at 31f42b7
  • Head: ws1/turn-vocabulary at a8e8bfa
  • Created: Jul 31, 2026, 5:24 PM UTC
  • Updated: Jul 31, 2026, 5:26 PM UTC
  • Run: ea1a3661-46bd-469c-b67f-79c134a99c15
  • Latest attempt: 1 · Completed · b710feff-4c80-49bf-925e-547166fe4bba

@ironloopai ironloopai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔍 Review complete · PR #6967

✅ No actionable findings

No actionable findings. The complete trusted base-to-head comparison consistently relocates turn vocabulary into ironclaw_host_api, preserves serialized representations and validation limits, distinguishes routing TurnGateRef from the unrelated kernel GateRef, relocates test-support fakes without losing behavior, removes only obsolete dependency exceptions, and updates consumers and manifests coherently.

Validation and technical details
  • Reviewed refs/ironloop/base (31f42b7) through refs/ironloop/head (a8e8bfa): 142 files, +1148/-1120.
  • Inspected all changed-area categories: canonical vocabulary moves, GateRef rename exceptions, RunOriginAdapter error/serde behavior, shim deletions, fake relocation, consumer dependency removals, lockfile, architecture ratchet, tests, and documentation.
  • Confirmed no stale live references to the removed ironclaw_turns::{ids,scope,product_adapter} module paths or ironclaw_turns::GateRef; remaining kernel GateRef uses resolve through ironclaw_host_api::ids.
  • Verified the six vocabulary-only consumers no longer reference ironclaw_turns, matching their manifest and Cargo.lock dependency removals.
  • git diff --check refs/ironloop/base..refs/ironloop/head passed.
  • The repository knowledge graph was unavailable, so live-code searches and crate guidance were used as the prescribed fallback.
  • Targeted Cargo tests could not be independently run because cargo is not installed in the review environment (cargo: command not found); static inspection found no defect in the changed test coverage or implementation.
  • Base: main
  • Head: ws1/turn-vocabulary at a8e8bfa
  • Run: ea1a3661-46bd-469c-b67f-79c134a99c15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ironclaw_extension_host/src/channel_host/e2e_tests.rs`:
- Around line 86-89: Update the imports in the e2e test around the turn
vocabulary list: import TurnGateRef, EventCursor, TurnStatus, TurnRunId, and
TurnScope from ironclaw_host_api::turn, while retaining only coordination-kernel
types under ironclaw_turns. Remove the moved shared types from the
ironclaw_turns import so the new API ownership is explicit.

In `@crates/ironclaw_extension_host/src/run_delivery_ports.rs`:
- Line 18: Update the imports and usages of TurnGateRef and TurnScope in
run_delivery_ports.rs to use the canonical ironclaw_host_api::turn module
instead of ironclaw_turns. After confirming no other coordination types from
ironclaw_turns remain in the crate, remove its dependency declaration.

In `@crates/ironclaw_product/src/approval_interaction/gate_ref.rs`:
- Line 2: Complete the canonical turn-vocabulary import migration by replacing
compatibility imports from ironclaw_turns with contract-owner imports from
ironclaw_host_api::turn. Update TurnGateRef in
crates/ironclaw_product/src/approval_interaction/gate_ref.rs:2-2,
read_model.rs:6-6, service.rs:14-15, types.rs:10-10, approval_prompt.rs:14-14,
auth_continuation.rs:14-16, auth_interaction/service.rs:9-10,
auth_interaction/types.rs:6-8, and gate_state.rs:2-2; import EventCursor from
ironclaw_host_api::turn in inbound_turn/tests.rs:33-36, preserving all other
imports and usage.

In `@crates/ironclaw_product/src/reborn_services.rs`:
- Around line 54-57: Replace every use of the compatibility-path TurnGateRef
with the canonical ironclaw_host_api::turn::TurnGateRef. Update imports and
public type references at crates/ironclaw_product/src/reborn_services.rs:54-57
and :6842, crates/ironclaw_product/src/reborn_services/types.rs:10-11,
crates/ironclaw_product/src/run_delivery.rs:41-42,
crates/ironclaw_product/tests/prompt_projection_contract.rs:15,
crates/ironclaw_product/tests/reborn_services_contract.rs:177-182,
crates/ironclaw_product/tests/run_delivery_contract.rs:50-55,
crates/ironclaw_reborn_composition/src/blocked_auth_resume.rs:252-255,
crates/ironclaw_reborn_composition/src/factory/auth_tests.rs:27-30, :210, and
:993, tests/integration/group_approvals/scenario_gate_ref_edge_cases.rs:17-19,
tests/integration/support/harness/mod.rs:62, and
tests/integration/support/builder.rs:52-55; preserve the existing APIs and
behavior while removing all ironclaw_turns::TurnGateRef references.

In `@crates/ironclaw_product/src/workflow.rs`:
- Around line 30-31: Move all TurnGateRef imports and constructor references in
workflow.rs, product_surface_inbound.rs, projection/tests.rs,
projection/turn_events.rs, run_delivery/prompts.rs,
approval_interaction_contract.rs, auth_interaction_contract.rs, and
product_surface_contract.rs (including both cited constructor sites) to
ironclaw_host_api::turn. Split mixed ironclaw_turns imports so TurnRunId and
other vocabulary remain sourced there while TurnGateRef uses its canonical path.

In `@crates/ironclaw_product/tests/product_surface_contract.rs`:
- Around line 2817-2820: Update the comment describing the InvalidGateRef branch
in resolve_via_delivered_auth_route by removing the approximate numeric
source-line reference and referring only to the TurnGateRef::new symbol instead.

In `@crates/ironclaw_product/tests/reborn_services_contract.rs`:
- Line 5376: Respect the validated TurnGateRef contract across all listed sites:
in crates/ironclaw_product/tests/reborn_services_contract.rs at lines 5376,
5408, 5440, 5472, 5507, 5542, 5580, 5944, 6058, and 6188, replace each bare gate
fixture and matching request payload with valid gate:-prefixed values; in
crates/ironclaw_product/tests/prompt_projection_contract.rs at line 128, replace
approval:missing with a valid gate reference; in
crates/ironclaw_product/tests/run_delivery_contract.rs at lines 65-68, update
the helper to accept Option<TurnGateRef> or return a fallible construction
result instead of using raw &str; and in
crates/ironclaw_reborn_composition/src/blocked_auth_resume.rs at line 416,
construct a valid reference such as gate:auth-{run_id}.

In `@crates/ironclaw_turns/src/lib.rs`:
- Around line 65-70: Remove the ironclaw_host_api::turn compatibility pub use
from the crate root in lib.rs, then update all consumers of the re-exported turn
types to import them directly from ironclaw_host_api::turn. Preserve the
canonical turn vocabulary ownership there and eliminate public ironclaw_turns
paths for these types.

In `@crates/ironclaw_turns/src/status.rs`:
- Around line 9-12: Remove remaining crate-root turn-vocabulary imports and use
the canonical host API ownership: in crates/ironclaw_turns/src/status.rs lines
9-12, import migrated types from ironclaw_host_api::turn; in
crates/ironclaw_turns/src/run_profile/runtime_context.rs lines 575-576,
reference TurnActor and TurnScope through ironclaw_host_api::turn; in
crates/ironclaw_turns/tests/agent_loop_host_contract.rs lines 17-22, retain
coordinator imports from ironclaw_turns while importing host-owned vocabulary
from ironclaw_host_api::turn. Do not define or re-alias these types.

In `@tools/ironclaw_stress/src/user_turn.rs`:
- Line 1677: Update the TurnGateRef construction near TurnGateRef::new to use an
accepted prefix: format the value as gate:approval-{run_id} for an approval
gate, or gate:auth-{run_id} when use_auth_gate selects the auth flow, so
block_run receives a valid gate reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3042f507-0ce7-475b-9342-73f81ba26609

📥 Commits

Reviewing files that changed from the base of the PR and between 31f42b7 and a8e8bfa.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/Cargo.lock
📒 Files selected for processing (141)
  • crates/Architecture.md
  • crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs
  • crates/ironclaw_architecture/tests/reborn_restructure_baselines.rs
  • crates/ironclaw_auth/Cargo.toml
  • crates/ironclaw_auth/src/product_auth/api/auth.rs
  • crates/ironclaw_auth/src/product_auth/api/auth/tests.rs
  • crates/ironclaw_auth/src/product_auth/oauth/oauth_gate.rs
  • crates/ironclaw_conversations/tests/inbound_contract.rs
  • crates/ironclaw_event_projections/Cargo.toml
  • crates/ironclaw_event_projections/src/lib.rs
  • crates/ironclaw_event_streams/Cargo.toml
  • crates/ironclaw_event_streams/src/admission.rs
  • crates/ironclaw_event_streams/src/manager.rs
  • crates/ironclaw_event_streams/src/types.rs
  • crates/ironclaw_event_streams/src/update_source.rs
  • crates/ironclaw_event_streams/tests/event_stream_manager_contract/core.rs
  • crates/ironclaw_event_streams/tests/event_stream_manager_contract/subscription.rs
  • crates/ironclaw_event_streams/tests/event_stream_manager_contract/support/fakes.rs
  • crates/ironclaw_event_streams/tests/event_stream_manager_contract/support/imports.rs
  • crates/ironclaw_extension_host/src/channel_host/e2e_tests.rs
  • crates/ironclaw_extension_host/src/run_delivery_ports.rs
  • crates/ironclaw_host_api/AGENTS.md
  • crates/ironclaw_host_api/CLAUDE.md
  • crates/ironclaw_host_api/src/ids.rs
  • crates/ironclaw_host_api/src/product_adapter/test_support.rs
  • crates/ironclaw_host_api/src/product_adapter/test_support/fakes.rs
  • crates/ironclaw_host_api/src/turn.rs
  • crates/ironclaw_host_runtime/src/memory_context.rs
  • crates/ironclaw_host_runtime/tests/memory_prompt_context.rs
  • crates/ironclaw_loop_host/src/subagent_spawn_port.rs
  • crates/ironclaw_outbound/Cargo.toml
  • crates/ironclaw_outbound/src/communication_preferences.rs
  • crates/ironclaw_outbound/src/delivered_gate_routes.rs
  • crates/ironclaw_outbound/src/delivery_resolution.rs
  • crates/ironclaw_outbound/src/delivery_targets.rs
  • crates/ironclaw_outbound/src/ids.rs
  • crates/ironclaw_outbound/src/outbound_state_store.rs
  • crates/ironclaw_outbound/src/resolution_engine.rs
  • crates/ironclaw_outbound/src/run_delivery_cleanup.rs
  • crates/ironclaw_outbound/src/run_final_reply_handoff.rs
  • crates/ironclaw_outbound/src/run_final_reply_target.rs
  • crates/ironclaw_outbound/src/service.rs
  • crates/ironclaw_outbound/src/store.rs
  • crates/ironclaw_outbound/src/triggered_run_delivery.rs
  • crates/ironclaw_outbound/src/types.rs
  • crates/ironclaw_outbound/src/validation.rs
  • crates/ironclaw_outbound/tests/outbound_policy_service_contract.rs
  • crates/ironclaw_outbound/tests/outbound_state_store_contract.rs
  • crates/ironclaw_product/src/approval_interaction/gate_ref.rs
  • crates/ironclaw_product/src/approval_interaction/read_model.rs
  • crates/ironclaw_product/src/approval_interaction/service.rs
  • crates/ironclaw_product/src/approval_interaction/types.rs
  • crates/ironclaw_product/src/approval_prompt.rs
  • crates/ironclaw_product/src/auth_continuation.rs
  • crates/ironclaw_product/src/auth_interaction/service.rs
  • crates/ironclaw_product/src/auth_interaction/types.rs
  • crates/ironclaw_product/src/communication_context.rs
  • crates/ironclaw_product/src/gate_state.rs
  • crates/ironclaw_product/src/inbound_turn/tests.rs
  • crates/ironclaw_product/src/lib.rs
  • crates/ironclaw_product/src/product_surface_inbound.rs
  • crates/ironclaw_product/src/projection/tests.rs
  • crates/ironclaw_product/src/projection/tests/turn_stream.rs
  • crates/ironclaw_product/src/projection/tests/turn_stream_auth.rs
  • crates/ironclaw_product/src/projection/turn_events.rs
  • crates/ironclaw_product/src/reborn_services.rs
  • crates/ironclaw_product/src/reborn_services/types.rs
  • crates/ironclaw_product/src/run_delivery.rs
  • crates/ironclaw_product/src/run_delivery/prompts.rs
  • crates/ironclaw_product/src/workflow.rs
  • crates/ironclaw_product/tests/approval_interaction_contract.rs
  • crates/ironclaw_product/tests/auth_interaction_contract.rs
  • crates/ironclaw_product/tests/product_surface_contract.rs
  • crates/ironclaw_product/tests/prompt_projection_contract.rs
  • crates/ironclaw_product/tests/reborn_services_contract.rs
  • crates/ironclaw_product/tests/run_delivery_contract.rs
  • crates/ironclaw_reborn_composition/src/blocked_auth_resume.rs
  • crates/ironclaw_reborn_composition/src/factory/auth_tests.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/openai_compat_serve.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/openai_compat_serve/tests.rs
  • crates/ironclaw_reborn_composition/src/process_gate_turn_view.rs
  • crates/ironclaw_reborn_composition/src/runtime.rs
  • crates/ironclaw_reborn_composition/src/runtime/approval.rs
  • crates/ironclaw_reborn_composition/src/runtime/approval_interaction_assembly.rs
  • crates/ironclaw_reborn_composition/src/runtime/auth_interaction.rs
  • crates/ironclaw_reborn_composition/src/runtime/capability_host/tests.rs
  • crates/ironclaw_reborn_composition/src/runtime/tests/core.rs
  • crates/ironclaw_reborn_composition/tests/auth_lifecycle.rs
  • crates/ironclaw_reborn_composition/tests/budget_approval_e2e.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/mod.rs
  • crates/ironclaw_runner/src/subagent/await_edge/resolver.rs
  • crates/ironclaw_runner/src/subagent/await_edge/store.rs
  • crates/ironclaw_runner/src/subagent/prompt_material.rs
  • crates/ironclaw_telegram_extension/Cargo.toml
  • crates/ironclaw_telegram_extension/src/preference_targets.rs
  • crates/ironclaw_triggers/Cargo.toml
  • crates/ironclaw_triggers/src/lib.rs
  • crates/ironclaw_triggers/src/libsql.rs
  • crates/ironclaw_triggers/src/postgres.rs
  • crates/ironclaw_triggers/src/worker/active_cleanup.rs
  • crates/ironclaw_triggers/src/worker/ports.rs
  • crates/ironclaw_triggers/src/worker/report.rs
  • crates/ironclaw_triggers/src/worker/tests.rs
  • crates/ironclaw_triggers/tests/repository_contract.rs
  • crates/ironclaw_turns/AGENTS.md
  • crates/ironclaw_turns/CLAUDE.md
  • crates/ironclaw_turns/src/agent_turn_runtime.rs
  • crates/ironclaw_turns/src/coordinator.rs
  • crates/ironclaw_turns/src/events.rs
  • crates/ironclaw_turns/src/ids.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/event_projection.rs
  • crates/ironclaw_turns/src/process_projection/runtime.rs
  • crates/ironclaw_turns/src/process_projection/tests.rs
  • crates/ironclaw_turns/src/product_adapter/mod.rs
  • crates/ironclaw_turns/src/request.rs
  • crates/ironclaw_turns/src/response.rs
  • crates/ironclaw_turns/src/run_profile/host/checkpoint.rs
  • crates/ironclaw_turns/src/run_profile/memory_context.rs
  • crates/ironclaw_turns/src/run_profile/runtime_context.rs
  • crates/ironclaw_turns/src/scope.rs
  • crates/ironclaw_turns/src/status.rs
  • crates/ironclaw_turns/tests/agent_loop_host_contract.rs
  • crates/ironclaw_turns/tests/memory_prompt_context_service.rs
  • docs/reborn/target-architecture/CHECKLIST.md
  • tests/integration/auth/auth_gate.rs
  • tests/integration/generated_restart_sequences.rs
  • tests/integration/group_approvals/scenario_gate_ref_edge_cases.rs
  • tests/integration/subagent_await_edge.rs
  • tests/integration/support/builder.rs
  • 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
💤 Files with no reviewable changes (9)
  • crates/ironclaw_event_projections/Cargo.toml
  • crates/ironclaw_auth/Cargo.toml
  • crates/ironclaw_outbound/Cargo.toml
  • crates/ironclaw_event_streams/Cargo.toml
  • crates/ironclaw_turns/src/ids.rs
  • crates/ironclaw_triggers/Cargo.toml
  • crates/ironclaw_turns/src/product_adapter/mod.rs
  • crates/ironclaw_telegram_extension/Cargo.toml
  • crates/ironclaw_turns/src/scope.rs

Comment thread crates/ironclaw_extension_host/src/channel_host/e2e_tests.rs Outdated
Comment thread crates/ironclaw_extension_host/src/run_delivery_ports.rs Outdated
Comment thread crates/ironclaw_product/src/approval_interaction/gate_ref.rs Outdated
Comment thread crates/ironclaw_product/src/reborn_services.rs Outdated
Comment thread crates/ironclaw_product/src/workflow.rs Outdated
Comment thread crates/ironclaw_product/tests/product_surface_contract.rs Outdated
Comment thread crates/ironclaw_product/tests/reborn_services_contract.rs
Comment thread crates/ironclaw_turns/src/lib.rs
Comment thread crates/ironclaw_turns/src/status.rs Outdated
Comment thread tools/ironclaw_stress/src/user_turn.rs
@github-actions

github-actions Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Coverage ratchet

Ratchet mode: ENFORCING

RATCHET PASS: global
  observed: 85.78% (318535 / 371337 lines)
  floor:    85.11% (tolerance 0.5pp -> effective floor 84.61%)
  denominator: 371337 lines now vs 375097 at floor capture (-3760 lines, -1%) — not a material change

RATCHET PASS: ironclaw_runner
  observed: 86% (15136 / 17599 lines)
  floor:    85.55% (tolerance 0.5pp -> effective floor 85.05%)
  floor_covered_lines: 14658 (tolerance 20 lines -> effective floor 14638)
  denominator: 17599 lines now vs 17133 at floor capture (+466 lines, +2.72%) — not a material change

RATCHET PASS: ironclaw_processes
  observed: 88.79% (5891 / 6635 lines)
  floor:    88.07% (tolerance 0.5pp -> effective floor 87.57%)
  floor_covered_lines: 5839 (tolerance 20 lines -> effective floor 5819)
  denominator: 6635 lines now vs 6630 at floor capture (+5 lines, +0.08%) — not a material change

RATCHET PASS: ironclaw_turns
  observed: 87.35% (9653 / 11051 lines)
  floor:    85.11% (tolerance 0.5pp -> effective floor 84.61%)
  floor_covered_lines: 9515 (tolerance 20 lines -> effective floor 9495)
  denominator: 11051 lines now vs 11179 at floor capture (-128 lines, -1.15%) — not a material change

RATCHET PASS: ironclaw_authorization
  observed: 86.59% (723 / 835 lines)
  floor:    62.51% (tolerance 0.5pp -> effective floor 62.01%)
  floor_covered_lines: 612 (tolerance 20 lines -> effective floor 592)
  denominator: 835 lines now vs 979 at floor capture (-144 lines, -14.71%) — material change (>5%)

RATCHET PASS: ironclaw_approvals
  observed: 91.05% (1820 / 1999 lines)
  floor:    85.86% (tolerance 0.5pp -> effective floor 85.36%)
  floor_covered_lines: 1822 (tolerance 20 lines -> effective floor 1802)
  denominator: 1999 lines now vs 2122 at floor capture (-123 lines, -5.8%) — material change (>5%)

RATCHET PASS: ironclaw_secrets
  observed: 85.81% (2896 / 3375 lines)
  floor:    84.01% (tolerance 0.5pp -> effective floor 83.51%)
  floor_covered_lines: 2795 (tolerance 20 lines -> effective floor 2775)
  denominator: 3375 lines now vs 3327 at floor capture (+48 lines, +1.44%) — not a material change

RATCHET PASS: ironclaw_filesystem
  observed: 76.23% (5849 / 7673 lines)
  floor:    75.93% (tolerance 0.5pp -> effective floor 75.43%)
  floor_covered_lines: 5826 (tolerance 20 lines -> effective floor 5806)
  denominator: 7673 lines now vs 7673 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_llm
  observed: 79.22% (20885 / 26364 lines)
  floor:    79.22% (tolerance 0.5pp -> effective floor 78.72%)
  floor_covered_lines: 20885 (tolerance 20 lines -> effective floor 20865)
  denominator: 26364 lines now vs 26364 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_triggers
  observed: 94.88% (3092 / 3259 lines)
  floor:    86.04% (tolerance 0.5pp -> effective floor 85.54%)
  floor_covered_lines: 2804 (tolerance 20 lines -> effective floor 2784)
  denominator: 3259 lines now vs 3259 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_product
  observed: 87.49% (22829 / 26094 lines)
  floor:    86.94% (tolerance 0.5pp -> effective floor 86.44%)
  floor_covered_lines: 21367 (tolerance 20 lines -> effective floor 21347)
  denominator: 26094 lines now vs 24576 at floor capture (+1518 lines, +6.18%) — material change (>5%)

RATCHET PASS: ironclaw_outbound
  observed: 94.68% (4271 / 4511 lines)
  floor:    93.49% (tolerance 0.5pp -> effective floor 92.99%)
  floor_covered_lines: 4105 (tolerance 20 lines -> effective floor 4085)
  denominator: 4511 lines now vs 4391 at floor capture (+120 lines, +2.73%) — not a material change

RATCHET PASS: ironclaw_extension_host
  observed: 83.82% (22271 / 26569 lines)
  floor:    83.82% (tolerance 0.5pp -> effective floor 83.32%)
  floor_covered_lines: 22271 (tolerance 20 lines -> effective floor 22251)
  denominator: 26569 lines now vs 26569 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_events
  observed: 80.55% (1197 / 1486 lines)
  floor:    80.55% (tolerance 0.5pp -> effective floor 80.05%)
  floor_covered_lines: 1197 (tolerance 20 lines -> effective floor 1177)
  denominator: 1486 lines now vs 1486 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_safety
  observed: 92.75% (4468 / 4817 lines)
  floor:    92.44% (tolerance 0.5pp -> effective floor 91.94%)
  floor_covered_lines: 3973 (tolerance 20 lines -> effective floor 3953)
  denominator: 4817 lines now vs 4298 at floor capture (+519 lines, +12.08%) — material change (>5%)

RATCHET PASS: ironclaw_host_runtime
  observed: 88.38% (21083 / 23855 lines)
  floor:    88.23% (tolerance 0.5pp -> effective floor 87.73%)
  floor_covered_lines: 20538 (tolerance 20 lines -> effective floor 20518)
  denominator: 23855 lines now vs 23277 at floor capture (+578 lines, +2.48%) — not a material change

Reborn integration-tier coverage

Line coverage (Reborn crates): 85.78% — 318535 / 371337 lines

Per-crate breakdown (60 crates, lowest-covered first)
Crate Line % Covered / Total
ironclaw_host_ingress 42.5% 17 / 40
ironclaw_memory 53.48% 630 / 1178
ironclaw_projects 72.36% 233 / 322
ironclaw_trust 73.71% 670 / 909
ironclaw_capabilities 74.7% 2884 / 3861
ironclaw_extractors 75.88% 538 / 709
ironclaw_reborn_cli 76.1% 11084 / 14566
ironclaw_observability 76.19% 32 / 42
ironclaw_filesystem 76.23% 5849 / 7673
ironclaw_wasm 78.84% 704 / 893
ironclaw_llm 79.22% 20885 / 26364
ironclaw_events 80.55% 1197 / 1486
ironclaw_auth 82.03% 5960 / 7266
ironclaw_first_party_extensions 82.57% 6784 / 8216
ironclaw_memory_native 82.85% 2850 / 3440
ironclaw_host_api 82.94% 9974 / 12026
ironclaw_libsql_runtime 83.3% 384 / 461
ironclaw_extension_host 83.82% 22271 / 26569
ironclaw_operator 84.47% 5309 / 6285
ironclaw_hooks 84.58% 9906 / 11712
ironclaw_event_projections 84.81% 854 / 1007
ironclaw_network 84.92% 890 / 1048
ironclaw_reborn_event_store 84.93% 1206 / 1420
ironclaw_reborn_config 85.29% 2110 / 2474
ironclaw_reborn_composition 85.51% 21781 / 25473
ironclaw_secrets 85.81% 2896 / 3375
ironclaw_runner 86% 15136 / 17599
ironclaw_authorization 86.59% 723 / 835
ironclaw_webui 86.93% 11821 / 13598
ironclaw_wasm_limiter 87.06% 74 / 85
ironclaw_common 87.33% 1641 / 1879
ironclaw_turns 87.35% 9653 / 11051
ironclaw_product 87.49% 22829 / 26094
ironclaw_reborn_traces 87.61% 11720 / 13377
ironclaw_extensions 87.84% 4870 / 5544
ironclaw_scripts 87.87% 420 / 478
ironclaw_skills 88.13% 2770 / 3143
ironclaw_threads 88.14% 5189 / 5887
ironclaw_host_runtime 88.38% 21083 / 23855
ironclaw_telegram_extension 88.52% 586 / 662
ironclaw_process_sandbox 88.64% 281 / 317
ironclaw_processes 88.79% 5891 / 6635
ironclaw_reborn_openai_compat 89.4% 3644 / 4076
ironclaw_telegram_v2_adapter 89.43% 1573 / 1759
ironclaw_loop_host 90.47% 18045 / 19946
ironclaw_resources 90.76% 4084 / 4500
ironclaw_approvals 91.05% 1820 / 1999
ironclaw_reborn_identity 91.3% 451 / 494
ironclaw_mcp 91.91% 1318 / 1434
ironclaw_conversations 92.08% 2383 / 2588
ironclaw_event_streams 92.5% 1048 / 1133
ironclaw_safety 92.75% 4468 / 4817
ironclaw_agent_loop 93.52% 10428 / 11151
ironclaw_slack_extension 93.94% 3689 / 3927
ironclaw_first_party_extension_ports 94.66% 3758 / 3970
ironclaw_outbound 94.68% 4271 / 4511
ironclaw_triggers 94.88% 3092 / 3259
ironclaw_prompt_envelope 97.46% 192 / 197
ironclaw_runtime_policy 97.6% 855 / 876
ironclaw_attachments 98.23% 831 / 846

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 (17 entry/entries excluded from the accounting above)
Module / Crate Reason Issue
crate: ironclaw_gateway v1-only: consumed only by root ironclaw (src/channels/web/platform/static_files.rs, src/channels/web/handlers/frontend.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_tui v1-only: consumed only by root ironclaw (src/main.rs, src/channels/tui.rs); no crates/* dependents. Crate's own doc comment confirms it bridges INTO v1, not Reborn. Covered by "Tests (Legacy)". #5657
crates/ironclaw_attachments/src/lib.rs Declarative crate facade: module declarations, constants, and re-exports only; executable attachment modules remain covered. #6524
crates/ironclaw_extension_host/src/ingress/mod.rs Declarative ingress module facade and documentation only; executable router modules remain covered. #6524
crates/ironclaw_host_api/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable host API modules remain covered. #6524
crates/ironclaw_host_api/src/product_adapter/mod.rs Declarative product-adapter facade: module declarations and re-exports only; executable adapter modules remain covered. #6524
crates/ironclaw_llm/src/rig_adapter/tests/finish_reason_tests.rs Test-only module stored under src/ for private adapter access; cargo-llvm-cov omits test harness source from production LCOV while the exercised rig_adapter.rs production lines remain coverage-gated. #6284
crates/ironclaw_outbound/src/error.rs Declarative error vocabulary only; variants have no LLVM-instrumentable production statements. #6524
crates/ironclaw_outbound/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable outbound modules remain covered. #6524
crates/ironclaw_product/src/lib.rs Declaration-only public facade with no executable Rust statements; rustc emits no LCOV source record. Executable product behavior remains covered in the owned implementation modules. #6524
crates/ironclaw_product/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable product modules remain covered. #6524
crates/ironclaw_product/src/scoped_fs/mod.rs Declarative scoped-filesystem facade and documentation only; executable scoped filesystem modules remain covered. #6524
crates/ironclaw_reborn_composition/src/support/fs/mod.rs Declarative composition support facade: module declarations and re-exports only; executable filesystem adapters remain covered. #6524
crates/ironclaw_slack_extension/src/lib.rs Declarative Slack crate facade: module declarations and re-exports only; executable Slack modules remain covered. #6524
crates/ironclaw_telegram_extension/src/lib.rs Declarative Telegram crate facade: module declarations and re-exports only; executable Telegram modules remain covered. #6524
crates/ironclaw_threads/src/lib.rs Declaration-only public facade with no executable Rust statements; rustc emits no LCOV source record. Executable thread behavior remains covered in the owned implementation modules. #6524
crates/ironclaw_webui/src/webui_v2/mod.rs Declaration-only WebUI v2 facade with no executable Rust statements; rustc emits no LCOV source record. Executable route behavior remains covered in the owned implementation modules. #6524

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6967 July 31, 2026 17:51 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/reborn/target-architecture/CHECKLIST.md (2)

136-136: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Make the WS1 exception counts consistent.

Line [136] records 20 → 15. Line [30] still requires removal of seven W4.3 exceptions plus auth→turns and sets the target at ≤12. Reconcile the checklist with the actual LAYER_MATRIX_EXCEPTIONS list and ratchet test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reborn/target-architecture/CHECKLIST.md` at line 136, Reconcile the WS1
exception-count claims in the checklist with the current LAYER_MATRIX_EXCEPTIONS
contents and reborn_layer_matrix_exceptions_ratchet_down_only test. Update the
conflicting “20 → 15” and “≤12” entries, including the W4.3 and auth→turns
removal requirements, so the checklist reflects the actual list size and ratchet
ceiling.

Source: Coding guidelines


21-21: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the stale event_projections → turns edge.

document/reborn/target-architecture/CHECKLIST.md line 111 says ProjectionError::TurnEventRebaseRequired carries ironclaw_turns::EventCursor, but TurnEventCursor is now ironclaw_host_api::turn::EventCursor and ironclaw_event_projections/Cargo.toml no longer depends on ironclaw_turns. Repoint the row text to ironclaw_host_api or remove this edge from WS1 complete criteria if the real live edge concern has been addressed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reborn/target-architecture/CHECKLIST.md` at line 21, Update the stale
event_projections-to-turns reference in the WS1 completion criteria: change the
documented EventCursor type to ironclaw_host_api::turn::EventCursor and remove
the obsolete ironclaw_turns dependency edge, or delete that edge if no live
dependency remains. Preserve the existing
ProjectionError::TurnEventRebaseRequired requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docs/reborn/target-architecture/CHECKLIST.md`:
- Line 136: Reconcile the WS1 exception-count claims in the checklist with the
current LAYER_MATRIX_EXCEPTIONS contents and
reborn_layer_matrix_exceptions_ratchet_down_only test. Update the conflicting
“20 → 15” and “≤12” entries, including the W4.3 and auth→turns removal
requirements, so the checklist reflects the actual list size and ratchet
ceiling.
- Line 21: Update the stale event_projections-to-turns reference in the WS1
completion criteria: change the documented EventCursor type to
ironclaw_host_api::turn::EventCursor and remove the obsolete ironclaw_turns
dependency edge, or delete that edge if no live dependency remains. Preserve the
existing ProjectionError::TurnEventRebaseRequired requirement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2dadcab7-c23d-4af3-be5f-7fc1729306ba

📥 Commits

Reviewing files that changed from the base of the PR and between a8e8bfa and e0888a8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/Cargo.lock
📒 Files selected for processing (1)
  • docs/reborn/target-architecture/CHECKLIST.md

BenKurrek and others added 2 commits July 31, 2026 14:46
…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>
@BenKurrek

Copy link
Copy Markdown
Collaborator Author

placeholder

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6967 July 31, 2026 18:55 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ironclaw_product/src/projection/tests.rs`:
- Around line 34-37: Update the imports in the test module so EventCursor is
imported from ironclaw_host_api::turn without the TurnEventCursor alias. Remove
it from the ironclaw_turns import list, leaving only coordination and projection
types there.

In `@docs/reborn/target-architecture/CHECKLIST.md`:
- Line 97: Remove the unrelated default-members and fuzz/ checklist changes from
the target-architecture checklist, leaving only edits relevant to the
TurnGateRef and turn-vocabulary migration; alternatively, document this
additional scope in the PR body and split it into a separate focused change if
appropriate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 92ad7cba-a245-4140-bad1-dc2235125794

📥 Commits

Reviewing files that changed from the base of the PR and between e0888a8 and e1b5297.

📒 Files selected for processing (64)
  • crates/ironclaw_conversations/tests/inbound_contract.rs
  • crates/ironclaw_extension_host/src/channel_host/e2e_tests.rs
  • crates/ironclaw_extension_host/src/run_delivery_ports.rs
  • crates/ironclaw_host_api/AGENTS.md
  • crates/ironclaw_host_api/src/turn.rs
  • crates/ironclaw_loop_host/src/subagent_spawn_port.rs
  • crates/ironclaw_product/src/approval_interaction/gate_ref.rs
  • crates/ironclaw_product/src/approval_interaction/read_model.rs
  • crates/ironclaw_product/src/approval_interaction/service.rs
  • crates/ironclaw_product/src/approval_interaction/types.rs
  • crates/ironclaw_product/src/approval_prompt.rs
  • crates/ironclaw_product/src/auth_continuation.rs
  • crates/ironclaw_product/src/auth_interaction/service.rs
  • crates/ironclaw_product/src/auth_interaction/types.rs
  • crates/ironclaw_product/src/gate_state.rs
  • crates/ironclaw_product/src/inbound_turn/tests.rs
  • crates/ironclaw_product/src/product_surface_inbound.rs
  • crates/ironclaw_product/src/projection/tests.rs
  • crates/ironclaw_product/src/projection/turn_events.rs
  • crates/ironclaw_product/src/reborn_services.rs
  • crates/ironclaw_product/src/reborn_services/types.rs
  • crates/ironclaw_product/src/run_delivery.rs
  • crates/ironclaw_product/src/run_delivery/prompts.rs
  • crates/ironclaw_product/src/workflow.rs
  • crates/ironclaw_product/tests/approval_interaction_contract.rs
  • crates/ironclaw_product/tests/auth_interaction_contract.rs
  • crates/ironclaw_product/tests/product_surface_contract.rs
  • crates/ironclaw_product/tests/prompt_projection_contract.rs
  • crates/ironclaw_product/tests/reborn_services_contract.rs
  • crates/ironclaw_product/tests/run_delivery_contract.rs
  • crates/ironclaw_reborn_composition/src/blocked_auth_resume.rs
  • crates/ironclaw_reborn_composition/src/factory/auth_tests.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/openai_compat_serve.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/openai_compat_serve/tests.rs
  • crates/ironclaw_reborn_composition/src/process_gate_turn_view.rs
  • crates/ironclaw_reborn_composition/src/runtime.rs
  • crates/ironclaw_reborn_composition/src/runtime/approval.rs
  • crates/ironclaw_reborn_composition/src/runtime/approval_interaction_assembly.rs
  • crates/ironclaw_reborn_composition/src/runtime/auth_interaction.rs
  • crates/ironclaw_reborn_composition/src/runtime/capability_host/tests.rs
  • crates/ironclaw_reborn_composition/src/runtime/tests/core.rs
  • crates/ironclaw_reborn_composition/tests/auth_lifecycle.rs
  • crates/ironclaw_reborn_composition/tests/budget_approval_e2e.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/mod.rs
  • crates/ironclaw_runner/src/subagent/await_edge/resolver.rs
  • crates/ironclaw_runner/src/subagent/await_edge/store.rs
  • crates/ironclaw_runner/src/subagent/prompt_material.rs
  • crates/ironclaw_turns/src/run_profile/runtime_context.rs
  • crates/ironclaw_turns/src/status.rs
  • crates/ironclaw_turns/tests/agent_loop_host_contract.rs
  • docs/reborn/target-architecture/CHECKLIST.md
  • tests/integration/auth/auth_gate.rs
  • tests/integration/changed-coverage-exemptions.toml
  • tests/integration/generated_restart_sequences.rs
  • tests/integration/group_approvals/scenario_gate_ref_edge_cases.rs
  • tests/integration/subagent_await_edge.rs
  • tests/integration/support/builder.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

Comment on lines +34 to +37
CancelRunRequest, CancelRunResponse, EventCursor as TurnEventCursor, GetRunStateRequest,
ResumeTurnRequest, ResumeTurnResponse, SubmitTurnRequest, SubmitTurnResponse,
TurnBlockedGateKind, TurnBlockedGateMetadata, TurnError, TurnEventKind, TurnEventPage,
TurnLifecycleEvent, TurnRunId, TurnRunState, TurnStatus,
TurnLifecycleEvent, TurnRunState,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Import EventCursor from its canonical owner.

Line 34 still imports EventCursor as TurnEventCursor from ironclaw_turns. Import it from ironclaw_host_api::turn and leave only coordination and projection types under ironclaw_turns.

This change is required to complete the ownership migration and avoid compatibility-boundary imports.

As per path instructions, consumers must import shared types from their owning crate. Based on learnings, modified import lines must reference the canonical contract owner.

Proposed import fix
 use ironclaw_host_api::turn::{
-    AcceptedMessageRef, RunProfileId, RunProfileVersion, SourceBindingRef, TurnGateRef, TurnRunId,
+    AcceptedMessageRef, EventCursor as TurnEventCursor, RunProfileId, RunProfileVersion,
+    SourceBindingRef, TurnGateRef, TurnRunId,
     TurnStatus,
 };

 use ironclaw_turns::{
-    CancelRunRequest, CancelRunResponse, EventCursor as TurnEventCursor, GetRunStateRequest,
+    CancelRunRequest, CancelRunResponse, GetRunStateRequest,
     ResumeTurnRequest, ResumeTurnResponse, SubmitTurnRequest, SubmitTurnResponse,
     TurnBlockedGateKind, TurnBlockedGateMetadata, TurnError, TurnEventKind, TurnEventPage,
     TurnLifecycleEvent, TurnRunState,
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CancelRunRequest, CancelRunResponse, EventCursor as TurnEventCursor, GetRunStateRequest,
ResumeTurnRequest, ResumeTurnResponse, SubmitTurnRequest, SubmitTurnResponse,
TurnBlockedGateKind, TurnBlockedGateMetadata, TurnError, TurnEventKind, TurnEventPage,
TurnLifecycleEvent, TurnRunId, TurnRunState, TurnStatus,
TurnLifecycleEvent, TurnRunState,
use ironclaw_host_api::turn::{
AcceptedMessageRef, EventCursor as TurnEventCursor, RunProfileId, RunProfileVersion,
SourceBindingRef, TurnGateRef, TurnRunId, TurnStatus,
};
use ironclaw_turns::{
CancelRunRequest, CancelRunResponse, GetRunStateRequest,
ResumeTurnRequest, ResumeTurnResponse, SubmitTurnRequest, SubmitTurnResponse,
TurnBlockedGateKind, TurnBlockedGateMetadata, TurnError, TurnEventKind, TurnEventPage,
TurnLifecycleEvent, TurnRunState,
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_product/src/projection/tests.rs` around lines 34 - 37, Update
the imports in the test module so EventCursor is imported from
ironclaw_host_api::turn without the TurnEventCursor alias. Remove it from the
ironclaw_turns import list, leaving only coordination and projection types
there.

Sources: Path instructions, Learnings


- [ ] Family directories created; every crate `git mv`'d to its §5 path **with** its narrowing milestone (retain-as-is crates may move in early batches); root `members` uses family paths; CI selectors/scripts/`Cargo.toml` path deps updated per batch.
- [ ] `tools/` unchanged (`stress` stays a member; optional `default-members` trim **[decision]**); ~~root `fuzz/` deleted or re-pointed (currently unresolvable)~~ — **the `fuzz/` half landed with the WS8 dead-crates PR** (deleted, not re-pointed: it declared `[dependencies.ironclaw] path = ".."` and fuzzed `ironclaw::safety` / `ironclaw::tools`, but the root package has had no lib target since the v1 monolith went, so it could never resolve; its workspace `exclude` entry went with it); `ironclaw_safety/fuzz` untouched. The `tools/`/`default-members` half is still open.
- [ ] `tools/` unchanged (`stress` stays a member; **`default-members` trim resolved — owner decision 2026-07-31: no trim; root `default-members` stays as-is, revisit only if default-build times become a complaint**); ~~root `fuzz/` deleted or re-pointed (currently unresolvable)~~ — **the `fuzz/` half landed with the WS8 dead-crates PR** (deleted, not re-pointed: it declared `[dependencies.ironclaw] path = ".."` and fuzzed `ironclaw::safety` / `ironclaw::tools`, but the root package has had no lib target since the v1 monolith went, so it could never resolve; its workspace `exclude` entry went with it); `ironclaw_safety/fuzz` untouched.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the unrelated checklist edit or document it in the PR scope.

Line 97 records a default-members decision and a fuzz/ deletion. These changes are unrelated to the TurnGateRef and turn-vocabulary migration described in the PR objectives. Remove this edit or split and describe the additional scope in the PR body.

As per coding guidelines: “Keep pull requests focused and avoid mixing unrelated concerns,” and the PR body must describe the full diff.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reborn/target-architecture/CHECKLIST.md` at line 97, Remove the
unrelated default-members and fuzz/ checklist changes from the
target-architecture checklist, leaving only edits relevant to the TurnGateRef
and turn-vocabulary migration; alternatively, document this additional scope in
the PR body and split it into a separate focused change if appropriate.

Source: Coding guidelines

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>
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6967 July 31, 2026 19:19 Destroyed
@BenKurrek

Copy link
Copy Markdown
Collaborator Author

Coordinator sign-off (Wave 1 slot 1 — WS1.1 turn vocabulary completion).

Review protocol ran across three passes: independent verification, one bundled feedback round (CodeRabbit's 10 threads + coordinator items), and the CI-derived coverage-manifest completion. All claims verified against the branch, not the report:

  • Exceptions 20 → 15, counted in the source; the five removed entries were obsolete (stale-exception detector enforces), the three survivors are the tracked later-slot tail. All five consumer crates verified free of ironclaw_turns in their manifests.
  • The GateRef collision was the find of the slice: the deleted shim aliased TurnGateRef over an unrelated kernel type of the same name — 336 occurrences classified by import source, 14 kernel-side files correctly excluded (including turns::run_profile::resolution, which a mechanical rename would have silently corrupted), compiler-verified.
  • Un-masking spotless: all 17 relocated tests moved verbatim; the one API change (RunOriginAdapter::new error type) is pinned by a constant + tests, call sites byte-identical.
  • Review honesty: two CodeRabbit "functional" Majors traced to a false premise in the PR's own guidance wording — the doc was fixed and both types' actual contracts pinned by a new test; six threads accepted (57 files repointed to canonical imports by strict line ownership); the rest deferred with plan citations.
  • The coverage-exemption manifest is derived, not asserted: the gate's own module replayed against CI's own lcov — failure reproduced byte-identically first, then entries added to a local 100.00% (138/138) exit 0. Three honest classes (llvm-cov counter-attribution with caller-hit proof; declaration-only type-name edits; rename-touched lines verified 0-hits at base and now). No coverage was lost anywhere. Defect classes recorded on Path-keyed CI gates that survive #6946: six silent + two loud, all blocking the first family git mv #6963 for a proper gate fix before the repoint-heavy slots.
  • Docs commit ticks WS1.1 and closes PLAN decision round Move whatsapp channel source to channels-src/ for consistency #1 (Strategy B confirmed; default-members no-trim).

Ready to merge. Note for the stack: ws1/loop-contracts (slot 2) is stacked on this branch and auto-retargets to main on merge.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6967 July 31, 2026 20:07 Destroyed
@BenKurrek
BenKurrek merged commit 53391ea into main Jul 31, 2026
11 of 13 checks passed
@BenKurrek
BenKurrek deleted the ws1/turn-vocabulary branch July 31, 2026 20:07
BenKurrek added a commit that referenced this pull request Jul 31, 2026
#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
BenKurrek added a commit that referenced this pull request Jul 31, 2026
…oop (WS1.2) (#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 #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 #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 #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>
BenKurrek added a commit that referenced this pull request Jul 31, 2026
…he dual import paths (WS1.3) (#6977)

* 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 #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 #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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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 #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>

* fix(collapse): drop the import the squash-collapse duplicated

Both sides independently rewrote the same ironclaw_turns::run_profile import
into ironclaw_loop_contracts, so the textual merge kept both copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* review: correct the module count and bound the scan walk

Both CodeRabbit findings verified against the tree and real: the crate guide
still said nine modules after hosted_mcp joined on the merge-down (lib.rs
declares ten), and crates/ironclaw_webui/frontend/node_modules really is
present -- 2,506 directories the location scan descended on every run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover two arms the changed-coverage gate exposed, exempt the rest

The gate flagged nine sites. Two were genuine gaps in this crate's own contract
surface and are now tested rather than exempted: the body_json_pointer egress
injection arm (the existing shape test grew the rejecting and accepting cases)
and ExtensionContract::capability. The remaining seven are declaration-only
lines, or a type path inside a body that was already fully uncovered, and are
exempted with their per-site evidence.

Every line number was derived by replaying the failing run's own merged lcov
against the gate until it reproduced byte-identically -- none was guessed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
BenKurrek added a commit that referenced this pull request Aug 1, 2026
…adapter half (WS1.4) (#6980)

* 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 #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 #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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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 #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>

* refactor(contracts): extract ironclaw_product_contracts and land the WS1.3 adapter half (WS1.4)

Carve the product tier's neutral contracts out of `ironclaw_host_api` into
`crates/ironclaw_product_contracts`, and — in the same change, because it is
what unblocks them — move `ChannelAdapter`/`ToolAdapter`/`RestrictedEgress`
into `ironclaw_extension_contracts` where PROPOSAL §6.1.2 assigns them.

`host_api::product_adapter` is one connected component: `channel_adapter`
names `inbound::ProductTriggerReason`, `inbound` names both
`channel_adapter::ChannelAttachmentRef` and `outbound::ProjectionCursor`,
`projection` names inbound and outbound, `interaction_commands` names inbound,
and `product_surface` names all of them. Since `ironclaw_host_api` may hold no
internal dependency, the adapters could only leave once nothing that stays
behind names them — so the product DTO modules moved with them.

`git mv` moved 15 modules at 83-100% similarity, so the diff reads as a move.

Regression coverage: `reborn_product_contract_location_scan.rs` (7 tests, 2
real gates + 5 self-tests with positive and negative fixtures) pins one home
and one import path for the product tier's ports; it fails on the four
re-export chains this change deletes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): record the hosted-MCP merge-down resolution on the WS1.4 row

The WS1.4 ruling on package_lifecycle survived the parent's #6930
reconciliation; the LifecyclePackageId split is the recorded resolution,
now stated as what happened rather than what was recommended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover the extracted auth-prompt and lifecycle-id surface

The changed-coverage gate on #6980 flagged 134 uncovered lines and 22
uncovered branch arms, all in the two modules WS1.4 created. That was a real
regression, not an attribution artifact: the code moved out of
host_api::product_adapter::outbound and host_api::package_lifecycle, and the
owning crates' unit suites did not move with it.

Fourteen tests close every one of those lines: render_channel_auth_prompt in
both the DM and mention shapes and with/without a pairing deep link, the
AuthPromptContextView constructors and wire round-trip, each nested validator
arm rejecting independently, and the bounded-id accessor and rejection surface.
Verified by replaying reborn_changed_coverage.py against the PR's own merged
lcov (byte-identical to CI) and re-measuring with cargo llvm-cov at this head:
134 -> 0.

Three sites remain exempted with per-site evidence, all unreachable by test:
a declaration-only crate facade's inner attribute, and two pre-existing error
arms whose only change is a repointed type path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover the newline/tab carve-out in bounded prompt text

The changed-coverage gate reached 100% line but left three branch arms on
validate_bounded_text's control-character predicate. Newline and tab are
deliberately legal in prompt copy — channels render multi-line instructions —
and every other control character is a rejection; both halves are now driven.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(coverage): exempt the four type-position lines the gate cannot instrument

With the auth-prompt and lifecycle-id holes tested, the gate reached 100%
line and 100% branch but still failed on four files 'contributing no
instrumented lines'. Each is a single type-position line — an enum variant
field, two parameter types, a struct field — whose only change is the
repointed path for a moved contract, wrapped onto its own line by rustfmt.
LLVM emits no coverage region for a type annotation, so no test can reach them.

Replayed against this PR's own merged lcov: exit 0, 285/285 lines, 38/38
branches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
BenKurrek added a commit that referenced this pull request Aug 1, 2026
…ss grants (WS1.5) (#6981)

* 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 #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 #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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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 #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>

* refactor(contracts): extract ironclaw_product_contracts and land the WS1.3 adapter half (WS1.4)

Carve the product tier's neutral contracts out of `ironclaw_host_api` into
`crates/ironclaw_product_contracts`, and — in the same change, because it is
what unblocks them — move `ChannelAdapter`/`ToolAdapter`/`RestrictedEgress`
into `ironclaw_extension_contracts` where PROPOSAL §6.1.2 assigns them.

`host_api::product_adapter` is one connected component: `channel_adapter`
names `inbound::ProductTriggerReason`, `inbound` names both
`channel_adapter::ChannelAttachmentRef` and `outbound::ProjectionCursor`,
`projection` names inbound and outbound, `interaction_commands` names inbound,
and `product_surface` names all of them. Since `ironclaw_host_api` may hold no
internal dependency, the adapters could only leave once nothing that stays
behind names them — so the product DTO modules moved with them.

`git mv` moved 15 modules at 83-100% similarity, so the diff reads as a move.

Regression coverage: `reborn_product_contract_location_scan.rs` (7 tests, 2
real gates + 5 self-tests with positive and negative fixtures) pins one home
and one import path for the product tier's ports; it fails on the four
re-export chains this change deletes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): record the hosted-MCP merge-down resolution on the WS1.4 row

The WS1.4 ruling on package_lifecycle survived the parent's #6930
reconciliation; the LifecyclePackageId split is the recorded resolution,
now stated as what happened rather than what was recommended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(contracts): consolidate sealed evidence minting behind witness grants (WS1.5)

CHECKLIST WS1's evidence-mint row: one sealed seam owns construction of
protocol-auth evidence, every other construction path is deleted, and the
refute-tests land with it.

The `host-auth-mint` cargo feature was not a seal. Cargo unifies features
across the packages selected in one invocation, so `ironclaw_webui`'s opt-in
(-> turns -> host_api) compiled `ironclaw_host_api` once with the gate ON for
every other crate in the same build. Measured before touching anything: a probe
in `ironclaw_agent_loop` — whose manifest names `ironclaw_host_api` with no
features — minted a verified bearer claim; it failed to compile alone and
passed as soon as `ironclaw_webui` joined the `cargo test`. Every workspace
build is the second case.

Replaced with the repo's existing witness-token idiom (`host_api::authorized`),
which no other crate's manifest can switch on:

- `HostAuthenticationGrant` <- `HostProtocolAuthenticator`, sole implementor
  `ironclaw_webui` (module-private `AuthLayerState`, trust stage T1).
- `VerifiedInboundGrant` <- `ChannelIngressVerifier`, sole implementor
  `ironclaw_extension_host` (`VerifiedEvidenceMint`, trust stage T2).
- Channel/webhook mint family -> `ironclaw_extension_contracts::verified_inbound`
  (§6.1.2); bearer/session family stays in `host_api` (§6.1.1). The evidence
  type does not move; `extension_contracts` reaches the private verified variant
  through the grant-gated `ProtocolAuthEvidence::seal_verified_inbound`.

Closed: the feature in 5 manifests + 1 CI recipe; four re-export chains
(`host_api::product_adapter`, `ironclaw_product` root, `ironclaw_product::auth`,
and composition's zero-consumer re-export).

Enforcement: `reborn_sealed_evidence_mint_ratchet` (10 tests, §11.2.5) plus
`host_api/tests/protocol_auth_evidence_seal.rs` (5) and
`extension_contracts/tests/verified_inbound_seal.rs` (4). +19 tests, 0 removed,
no surviving assertion edited. The production-struct dead-code baseline shrinks
81/282 -> 80/277: the five `dead_code` suppressions in `auth.rs` existed only
because the constructors were feature-gated.

* test(contracts): cover the extracted auth-prompt and lifecycle-id surface

The changed-coverage gate on #6980 flagged 134 uncovered lines and 22
uncovered branch arms, all in the two modules WS1.4 created. That was a real
regression, not an attribution artifact: the code moved out of
host_api::product_adapter::outbound and host_api::package_lifecycle, and the
owning crates' unit suites did not move with it.

Fourteen tests close every one of those lines: render_channel_auth_prompt in
both the DM and mention shapes and with/without a pairing deep link, the
AuthPromptContextView constructors and wire round-trip, each nested validator
arm rejecting independently, and the bounded-id accessor and rejection surface.
Verified by replaying reborn_changed_coverage.py against the PR's own merged
lcov (byte-identical to CI) and re-measuring with cargo llvm-cov at this head:
134 -> 0.

Three sites remain exempted with per-site evidence, all unreachable by test:
a declaration-only crate facade's inner attribute, and two pre-existing error
arms whose only change is a repointed type path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover the newline/tab carve-out in bounded prompt text

The changed-coverage gate reached 100% line but left three branch arms on
validate_bounded_text's control-character predicate. Newline and tab are
deliberately legal in prompt copy — channels render multi-line instructions —
and every other control character is a rejection; both halves are now driven.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(coverage): exempt the four type-position lines the gate cannot instrument

With the auth-prompt and lifecycle-id holes tested, the gate reached 100%
line and 100% branch but still failed on four files 'contributing no
instrumented lines'. Each is a single type-position line — an enum variant
field, two parameter types, a struct field — whose only change is the
repointed path for a moved contract, wrapped onto its own line by rustfmt.
LLVM emits no coverage region for a type annotation, so no test can reach them.

Replayed against this PR's own merged lcov: exit 0, 285/285 lines, 38/38
branches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
BenKurrek added a commit that referenced this pull request Aug 1, 2026
…7) (#6982)

* 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 #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 #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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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 #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>

* refactor(contracts): extract ironclaw_product_contracts and land the WS1.3 adapter half (WS1.4)

Carve the product tier's neutral contracts out of `ironclaw_host_api` into
`crates/ironclaw_product_contracts`, and — in the same change, because it is
what unblocks them — move `ChannelAdapter`/`ToolAdapter`/`RestrictedEgress`
into `ironclaw_extension_contracts` where PROPOSAL §6.1.2 assigns them.

`host_api::product_adapter` is one connected component: `channel_adapter`
names `inbound::ProductTriggerReason`, `inbound` names both
`channel_adapter::ChannelAttachmentRef` and `outbound::ProjectionCursor`,
`projection` names inbound and outbound, `interaction_commands` names inbound,
and `product_surface` names all of them. Since `ironclaw_host_api` may hold no
internal dependency, the adapters could only leave once nothing that stays
behind names them — so the product DTO modules moved with them.

`git mv` moved 15 modules at 83-100% similarity, so the diff reads as a move.

Regression coverage: `reborn_product_contract_location_scan.rs` (7 tests, 2
real gates + 5 self-tests with positive and negative fixtures) pins one home
and one import path for the product tier's ports; it fails on the four
re-export chains this change deletes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): record the hosted-MCP merge-down resolution on the WS1.4 row

The WS1.4 ruling on package_lifecycle survived the parent's #6930
reconciliation; the LifecyclePackageId split is the recorded resolution,
now stated as what happened rather than what was recommended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(contracts): consolidate sealed evidence minting behind witness grants (WS1.5)

CHECKLIST WS1's evidence-mint row: one sealed seam owns construction of
protocol-auth evidence, every other construction path is deleted, and the
refute-tests land with it.

The `host-auth-mint` cargo feature was not a seal. Cargo unifies features
across the packages selected in one invocation, so `ironclaw_webui`'s opt-in
(-> turns -> host_api) compiled `ironclaw_host_api` once with the gate ON for
every other crate in the same build. Measured before touching anything: a probe
in `ironclaw_agent_loop` — whose manifest names `ironclaw_host_api` with no
features — minted a verified bearer claim; it failed to compile alone and
passed as soon as `ironclaw_webui` joined the `cargo test`. Every workspace
build is the second case.

Replaced with the repo's existing witness-token idiom (`host_api::authorized`),
which no other crate's manifest can switch on:

- `HostAuthenticationGrant` <- `HostProtocolAuthenticator`, sole implementor
  `ironclaw_webui` (module-private `AuthLayerState`, trust stage T1).
- `VerifiedInboundGrant` <- `ChannelIngressVerifier`, sole implementor
  `ironclaw_extension_host` (`VerifiedEvidenceMint`, trust stage T2).
- Channel/webhook mint family -> `ironclaw_extension_contracts::verified_inbound`
  (§6.1.2); bearer/session family stays in `host_api` (§6.1.1). The evidence
  type does not move; `extension_contracts` reaches the private verified variant
  through the grant-gated `ProtocolAuthEvidence::seal_verified_inbound`.

Closed: the feature in 5 manifests + 1 CI recipe; four re-export chains
(`host_api::product_adapter`, `ironclaw_product` root, `ironclaw_product::auth`,
and composition's zero-consumer re-export).

Enforcement: `reborn_sealed_evidence_mint_ratchet` (10 tests, §11.2.5) plus
`host_api/tests/protocol_auth_evidence_seal.rs` (5) and
`extension_contracts/tests/verified_inbound_seal.rs` (4). +19 tests, 0 removed,
no surviving assertion edited. The production-struct dead-code baseline shrinks
81/282 -> 80/277: the five `dead_code` suppressions in `auth.rs` existed only
because the constructors were feature-gated.

* refactor(common): narrow ironclaw_common to its §6.1.5 contract (WS1.6)

`ironclaw_common` now matches PROPOSAL §6.1.5's *Owns* list exactly, plus
three named exceptions that a pinned rule blocks from moving.

Deleted, not relocated — each re-verified with `git grep` over tracked files
on this base:

* `event.rs` (1,234 lines). All seven exported symbols have zero consumers
  outside the crate; the only live workspace references are negative ones
  (the architecture test asserting the OpenAI-compatible routes must *not*
  stream `AppEvent`, one doc comment, one Python docstring). This confirms
  WS1.4's disposition 7 rather than repeating it, and closes the WS8
  deletion-candidate row. The `AppEvent` `ForbiddenUse` entry stays as a
  reintroduction pin with its reason updated to say the enum is gone — the
  convention the file already applies to the retired v1 `ironclaw::` crate.
* `platform.rs`. `PlatformInfo` has zero references anywhere; §6.1.5's
  "→ its consumer" is unsatisfiable because there is no consumer.
* The four budget constants. Zero consumers workspace-wide, and
  `ironclaw_resources` already governs the same concern with a live,
  differently-shaped mechanism (`ResourceLimits::max_wall_clock_ms`) that
  references none of them — so "→ `resources`" would have seeded four
  unreachable constants beside a working governor.

Moved: `automation` → `ironclaw_triggers`, which owns automations and had
already defined `MAX_TRIGGER_NAME_BYTES` as an alias of the common constant.
This eviction is in §6.1.5 but missing from the CHECKLIST row.
`ironclaw_product`'s cross-crate `pub use` of the newtype (a second import
path with zero external consumers) is deleted rather than re-sourced,
following the WS1.3/WS1.4 dual-path rule.

Already done, so recorded rather than redone: `trust_boundary` went with
#6943, and the `AttachmentRef` collision is already resolved as
`ChannelAttachmentRef`. That rename left a wrong cross-reference in its own
doc comment (`ironclaw_common::ChannelAttachmentRef` for
`ironclaw_common::AttachmentRef`), fixed here.

Not moved, with evidence: `provider_transcript` (its `agent_loop` consumer
is contracts-only by pinned rule; `ironclaw_llm` is substrates),
`model_selection` (`openai_compat`'s boundary rule forbids `ironclaw_llm`
outright, and `llm` never uses the module), and `llm_costs` (`llm` uses 2 of
its 7 public items; moving it would hand `ironclaw_product` a
reqwest/rig-core cone for a pricing table — the `ModelCostTable` port is the
real seam, and that is a WS4 design change). All three are documented in
`crates/ironclaw_common/AGENTS.md`, which also stops claiming ownership of
the long-deleted `trust_boundary`.

Un-masking: `ironclaw_common` 123 → 110 tests (10 `event::tests::*`, each
classified to a deleted symbol; 3 `automation::tests::*` reappearing
verbatim in `ironclaw_triggers`, 166 → 169). Zero unclassified, no surviving
test edited. The extension-specificity gate demanded its now-stale
`platform.rs` carve-out be deleted — the property it was built with.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(host-api): move failure-summary data out of the runner (WS1.7)

PROPOSAL §6.1.1 assigns the category→summary tables to `host_api::failure`
"so product stops depending on runner". They are now
`ironclaw_host_api::failure::{categories, summary}`, and
`ironclaw_product`'s manifest no longer names `ironclaw_runner` under
`[dependencies]` — the dev-dep stays and is documented, because product's
harnesses legitimately build a full turn stack.

The row calls these "the two single-symbol product edges". Measured on this
base, neither is single-symbol:

* `product → runner` was two modules — a category constant plus four
  `failure_summary` items. **Severed.** What could not follow, because
  `ironclaw_host_api` may hold no internal dependency: the envelope *writer*
  (typed on `agent_loop`'s `CheckpointKind` and `loop_contracts`'
  `LoopSafeSummary`) and every classifier. Both runner modules are now
  private.
* `product → loop_host` has **three** production sites, and only the prompt
  constant was one. `FAILURE_EXPLANATION_SYSTEM_PROMPT` and its asset are
  now product-owned (prompt *content* is out of charter for the loop tier,
  §6.1.4/§6.7.2), but `project_create_capability.rs` (the #6691 arrival
  §2.3 already flags) and — not previously recorded anywhere —
  `scoped_fs/attachment_landing.rs`, which consumes `LoopAttachmentReadPort`,
  both carry real behavior. The edge survives; severing it is WS5/WS6 work.

One disposition worth review: the envelope reader moved and now revalidates
its cause with `SafeSummary::new` rather than `LoopSafeSummary::new`. That
is behavior-preserving, and the claim is pinned rather than asserted —
`validate_loop_safe_summary` delegates to `SafeSummary::new` with exactly
one bypass, the fixed `INPUT_ENCODE_HUMAN_SUMMARY` literal, which
independently satisfies the canonical rule
(`loop_input_encode_sentinel_needs_no_bypass_here`). Splitting writer from
reader also split the checkpoint-stage vocabulary across two crates, so the
pre-existing round-trip (which covered only `BeforeModel`) gains a sibling
driving all four `CheckpointKind`s writer→reader across the boundary.

Neither edge was ever a `LAYER_MATRIX_EXCEPTION` — `products → kernel` and
`products → loops` are matrix-legal — so this cannot move the count, which
stays at 13. The value is graph narrowing and prompt-content placement.

Enumerating gates, all shrink-only: the composition pub-use snapshot loses
exactly one line, because the `reborn_failure_summary_for_category`
re-export is deleted rather than re-sourced (its sole consumer, the CLI,
already depends on `ironclaw_host_api`, so the facade hop bought nothing).
The product-side category-coverage `include_str!` is repointed, not added,
so the §11.2.7 inventory is unchanged at 19.

Also hardens `reborn_loop_port_location_scan`'s directory walk, which
excluded only `target` and so descended the whole npm tree on any checkout
with the frontend installed — the same defect already fixed in the sibling
scanners.

Un-masking: 10 table tests moved runner → host_api with identical names and
no content edits (runner 467 → 458, host_api 248 → 260; deltas are the 10
moved plus 1 new round-trip and 2 new pins). loop_host and product rosters
are byte-identical at 572 and 1032.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): tick WS1.6, record WS1.7's partial sever, close Wave 1

Ticks the `ironclaw_common` narrowing row with the seven dispositions that
differ from how it was written (five of six clauses moved differently:
two deletions where the row said relocate, one unsatisfiable "→ its
consumer", two clauses already done, one eviction present in §6.1.5 but
missing from the row), and the three LLM-data evictions each refuted by a
pinned rule.

Leaves the WS1.7 row **open** rather than ticking it. Two of three clauses
landed — the data move and the `product → runner` sever — but the
`product → loop_host` sever did not and cannot here: the edge has three
production sites, two of them behavioral, one of which
(`attachment_landing.rs`'s `LoopAttachmentReadPort`) was not recorded
anywhere before. Ticking it would over-claim.

Records the Wave 1 exit state on the WS1 verify row. The milestone's
"20 → 12" is not met and **the 12 was wrong**: the true end-state is 13,
because the 13th survivor, `conversations → turns`, is turn *admission
authority* rather than vocabulary, so no contracts crate can dissolve it and
it falls in WS5. The wave's other clauses did land — three contracts crates,
`agent_loop` contracts-only with zero exceptions, evidence mint sealed. The
mint row's measurement is carried forward as the reusable finding: the
`host-auth-mint` cargo feature was never a seal, because feature unification
compiled the gate on for every crate in any workspace-wide build.

Adds the dated one-line Wave 1 summary to PLAN's Wave 1 section, including
the discipline every slot in this wave independently confirmed: re-measure
rows against your own base, never inherit a predecessor's count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
BenKurrek added a commit that referenced this pull request Aug 1, 2026
…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>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…ire the turns shims (WS1.1) (nearai#6967)

* 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>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…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>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…he dual import paths (WS1.3) (nearai#6977)

* 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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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>

* fix(collapse): drop the import the squash-collapse duplicated

Both sides independently rewrote the same ironclaw_turns::run_profile import
into ironclaw_loop_contracts, so the textual merge kept both copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* review: correct the module count and bound the scan walk

Both CodeRabbit findings verified against the tree and real: the crate guide
still said nine modules after hosted_mcp joined on the merge-down (lib.rs
declares ten), and crates/ironclaw_webui/frontend/node_modules really is
present -- 2,506 directories the location scan descended on every run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover two arms the changed-coverage gate exposed, exempt the rest

The gate flagged nine sites. Two were genuine gaps in this crate's own contract
surface and are now tested rather than exempted: the body_json_pointer egress
injection arm (the existing shape test grew the rejecting and accepting cases)
and ExtensionContract::capability. The remaining seven are declaration-only
lines, or a type path inside a body that was already fully uncovered, and are
exempted with their per-site evidence.

Every line number was derived by replaying the failing run's own merged lcov
against the gate until it reproduced byte-identically -- none was guessed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…adapter half (WS1.4) (nearai#6980)

* 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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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>

* refactor(contracts): extract ironclaw_product_contracts and land the WS1.3 adapter half (WS1.4)

Carve the product tier's neutral contracts out of `ironclaw_host_api` into
`crates/ironclaw_product_contracts`, and — in the same change, because it is
what unblocks them — move `ChannelAdapter`/`ToolAdapter`/`RestrictedEgress`
into `ironclaw_extension_contracts` where PROPOSAL §6.1.2 assigns them.

`host_api::product_adapter` is one connected component: `channel_adapter`
names `inbound::ProductTriggerReason`, `inbound` names both
`channel_adapter::ChannelAttachmentRef` and `outbound::ProjectionCursor`,
`projection` names inbound and outbound, `interaction_commands` names inbound,
and `product_surface` names all of them. Since `ironclaw_host_api` may hold no
internal dependency, the adapters could only leave once nothing that stays
behind names them — so the product DTO modules moved with them.

`git mv` moved 15 modules at 83-100% similarity, so the diff reads as a move.

Regression coverage: `reborn_product_contract_location_scan.rs` (7 tests, 2
real gates + 5 self-tests with positive and negative fixtures) pins one home
and one import path for the product tier's ports; it fails on the four
re-export chains this change deletes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): record the hosted-MCP merge-down resolution on the WS1.4 row

The WS1.4 ruling on package_lifecycle survived the parent's nearai#6930
reconciliation; the LifecyclePackageId split is the recorded resolution,
now stated as what happened rather than what was recommended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover the extracted auth-prompt and lifecycle-id surface

The changed-coverage gate on nearai#6980 flagged 134 uncovered lines and 22
uncovered branch arms, all in the two modules WS1.4 created. That was a real
regression, not an attribution artifact: the code moved out of
host_api::product_adapter::outbound and host_api::package_lifecycle, and the
owning crates' unit suites did not move with it.

Fourteen tests close every one of those lines: render_channel_auth_prompt in
both the DM and mention shapes and with/without a pairing deep link, the
AuthPromptContextView constructors and wire round-trip, each nested validator
arm rejecting independently, and the bounded-id accessor and rejection surface.
Verified by replaying reborn_changed_coverage.py against the PR's own merged
lcov (byte-identical to CI) and re-measuring with cargo llvm-cov at this head:
134 -> 0.

Three sites remain exempted with per-site evidence, all unreachable by test:
a declaration-only crate facade's inner attribute, and two pre-existing error
arms whose only change is a repointed type path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover the newline/tab carve-out in bounded prompt text

The changed-coverage gate reached 100% line but left three branch arms on
validate_bounded_text's control-character predicate. Newline and tab are
deliberately legal in prompt copy — channels render multi-line instructions —
and every other control character is a rejection; both halves are now driven.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(coverage): exempt the four type-position lines the gate cannot instrument

With the auth-prompt and lifecycle-id holes tested, the gate reached 100%
line and 100% branch but still failed on four files 'contributing no
instrumented lines'. Each is a single type-position line — an enum variant
field, two parameter types, a struct field — whose only change is the
repointed path for a moved contract, wrapped onto its own line by rustfmt.
LLVM emits no coverage region for a type annotation, so no test can reach them.

Replayed against this PR's own merged lcov: exit 0, 285/285 lines, 38/38
branches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…ss grants (WS1.5) (nearai#6981)

* 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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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>

* refactor(contracts): extract ironclaw_product_contracts and land the WS1.3 adapter half (WS1.4)

Carve the product tier's neutral contracts out of `ironclaw_host_api` into
`crates/ironclaw_product_contracts`, and — in the same change, because it is
what unblocks them — move `ChannelAdapter`/`ToolAdapter`/`RestrictedEgress`
into `ironclaw_extension_contracts` where PROPOSAL §6.1.2 assigns them.

`host_api::product_adapter` is one connected component: `channel_adapter`
names `inbound::ProductTriggerReason`, `inbound` names both
`channel_adapter::ChannelAttachmentRef` and `outbound::ProjectionCursor`,
`projection` names inbound and outbound, `interaction_commands` names inbound,
and `product_surface` names all of them. Since `ironclaw_host_api` may hold no
internal dependency, the adapters could only leave once nothing that stays
behind names them — so the product DTO modules moved with them.

`git mv` moved 15 modules at 83-100% similarity, so the diff reads as a move.

Regression coverage: `reborn_product_contract_location_scan.rs` (7 tests, 2
real gates + 5 self-tests with positive and negative fixtures) pins one home
and one import path for the product tier's ports; it fails on the four
re-export chains this change deletes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): record the hosted-MCP merge-down resolution on the WS1.4 row

The WS1.4 ruling on package_lifecycle survived the parent's nearai#6930
reconciliation; the LifecyclePackageId split is the recorded resolution,
now stated as what happened rather than what was recommended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(contracts): consolidate sealed evidence minting behind witness grants (WS1.5)

CHECKLIST WS1's evidence-mint row: one sealed seam owns construction of
protocol-auth evidence, every other construction path is deleted, and the
refute-tests land with it.

The `host-auth-mint` cargo feature was not a seal. Cargo unifies features
across the packages selected in one invocation, so `ironclaw_webui`'s opt-in
(-> turns -> host_api) compiled `ironclaw_host_api` once with the gate ON for
every other crate in the same build. Measured before touching anything: a probe
in `ironclaw_agent_loop` — whose manifest names `ironclaw_host_api` with no
features — minted a verified bearer claim; it failed to compile alone and
passed as soon as `ironclaw_webui` joined the `cargo test`. Every workspace
build is the second case.

Replaced with the repo's existing witness-token idiom (`host_api::authorized`),
which no other crate's manifest can switch on:

- `HostAuthenticationGrant` <- `HostProtocolAuthenticator`, sole implementor
  `ironclaw_webui` (module-private `AuthLayerState`, trust stage T1).
- `VerifiedInboundGrant` <- `ChannelIngressVerifier`, sole implementor
  `ironclaw_extension_host` (`VerifiedEvidenceMint`, trust stage T2).
- Channel/webhook mint family -> `ironclaw_extension_contracts::verified_inbound`
  (§6.1.2); bearer/session family stays in `host_api` (§6.1.1). The evidence
  type does not move; `extension_contracts` reaches the private verified variant
  through the grant-gated `ProtocolAuthEvidence::seal_verified_inbound`.

Closed: the feature in 5 manifests + 1 CI recipe; four re-export chains
(`host_api::product_adapter`, `ironclaw_product` root, `ironclaw_product::auth`,
and composition's zero-consumer re-export).

Enforcement: `reborn_sealed_evidence_mint_ratchet` (10 tests, §11.2.5) plus
`host_api/tests/protocol_auth_evidence_seal.rs` (5) and
`extension_contracts/tests/verified_inbound_seal.rs` (4). +19 tests, 0 removed,
no surviving assertion edited. The production-struct dead-code baseline shrinks
81/282 -> 80/277: the five `dead_code` suppressions in `auth.rs` existed only
because the constructors were feature-gated.

* test(contracts): cover the extracted auth-prompt and lifecycle-id surface

The changed-coverage gate on nearai#6980 flagged 134 uncovered lines and 22
uncovered branch arms, all in the two modules WS1.4 created. That was a real
regression, not an attribution artifact: the code moved out of
host_api::product_adapter::outbound and host_api::package_lifecycle, and the
owning crates' unit suites did not move with it.

Fourteen tests close every one of those lines: render_channel_auth_prompt in
both the DM and mention shapes and with/without a pairing deep link, the
AuthPromptContextView constructors and wire round-trip, each nested validator
arm rejecting independently, and the bounded-id accessor and rejection surface.
Verified by replaying reborn_changed_coverage.py against the PR's own merged
lcov (byte-identical to CI) and re-measuring with cargo llvm-cov at this head:
134 -> 0.

Three sites remain exempted with per-site evidence, all unreachable by test:
a declaration-only crate facade's inner attribute, and two pre-existing error
arms whose only change is a repointed type path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(contracts): cover the newline/tab carve-out in bounded prompt text

The changed-coverage gate reached 100% line but left three branch arms on
validate_bounded_text's control-character predicate. Newline and tab are
deliberately legal in prompt copy — channels render multi-line instructions —
and every other control character is a rejection; both halves are now driven.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(coverage): exempt the four type-position lines the gate cannot instrument

With the auth-prompt and lifecycle-id holes tested, the gate reached 100%
line and 100% branch but still failed on four files 'contributing no
instrumented lines'. Each is a single type-position line — an enum variant
field, two parameter types, a struct field — whose only change is the
repointed path for a moved contract, wrapped onto its own line by rustfmt.
LLVM emits no coverage region for a type annotation, so no test can reach them.

Replayed against this PR's own merged lcov: exit 0, 285/285 lines, 38/38
branches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…7) (nearai#6982)

* 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>

* refactor(contracts): extract ironclaw_extension_contracts and close the dual import paths (WS1.3)

Carve the extension tier's neutral contracts out of the host API into a new
contracts-layer crate per PROPOSAL 6.1.2, repoint every consumer, and pin the
boundary with the 11.2.3 purity allowlist and the 11.2.4 location scan.

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(collapse): reconcile the new crate's lock pins with main's dep bumps

The extension_contracts lock entry was generated before the parent branch
collapsed against main, so it pinned thiserror 2.0.18 and toml 1.1.2 — versions
that no longer have [[package]] blocks. --locked lanes would have failed to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(arch): register the new contracts crate in the two gates that enumerate deps

The composition pub-use snapshot still carried product's PreferenceTargetCodec
re-export, and the CLI's exact-dependency allowlist did not know the extension
tier's contracts crate. Both are enumerating gates, so both failed loudly rather
than passing vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(contracts): record why the extension tier's traits are not sealed

The visibility kit's sealed-strategy template exists to close a strategy set;
every trait here exists to be implemented outside the crate. State that, so the
absence reads as a decision rather than an omission.

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>

* refactor(contracts): extract ironclaw_product_contracts and land the WS1.3 adapter half (WS1.4)

Carve the product tier's neutral contracts out of `ironclaw_host_api` into
`crates/ironclaw_product_contracts`, and — in the same change, because it is
what unblocks them — move `ChannelAdapter`/`ToolAdapter`/`RestrictedEgress`
into `ironclaw_extension_contracts` where PROPOSAL §6.1.2 assigns them.

`host_api::product_adapter` is one connected component: `channel_adapter`
names `inbound::ProductTriggerReason`, `inbound` names both
`channel_adapter::ChannelAttachmentRef` and `outbound::ProjectionCursor`,
`projection` names inbound and outbound, `interaction_commands` names inbound,
and `product_surface` names all of them. Since `ironclaw_host_api` may hold no
internal dependency, the adapters could only leave once nothing that stays
behind names them — so the product DTO modules moved with them.

`git mv` moved 15 modules at 83-100% similarity, so the diff reads as a move.

Regression coverage: `reborn_product_contract_location_scan.rs` (7 tests, 2
real gates + 5 self-tests with positive and negative fixtures) pins one home
and one import path for the product tier's ports; it fails on the four
re-export chains this change deletes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): record the hosted-MCP merge-down resolution on the WS1.4 row

The WS1.4 ruling on package_lifecycle survived the parent's nearai#6930
reconciliation; the LifecyclePackageId split is the recorded resolution,
now stated as what happened rather than what was recommended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(contracts): consolidate sealed evidence minting behind witness grants (WS1.5)

CHECKLIST WS1's evidence-mint row: one sealed seam owns construction of
protocol-auth evidence, every other construction path is deleted, and the
refute-tests land with it.

The `host-auth-mint` cargo feature was not a seal. Cargo unifies features
across the packages selected in one invocation, so `ironclaw_webui`'s opt-in
(-> turns -> host_api) compiled `ironclaw_host_api` once with the gate ON for
every other crate in the same build. Measured before touching anything: a probe
in `ironclaw_agent_loop` — whose manifest names `ironclaw_host_api` with no
features — minted a verified bearer claim; it failed to compile alone and
passed as soon as `ironclaw_webui` joined the `cargo test`. Every workspace
build is the second case.

Replaced with the repo's existing witness-token idiom (`host_api::authorized`),
which no other crate's manifest can switch on:

- `HostAuthenticationGrant` <- `HostProtocolAuthenticator`, sole implementor
  `ironclaw_webui` (module-private `AuthLayerState`, trust stage T1).
- `VerifiedInboundGrant` <- `ChannelIngressVerifier`, sole implementor
  `ironclaw_extension_host` (`VerifiedEvidenceMint`, trust stage T2).
- Channel/webhook mint family -> `ironclaw_extension_contracts::verified_inbound`
  (§6.1.2); bearer/session family stays in `host_api` (§6.1.1). The evidence
  type does not move; `extension_contracts` reaches the private verified variant
  through the grant-gated `ProtocolAuthEvidence::seal_verified_inbound`.

Closed: the feature in 5 manifests + 1 CI recipe; four re-export chains
(`host_api::product_adapter`, `ironclaw_product` root, `ironclaw_product::auth`,
and composition's zero-consumer re-export).

Enforcement: `reborn_sealed_evidence_mint_ratchet` (10 tests, §11.2.5) plus
`host_api/tests/protocol_auth_evidence_seal.rs` (5) and
`extension_contracts/tests/verified_inbound_seal.rs` (4). +19 tests, 0 removed,
no surviving assertion edited. The production-struct dead-code baseline shrinks
81/282 -> 80/277: the five `dead_code` suppressions in `auth.rs` existed only
because the constructors were feature-gated.

* refactor(common): narrow ironclaw_common to its §6.1.5 contract (WS1.6)

`ironclaw_common` now matches PROPOSAL §6.1.5's *Owns* list exactly, plus
three named exceptions that a pinned rule blocks from moving.

Deleted, not relocated — each re-verified with `git grep` over tracked files
on this base:

* `event.rs` (1,234 lines). All seven exported symbols have zero consumers
  outside the crate; the only live workspace references are negative ones
  (the architecture test asserting the OpenAI-compatible routes must *not*
  stream `AppEvent`, one doc comment, one Python docstring). This confirms
  WS1.4's disposition 7 rather than repeating it, and closes the WS8
  deletion-candidate row. The `AppEvent` `ForbiddenUse` entry stays as a
  reintroduction pin with its reason updated to say the enum is gone — the
  convention the file already applies to the retired v1 `ironclaw::` crate.
* `platform.rs`. `PlatformInfo` has zero references anywhere; §6.1.5's
  "→ its consumer" is unsatisfiable because there is no consumer.
* The four budget constants. Zero consumers workspace-wide, and
  `ironclaw_resources` already governs the same concern with a live,
  differently-shaped mechanism (`ResourceLimits::max_wall_clock_ms`) that
  references none of them — so "→ `resources`" would have seeded four
  unreachable constants beside a working governor.

Moved: `automation` → `ironclaw_triggers`, which owns automations and had
already defined `MAX_TRIGGER_NAME_BYTES` as an alias of the common constant.
This eviction is in §6.1.5 but missing from the CHECKLIST row.
`ironclaw_product`'s cross-crate `pub use` of the newtype (a second import
path with zero external consumers) is deleted rather than re-sourced,
following the WS1.3/WS1.4 dual-path rule.

Already done, so recorded rather than redone: `trust_boundary` went with
nearai#6943, and the `AttachmentRef` collision is already resolved as
`ChannelAttachmentRef`. That rename left a wrong cross-reference in its own
doc comment (`ironclaw_common::ChannelAttachmentRef` for
`ironclaw_common::AttachmentRef`), fixed here.

Not moved, with evidence: `provider_transcript` (its `agent_loop` consumer
is contracts-only by pinned rule; `ironclaw_llm` is substrates),
`model_selection` (`openai_compat`'s boundary rule forbids `ironclaw_llm`
outright, and `llm` never uses the module), and `llm_costs` (`llm` uses 2 of
its 7 public items; moving it would hand `ironclaw_product` a
reqwest/rig-core cone for a pricing table — the `ModelCostTable` port is the
real seam, and that is a WS4 design change). All three are documented in
`crates/ironclaw_common/AGENTS.md`, which also stops claiming ownership of
the long-deleted `trust_boundary`.

Un-masking: `ironclaw_common` 123 → 110 tests (10 `event::tests::*`, each
classified to a deleted symbol; 3 `automation::tests::*` reappearing
verbatim in `ironclaw_triggers`, 166 → 169). Zero unclassified, no surviving
test edited. The extension-specificity gate demanded its now-stale
`platform.rs` carve-out be deleted — the property it was built with.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(host-api): move failure-summary data out of the runner (WS1.7)

PROPOSAL §6.1.1 assigns the category→summary tables to `host_api::failure`
"so product stops depending on runner". They are now
`ironclaw_host_api::failure::{categories, summary}`, and
`ironclaw_product`'s manifest no longer names `ironclaw_runner` under
`[dependencies]` — the dev-dep stays and is documented, because product's
harnesses legitimately build a full turn stack.

The row calls these "the two single-symbol product edges". Measured on this
base, neither is single-symbol:

* `product → runner` was two modules — a category constant plus four
  `failure_summary` items. **Severed.** What could not follow, because
  `ironclaw_host_api` may hold no internal dependency: the envelope *writer*
  (typed on `agent_loop`'s `CheckpointKind` and `loop_contracts`'
  `LoopSafeSummary`) and every classifier. Both runner modules are now
  private.
* `product → loop_host` has **three** production sites, and only the prompt
  constant was one. `FAILURE_EXPLANATION_SYSTEM_PROMPT` and its asset are
  now product-owned (prompt *content* is out of charter for the loop tier,
  §6.1.4/§6.7.2), but `project_create_capability.rs` (the nearai#6691 arrival
  §2.3 already flags) and — not previously recorded anywhere —
  `scoped_fs/attachment_landing.rs`, which consumes `LoopAttachmentReadPort`,
  both carry real behavior. The edge survives; severing it is WS5/WS6 work.

One disposition worth review: the envelope reader moved and now revalidates
its cause with `SafeSummary::new` rather than `LoopSafeSummary::new`. That
is behavior-preserving, and the claim is pinned rather than asserted —
`validate_loop_safe_summary` delegates to `SafeSummary::new` with exactly
one bypass, the fixed `INPUT_ENCODE_HUMAN_SUMMARY` literal, which
independently satisfies the canonical rule
(`loop_input_encode_sentinel_needs_no_bypass_here`). Splitting writer from
reader also split the checkpoint-stage vocabulary across two crates, so the
pre-existing round-trip (which covered only `BeforeModel`) gains a sibling
driving all four `CheckpointKind`s writer→reader across the boundary.

Neither edge was ever a `LAYER_MATRIX_EXCEPTION` — `products → kernel` and
`products → loops` are matrix-legal — so this cannot move the count, which
stays at 13. The value is graph narrowing and prompt-content placement.

Enumerating gates, all shrink-only: the composition pub-use snapshot loses
exactly one line, because the `reborn_failure_summary_for_category`
re-export is deleted rather than re-sourced (its sole consumer, the CLI,
already depends on `ironclaw_host_api`, so the facade hop bought nothing).
The product-side category-coverage `include_str!` is repointed, not added,
so the §11.2.7 inventory is unchanged at 19.

Also hardens `reborn_loop_port_location_scan`'s directory walk, which
excluded only `target` and so descended the whole npm tree on any checkout
with the frontend installed — the same defect already fixed in the sibling
scanners.

Un-masking: 10 table tests moved runner → host_api with identical names and
no content edits (runner 467 → 458, host_api 248 → 260; deltas are the 10
moved plus 1 new round-trip and 2 new pins). loop_host and product rosters
are byte-identical at 572 and 1032.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(checklist): tick WS1.6, record WS1.7's partial sever, close Wave 1

Ticks the `ironclaw_common` narrowing row with the seven dispositions that
differ from how it was written (five of six clauses moved differently:
two deletions where the row said relocate, one unsatisfiable "→ its
consumer", two clauses already done, one eviction present in §6.1.5 but
missing from the row), and the three LLM-data evictions each refuted by a
pinned rule.

Leaves the WS1.7 row **open** rather than ticking it. Two of three clauses
landed — the data move and the `product → runner` sever — but the
`product → loop_host` sever did not and cannot here: the edge has three
production sites, two of them behavioral, one of which
(`attachment_landing.rs`'s `LoopAttachmentReadPort`) was not recorded
anywhere before. Ticking it would over-claim.

Records the Wave 1 exit state on the WS1 verify row. The milestone's
"20 → 12" is not met and **the 12 was wrong**: the true end-state is 13,
because the 13th survivor, `conversations → turns`, is turn *admission
authority* rather than vocabulary, so no contracts crate can dissolve it and
it falls in WS5. The wave's other clauses did land — three contracts crates,
`agent_loop` contracts-only with zero exceptions, evidence mint sealed. The
mint row's measurement is carried forward as the reusable finding: the
`host-auth-mint` cargo feature was never a seal, because feature unification
compiled the gate on for every crate in any workspace-wide build.

Adds the dated one-line Wave 1 summary to PLAN's Wave 1 section, including
the discipline every slot in this wave independently confirmed: re-measure
rows against your own base, never inherit a predecessor's count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…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>

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-6967 — 1cafd617 Deployed Jul 31, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules scope: dependencies Dependency updates scope: docs Documentation size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant