Skip to content

refactor(ws6): execute the 13 WS6 renames and close the remaining Wave 4 rows - #7152

Merged
BenKurrek merged 140 commits into
mainfrom
ws6/wave4-part2
Aug 5, 2026
Merged

BenKurrek merged 140 commits into
mainfrom
ws6/wave4-part2

Conversation

@BenKurrek

@BenKurrek BenKurrek commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator

Wave 4, part 2: every WS6 row not owned by the part-1 consolidation (#7139).

Commits are separated per item and labelled below as semantic or pure move/rename.


What lands

# Item Kind State
1 13 crate/dir renames pure rename DONE
2 traces re-export modules dropped semantic DONE
3 llm providers.json -> crate asset + boundary rule semantic DONE
4 skills stale v1 lib.rs doc doc DONE
5 conversations trusted-trigger-prompt seam (absorbs #7136) semantic DONE
6 identity absorbs host_api::user_identity — REFUTED, amended
7 traces ScopedFilesystem — OPEN, re-measured + row corrected
8 local_runtime misnomer (#7098) — OPEN, re-scoped
9 Items 2/3/4/5/6/10 of the brief — NOT DONE — see "Not delivered"

Renames — 14 of 14 clauses across three rows

Stutter kills: ironclaw_events->ironclaw_event_log · ironclaw_extensions->ironclaw_extension_registry · ironclaw_product->ironclaw_assistant
Naming audit: ironclaw_architecture->ironclaw_architecture_tests · ironclaw_runner->ironclaw_turn_runner (ironclaw_extension_support landed with WS2.6)
reborn_ batch: composition · config · event_store · identity · openai_compat · reborn_traces->trace_commons · cli dir -> crates/app/ironclaw_cli · root pkg -> ironclaw_integration_tests

4,806 occurrences across 901 files + 11 git mvs, then 82 more path refs for the cli move. No shims. No type renamed, no module moved, no signature changed. cargo check --workspace --all-targets clean.

Path-keyed gates — the WS10 hazard

Renames are where a path-keyed gate goes silently green. Each was re-run and required to report a non-zero scan:

  • no_panics_reborn_baseline.txt — 3 entries repointed, 0 stale; --reborn-baseline reports "OK (1203 files, 51 reviewed invariant(s))"; --self-test 34/34.
  • docs/plans/composition-pubuse.snapshot — a test fixture despite its path. Went stale twice; the second time because cargo fmt rewrapped a line the rename lengthened. Regenerated after fmt; diff is one alphabetical re-sort + one rewrap, no symbol added or removed.
  • ws12_workflow_contracts.py — failed loudly and correctly on the cli move: "expected exactly one crate directory named 'ironclaw_reborn_cli' under crates/, found 0 ... repoint the gate that names it rather than letting it measure an empty tree." Repointed in code_style.yml (incl. the CI lane name the row calls out), ws12's probe table, and check-generic-without-concrete.sh.
  • Also repointed: classify-test-scope.sh, reborn-crate-test-buckets.sh (+self-test), discover-reborn-package-crates.sh, package-feature-flags.sh, dev_metrics.py, reborn-e2e-rust.sh, pre-commit-safety.sh, and main's new cut_ironclaw_release.py (+self-test), which the merge reintroduced with a stale path.

LAYER_MATRIX_EXCEPTIONS counted with Python between the const and its ]; — 6, unchanged.

Semantic items

traces re-export modules (§6.4.14). recording/paths were pub use <other crate>::* passthroughs. §6.4.14's "consumers import the owners" could not be taken literally: the sole consumer is the CLI, whose tested allowlist (reborn_cli_binary_crate_stays_separate_from_v1_root) deliberately excludes ironclaw_llm. Importing the owner would trade laundering for a breached boundary. Satisfied by giving the owning crate the operation instead — onboarding::onboard_instance, TraceClientHost::build_envelope_from_recorded_trace_json, and delegating the CLI's private trace_contribution_dir() to trace_contribution_dir_for_scope(None) (verified byte-identical). No dependency added anywhere.

llm providers.json. Moved from the repo root to crates/ironclaw_llm/assets/. ⚠ The inherited claim that its load-bearing include site is in the CLI and that it "needs a new mechanism, not a new path" is refuted — it is ironclaw_llm/src/registry.rs:383, inside the owning crate. New rule reborn_provider_catalog_is_owned_by_its_crate, sabotage-tested in 5 cases (root asset restored / foreign crate embeds / catalog drifts from the CLI mirror / walker sees 0 files / mirrored const renamed) — each red with the right message, each restored green. Case 2 caught a false positive in my own first draft (a file-level include_str!+providers.json conjunction flagged smoke.rs, which names the runtime file); the matcher now inspects the macro argument.
Test accounting: CLI config-init tests 2 -> 1; the removed one is reborn in the architecture suite, strictly stronger. Net +0.

#7136 absorbed by merge (not rebase). Byte-identity verified against the branch's own merge base: 12/16 files identical, 4 differ only by my additions, 0 unexplained. Reviewed as my own: the scan is now an invariant of TrustedTriggerSubmitRequest::new (so it applies to every submitter, and new_for_test delegates to it), and its tests drive worker.tick_once() — the real caller tier — with a negative case. Sabotage-tested its boundary guard: re-adding ironclaw_safety to ironclaw_conversations fails with "must not have a normal dependency on ironclaw_safety". My first sabotage attempt used a wrong version and Cargo never resolved, so the test never ran and the "green" was meaningless — re-run correctly. Supersedes #7136; not closing it — owner's call.

Pre-existing bug fixed in passing

check-generic-without-concrete.sh listed "ironclaw_reborn_cli" among sanctioned assemblers, but that set matches cargo package names and the CLI package is ironclaw. The exemption matched nothing, so the gate was already red on clean origin/main @ 283e1f6 (reproduced on a clean checkout before assuming this PR caused it), reporting the two extension crates DEL-7 explicitly allows. Fixed by naming the package.

Not delivered — measured, not hand-waved

These WS6 rows are still open. I am not ticking them.

  1. ChannelExtensionBinding.extension_id -> typed ExtensionId + env reads behind ironclaw_config.
  2. config Google half (GoogleSection, Google update pipeline, capability_remediation.rs) + CLI Google-OAuth shed.
  3. RebornRuntime re-export wall (228 pub use lines; 17 of 39 _for_test fns still ungated). Note WS6: measurements for the next slice — RebornRuntime re-export wall (§6.10) and typed ChannelExtensionBinding.extension_id #7107's clause-3 refutation stands: product_live_adapters is live cross-crate test-support API — not deleted.
  4. Module charters — mcp single-file split (lib.rs is 2,738 lines, one file), auth two-engine split, webui handlers.rs charter map (4,443 lines). ⚠ §6.9.4 contains no charter-map clause and its arch-exempt: large_file waiver at handlers.rs:15 names live plan feat(reborn): route caller-requested model on OpenAI-compatible API (Phase 2) #5985 — that waiver must stay; this is the opposite of the contribution.rs case.
  5. Composition behaviour evictions (§6.10.1 remaining inventory), incl. the second hop for project service / project-create capability.
  6. traces ScopedFilesystem — row text corrected (§6.4.14 says adopt, not "drop"). Production surface is 11 fs calls in contribution.rs + ~7 in device_key.rs, not the "~91" a prior lane counted (that counted tests). Deliberately not attempted: every site is inside the 17,470-line contribution.rs that refactor(traces): split the 17,470-line contribution.rs into chartered modules (WS6) #7124 is concurrently splitting.
  7. local_runtime (WS6: retire the surviving local_runtime misnomer (191 occurrences, 6 public API symbols — not the 1-line residue the docs claim) #7098) — re-scoped, not closed. It is 14 identifiers, not one variable; RebornLocalRuntimeIdentity is pub(crate), not public API; and a second ratchet (reborn_deployment_mode_typename_ratchet) already inventories it and records the sanctioned exit as Slice B, not a rename. Every obvious target name (RebornRuntimeIdentity, runtime_identity, runtime_policy, runtime_root) is already a different concept, and Standalone* would violate §4.4's own rule.
  8. triggers SQL ADR-or-converge; projects absorbs its composition service adapter.

Verification

cargo check --workspace --all-targets clean · ironclaw_architecture_tests 32/32 suites green · ironclaw_composition --lib 545/546 (multi_tool_call_response_survives_surface_change_mid_register fails only under parallel load, passes isolated) · ws12 / include-str / panic-baseline / bucket + classifier self-tests / release-cut self-test all pass.

⚠ Known pre-existing on main: #7119 clippy, and test_live_canary_workflow_shards_cover_non_telegram_qa_suite.

Reconciled with main (#7143 @ acbf1d89e8) — 2026-08-04

Merged, never rebased: #7143 landed as a squash, so its tip is not an ancestor of main and a rebase replays the whole Wave 2 change as phantom conflicts.

⚠ Run this after every merge-down — a clean merge silently reintroduces old crate names

This PR renames 13 crates. Content main adds that references an old name merges with zero conflicts — the rename and the new content touch different lines — and then the build breaks, or a path-keyed gate quietly stops matching anything. "0 conflicts" is not evidence on a rename branch.

# 13 old names: ironclaw_architecture, ironclaw_events, ironclaw_extensions,
# ironclaw_product, ironclaw_reborn_{cli,composition,config,event_store,
# identity,openai_compat,traces,integration_tests}, ironclaw_runner
git grep -nIw "<old_name>" -- . ':(exclude)docs/' ':(exclude)openwiki/' ':(exclude)CHANGELOG.md'

Use git grep -w, not git grep -E '\b…\b'. git's ERE does not support \b and silently matches nothing — the first version of this scan was written that way and "passed" a sabotage test reporting 0 hits. -w is correct because git treats _ as a word character, so -w ironclaw_architecture does not match ironclaw_architecture_tests but does match crates/ironclaw_architecture/.

Two zones: code/config/CI must be 0; docs/ is baseline-pinned at 28, because the append-only ledgers legitimately record the renames themselves (`ironclaw_events`→`ironclaw_event_log`). An increase in the docs zone is a reintroduction.

Result: 3 → 0 in the code zone; docs zone 28 → 28.

Reintroduced with no conflict Fix
extension_ingress.rs, channel_pairing/tests.rs Both sides' imports survived → E0252. Dropped the ironclaw_assistant re-export import, kept the owning contract's.
reborn_dependency_boundaries.rs, ironclaw_host_ingress/CLAUDE.md main's new prose named ironclaw_product / crates/ironclaw_architecture/.
CHECKLIST.md, PROPOSAL.md main's new amendments named ironclaw_product.
.gitattributes (pre-existing miss) Its whitespace= rule pointed at crates/ironclaw_reborn_cli/wix/main.wxs, renamed away by this PR — a path-keyed rule that had gone quiet.

Conflicts (5, all content-vs-rename)

Three extension_host files: main moved RebornChannelConnectStrategy off the ironclaw_assistant re-export onto its owning contract. Took main's direction, kept our rename — including inside main's new explanatory comment, which itself named ironclaw_product.

lifecycle_restore.rs: main deleted RETIRED_SLACK_USER_EXTENSION_ID and its test. Took the deletion — reborn_retired_taxonomy.rs pins that identifier at zero. Test roster diffed before/after: exactly one test gone, retired_slack_user_id_remains_stable, the one #7143 removed.

CHECKLIST.md (append-only): verified per conflict region that our side was exactly rename(base), proving main's text loses nothing. All 18 dated 2026-08-04 amendments preserved in date order; our renames re-applied over them.

Specificity baseline recounted off the compiler

Ours was 129, main's 125, and the auto-merge silently took 125 — neither side's number is evidence for the union. Set the baseline to 0, let the ratchet report the true length, then pinned it: 125, exactly zero slack. Not counted by eye (the ratchet is <=, so a too-high number sits green while carrying untracked slack — #7147).

Tests (Reborn) planner: fixed as a class

.gitattributes aborted the planner ("unclassified pull-request path"). Every entry already in that set was added one-per-red-run — Dockerfile, then clippy.toml, then six more. Drove the planner over every tracked root file, found all 19 unclassified, and listed 17. .dockerignore and .env.example are deliberately left fail-closed — they have real test readers, and silently classifying a file a test depends on would skip that test.

A path-keyed gate that had gone quiet — and the guard so it can't recur

Fast deterministic checks went red on the test-scope classifier:

FAIL reborn binary crate
Expected: has_legacy_tests=false has_reborn_tests=true
Actual:   has_legacy_tests=true  has_reborn_tests=false

is_reborn_test_path matched the CLI through the glob crates/ironclaw_reborn_*/*. The renames dropped that prefix from all seven crates that carried it, so the glob matches nothing and every one of them silently reclassified as legacy. Enumerated the seven new names instead of re-globbing — they share no prefix.

Fixed the classifier, not the fixture. The self-test's expectations describe intended behaviour; flipping them to match the break is how a gate goes quiet.

⚠ The whole-word name scan could not have caught this — the pattern is a glob stem (ironclaw_reborn_*), not any of the 13 whole names. So the scan was widened: every crates/<name> literal and glob stem across scripts/, .github/, and the architecture tests is now checked against the real tree. The only dead reference attributable to the 13 renames is this one; the rest are synthetic self-test fixtures or crates deleted long before this branch.

This class fails OPEN — the classifier keeps answering, just wrongly — so it surfaced only because one crate happened to have a fixture. Added a guard asserting every crates/… pattern in the classifier matches at least one real path, the same shape as sanctioned_paths_all_match_real_files: an exemption may not outlive the code it exempts. Two pre-existing dead arms (crates/ironclaw_extension_support/, crates/ironclaw_oauth/) are listed known-dead and shrink-only rather than repointed — both match nothing today, so neither is load-bearing, and repointing them would change which tests those crates select.

Sabotage-tested both, red with the right message and green after restore: restoring the dead glob reproduces FAIL reborn binary crate; adding crates/ironclaw_totally_invented/* trips the new guard with classifier pattern matches no real path.

Known residue, deliberate

The docs-zone baseline of 28 is: the append-only ledgers recording the renames themselves, plus 12 stale ironclaw_runner labels in docs/reborn/subagent-spawn/diagrams/ (4 .d2 + their .svg). The sibling .md prose in that directory was repointed. The diagrams were not, deliberately: the documented workflow is "edit the .d2 and re-run d2 <name>.d2 <name>.svg", and regeneration is not reproducible in this environment — d2 0.7.1 (the exact version stamped in the committed files) reproduces neither the committed layout with the default engine nor with elk (viewBox 926×2191 committed vs 1560×2171 / 1046×2311 regenerated). Regenerating would churn every diagram with layout changes I cannot verify against the original intent, and hand-editing generated SVG violates the documented workflow. Zero functional impact, no CI gate; left for whoever owns that design doc.

The cascade, run down locally instead of one CI cycle at a time

Static-check self-tests runs fifteen scripts in one step and stops at the first failure, and the nine steps after it are then skipped. So fixing the classifier only uncovered whatever was behind it — the same one-per-red-run sequence this PR already hit on the planner. Rather than discover them serially, all nine remaining scripts in that step and all nine skipped steps after it were run locally.

Exactly one more was broken, and the rename broke it. test-check-composition-budget.sh T4 asserts the budget gate fails loudly when the composition crate is absent: it builds a fixture under the crate's real name and renames it away. The destination was hard-coded ironclaw_composition — precisely what the rename turned the crate's real name into — so both sides of the mv became the same path.

mv X X does not rename. It tries to nest a directory inside itself, dies with Invalid argument, and the negative case stops running. Destination is now composition_renamed_away, deliberately synthetic so no future crate rename can collide with it.

Sabotage-tested: skipping the rename (crate present) makes T4 fail with expected exit 1, got 0 plus the missing-message assertion — 49 passed, 2 failed; restored, 51 passed, 0 failed. The case exercises the absence again rather than passing because it never ran.

The other seventeen checks pass locally: panic self-test / reborn-baseline / changed-code, WASM freshness, composition mass budget, dev-metrics, changed-coverage manifest, release-binary smoke gate, and the ten step-11 scripts.

Also verified, no change needed

discover-reborn-package-crates.sh still carries startswith("ironclaw_reborn"), now dead. Proven inert empirically rather than by reading: the discovered package set is byte-identical with and without the clause, and all twelve renamed crates are still discovered through the binary-closure arm. No lane lost coverage, so the clause was left alone rather than churned into a file two sibling PRs also touch.

🤖 Generated with Claude Code

BenKurrek and others added 30 commits August 3, 2026 09:38
…contract (WS3)

Deletes the two `-> ironclaw_extensions` layer-matrix exceptions
(`ironclaw_mcp`, `ironclaw_scripts`) by giving the runtimes-layer lanes a
contracts home for the descriptors they read, instead of the registry crate
they may not depend on. Exceptions 13 -> 11; baseline lowered in the same
change.

Moved to `ironclaw_extension_contracts`:
- `runtime::{ExtensionRuntime, ExtensionAssetPath, ExtensionAssetPathError}`
- `hosted_mcp::{HostedMcpDiscoveredTool, HostedMcpDiscoveredToolAnnotations}`

`ExtensionPackage`/`ExtensionManifest` deliberately stay in
`ironclaw_extensions`: they carry the whole parsed manifest tree and a
`PackageRootBinding` typed on `ironclaw_filesystem::VirtualPath`, which the
§11.2.3 contracts-purity allowlist (`{ironclaw_host_api}` only) forbids the
contracts crate from naming. Measured instead: both lanes read exactly three
things off the package — `id`, `capabilities`, `manifest.runtime` — so the
lane request structs now take those three and the caller (which owns the
package) projects them.

Also repointed `ResourceReceipt` to its real owner: `ironclaw_resources`
only re-exports `ironclaw_host_api::resource::ResourceReceipt`, so the lanes'
import was a §11.2.4 two-import-paths hop, not a dependency.

No `pub use` shims (§11.3): every consumer is repointed in this change, and
`resolve_under` becomes the free function `ironclaw_extensions::resolve_asset_under`
because the orphan rule forbids an inherent impl on the moved type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Creates `ironclaw_sandbox` (runtimes) from the three halves of "run an
already-authorized command away from the host", and deletes the two crates
PROPOSAL §6.6.4 marks for merge:

- `ironclaw_process_sandbox` (plan contract)      -> `src/plan.rs`, `src/validation.rs`
- `ironclaw_host_runtime::sandbox_process`        -> `src/sandbox_process/**`
- `ironclaw_scripts` (script lane + Docker path)  -> `src/script.rs`

The kernel sheds the Docker/CA cone: `bollard`, `rcgen`, `x509-parser` and
`time` are gone from `ironclaw_host_runtime`'s manifest, and `bollard`/`rcgen`
are now declared by exactly one crate in the workspace.

Two migration details PROPOSAL §6.6.4 and CHECKLIST WS10 call load-bearing:
- `PROCESS_SANDBOX_CAPABILITY_ID` -> `ironclaw_host_api::capability`, so
  `ironclaw_loop_host` drops its lane dependency (production dep gone; a
  dev-dep remains for the tests that build plans).
- `SandboxCommandTransport` -> `ironclaw_host_api::process`, with the shapes
  it names (`CommandExecutionRequest`/`Output`, `RuntimeProcessError`,
  `SavedCommandOutput`, `SavedCommandOutputSanitization`). Without this the
  runtimes-layer lane could not implement what the kernel consumes.

Enumerating gates were repointed, never relaxed: the specificity carve-outs and
the struct/test-support ratchet entries moved with their files (both baselines
unchanged at 129 and their prior values), the panic-gate baseline row moved,
`reborn-crate-test-buckets.sh` registers the new crate, and the three
`reborn-e2e-rust.sh` script selectors follow the tests (plus `docker_security`,
which had no selector before).

One gate would have gone silently vacuous and was fixed rather than moved: the
script-lane surface scan in `reborn_dependency_boundaries.rs` read a hardcoded
`src/lib.rs`, which after the merge no longer holds the lane. It now scans the
whole crate source tree with a fatal-read walk and a non-vacuity assertion.

One deletion, recorded: `RebornScopedSandboxCommandTransport::into_process_port`
returned a kernel type a runtimes crate may not name. It had zero callers
workspace-wide; the kernel wraps the transport, which is the direction the port
inversion requires.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ence

Three dated amendments, each quoting the text it replaces:

1. CHECKLIST WS3 sandbox row + PROPOSAL §6.6.4 — "all pieces currently
   unwired/test-only" is REFUTED. Three production paths cross the merged
   crate (spawn-path plan validation, the process_executor routing check, and
   the saved-command-output scope digest). The accurate claim is narrower:
   no production *execution backend*. Behavior preservation is therefore
   argued at the diff (11 of 26 moved files byte-identical, 9 more differing
   by one import line, +63/-36 overall), not inferred from deadness.

2. CHECKLIST WS3 mcp row + PROPOSAL §6.6.3 — the prior wave's "structurally
   blocked" finding is half right, and the wrong half is load-bearing: only
   `ExtensionPackage` is un-absorbable, and no lane ever needed it (both read
   `id`, `capabilities`, `manifest.runtime` and nothing else). The registry
   half of the flip is done; the `resources` half is refuted as phrased —
   the estimate/usage vocabulary the row asks about is already in
   `host_api::resource` and already imported from there, while the real
   blocker is the `ResourceGovernor` authority port and `ResourceError`'s
   denial cone.

3. Recorded as a structural finding, not a note: the sandbox row and the mcp
   row are ONE problem. `ironclaw_scripts` imports the identical DTO set, so
   the merge alone deletes zero exceptions and only the mcp carve-out lets
   either lane shed the registry edge.

Also reconciled: PROPOSAL §6.1.2's as-built inventory gains the two modules
WS3 landed (and states why `ExtensionPackage` stayed); §2's package count
66 -> 65; the §9 disposition rows for `ironclaw_scripts`/`ironclaw_process_sandbox`/
`ironclaw_mcp`; the §11.2.2 ratchet rows (13 -> 11); the WS3 verify row; the
stale WS1.3 sentence asserting the blocker as settled fact; and
`reborn_restructure_baselines.rs`'s doc table, which still read 15.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`process_port.rs` no longer names `MountView` or `thiserror::Error` (both went
to `host_api::process` with the types that used them), and `sandbox_process.rs`
no longer needs `sync::Arc` after `into_process_port` was deleted. Found by
per-crate `clippy --all-targets --all-features -D warnings`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tions

Three fail-closed gaps in `reborn_pr_test_plan.py`, all hit by this PR and all
live on `main` today — any PR with the same change shape is unplannable.

1. `.claude/**` was unclassified, so the planner refused outright. It is agent
   guidance in exactly the sense `docs/**` is human guidance: no Rust test
   reads either as data (the only in-tree references are prose citations in
   test doc comments). Added to `IGNORED_PREFIXES`. Without this, "guidance
   travels with the change" — the restructure's own discipline — cannot be
   satisfied in a single PR.

2. `crates/AGENTS.md`, `crates/README.md`, `crates/Architecture.md` raised
   "unmapped crate path": they sit under `crates/` but belong to no package.
   Now classified as crate-tree prose, matched by "Markdown no package
   directory owns" so a genuinely unmapped crate path is unaffected.

3. An unmapped crate path used to raise. `git diff` reports a deleted crate's
   old paths and CI feeds the planner that diff, so **every crate deletion or
   rename was unplannable** — including the six deletions PROPOSAL §2 plans.
   It now widens to the exhaustive plan. This is a semantic change and it is
   the safe direction: the full plan is a superset of any narrowing, so an
   unattributable path can never cause under-selection, whereas refusing to
   plan blocks the PR instead of protecting it. Malformed input is still
   rejected by the unclassified-path branch.

Each lands with fixtures per WS10's rule, positive and negative: guidance
paths select nothing while non-guidance paths still fail closed; crate-tree
prose selects nothing while crate *code* under the same unmapped directory
widens to `full` (so the Markdown carve-out cannot swallow code). The
pre-existing `test_unmapped_crate_path_fails_fast` is renamed and rewritten to
pin the new contract rather than deleted.

Verified against this PR's real 130-path diff: the planner returns `mode:
full`, and the workflow's own exhaustiveness guard passes on that output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… a wave

Review (#7065) caught that both surviving `-> ironclaw_resources` exceptions
declared `removes_in = "WS3"` — the wave this PR *is*, which does not remove
them. That is precisely the defect §11.2.2 already records against
`conversations -> turns` ("`removes_in = "WS5"` and WS5 has partly shipped
without it falling"), and it would have been repeated here.

Both now point at issue #7067, which owns the design work that actually clears
them: replacing the `ResourceGovernor` dependency with a narrow
reserve/reconcile/release port. The issue carries the measurements — 3 of 10
methods used, zero implementors, and the `ResourceError` denial cone — plus the
two open questions (error shape, port home) that make it a design slice rather
than a move.

An owning issue is also what §11.2.2 asks for and what the ratchet still cannot
enforce (there is no `owning_issue` field yet), so this is the strongest form
currently expressible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…on_contracts

`validate_asset_path` moved here with `ExtensionAssetPath`, the type it
constructs. In `ironclaw_extensions` it was only ever reached indirectly
through manifest parsing, so its six rejection branches had no direct test —
and a contracts crate that carries validation owes that validation one.

Two tests: every reject branch with its exact reason and `Display` output
(empty, NUL/control, URL, absolute, Windows drive and backslash, and the
empty/`.`/`..` segment cases) plus the manifest-relative shapes that must keep
being accepted; and `ExtensionRuntime::kind()` over all five variants, since
that projection is what every lane uses to reject a runtime it does not serve.

Also removes a changed-line coverage risk this PR would otherwise carry into
the merge queue: the gate does not run on ordinary PRs (#7036), so ~100
newly-added lines of validator would first be measured where a failure is
expensive to diagnose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…andbox lane

`RATCHET FAIL: ironclaw_host_runtime` — observed 18854 covered vs a
`floor_covered_lines` of 20538. This is the shrinkage case the ratchet's own
"To fix" text describes, not a coverage regression: `sandbox_process/**` moved
to `ironclaw_sandbox`, so the crate's denominator fell 23277 -> 21267 (-2010
instrumented lines) and its covered lines fell with it.

The percentage floor is **raised, not lowered**: observed 88.65% against an old
floor of 88.23%, so the entry now reads 88.65. Only the absolute line count
moves down, and it must — those lines are no longer in this crate.

To keep that from being a net loss of protection, `ironclaw_sandbox` is floored
on arrival at its observed 87.09% (3185 / 3657). This is a net *increase* in
ratchet coverage: neither `ironclaw_scripts` nor `ironclaw_process_sandbox` was
ever floored, and the `sandbox_process` half was protected only as part of
host_runtime's line count, which this PR necessarily reduces. Floored crates
16 -> 17.

Verified by replaying the ratchet arithmetic against CI's observed numbers:
both crates pass on percentage and on covered lines. Numbers taken from the
failing run's own report (job 91740733521), which is the authority for this
gate.

The `Tests (Reborn)` roll-up failed solely on this sub-job
("coverage-report result 'failure' did not match planned=true"); no other lane
failed — 50 pass, 2 fail, both this root cause and its roll-up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…itive gate

WS3 hit a gate no move row had named. `tests/integration/coverage-floor.toml`
is keyed on crate identity plus absolute covered-line counts, so it is
invisible to WS10's path-keyed gate audit and yet it fails on every crate move,
merge, rename, or family `git mv` that shifts instrumented lines between
crates — as it did here, while the percentage floor was *improving*.

Recorded on WS10 with the three rules WS7 will need: re-capture in the same PR,
raise the percentage floor rather than leaving it, and floor the destination
crate or the move silently drops that code out of the ratchet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Brings in #6780 (ironhub deep-link register/install + the REBORN_COV_COLLECT
hermetic-env allowlist fix), #7050, and #7033.

Two conflicts, both resolved as a union with each side's contribution verified
present afterwards:

- `crates/ironclaw_extension_host/src/available_extension_import.rs` — main
  added `use ironclaw_extension_contracts::recipe::VendorAuthRecipe;` at the
  same import position this branch added
  `use ironclaw_extension_contracts::runtime::ExtensionAssetPath;`. Both kept.

- `scripts/ci/test_reborn_pr_test_plan.py` — main added
  `test_selected_integration_lane_keeps_msrv_override` and kept
  `test_unmapped_crate_path_fails_fast`; this branch had replaced the latter
  with `test_unmapped_crate_path_widens_instead_of_refusing`. Resolution keeps
  main's new test verbatim and this branch's rewrite, and drops the superseded
  original — it asserts the exact behavior this branch deliberately changed
  (an unmapped crate path now widens instead of refusing, so crate deletions
  are plannable). Keeping both would have been contradictory, not a union.
  37 tests pass (36 here + main's 1).

Post-merge verification of both sides: exceptions 11 with baseline 11 and
`ironclaw_scripts` absent from the matrix (this branch); 9 #7033 decision
markers and the `REBORN_COV_COLLECT` allowlist entry present (main).

Note on the coverage floors this branch re-captured: #6780's fix stops the
hermetic env filter stripping `REBORN_COV_COLLECT`, which gates *whether* a
lane collects coverage. This branch's numbers were captured on a `full` plan,
where every lane collects either way, so they are expected to hold — CI
re-measures and will say so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…Path

A semantic conflict the merge could not see: #6780 landed
`ironhub/{package,catalog}.rs` importing `ExtensionAssetPath` from
`ironclaw_extensions`, while this branch moved that type to
`ironclaw_extension_contracts::runtime`. Different files, so git auto-merged
cleanly and the breakage surfaced only at `cargo check`.

Repointed both sites to the contracts crate (no shim, per §11.3). The manifest
already named `ironclaw_extension_contracts`, so this is imports only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…gate

The changed-lines coverage gate went red on four files while changed-line
coverage was 95.35% against a 90% floor: the failure was its two fail-closed
STRUCTURAL assertions, not any percentage.

Every line below was derived by replaying scripts/ci/reborn_changed_coverage.py
against this PR's own merged lcov (run 30831658659) with the base lcov the gate
itself resolved (run 30828540055 @ b89fcd3), until the replay reproduced the
CI verdict byte-identically. Line numbers come from the gate's own
`candidate_lines - mechanically_uninstrumentable_lines()`, not from the log.

- host_api/src/process.rs (31 lines): new placement-neutral process vocabulary
  with no function body anywhere in the file; rustc emits no LCOV record for it
  at all. Same shape already exempted for product_contracts/loop_contracts.
- extension_contracts/src/hosted_mcp.rs (12): field declarations of the two new
  tools/list descriptor structs. The file is plainly instrumented (191 DA, 164
  hit), so this is a no-region artifact, not an instrumentation gap.
- host_runtime/src/services/runtime_adapters.rs (13): continuation lines of
  three rewritten calls, all PROVEN EXECUTING by their region-start heads
  (lines 380/434/977 score 24/16/63 hits). The four genuinely-uncovered lines
  in the same rewrite are deliberately NOT exempted -- the gate already
  subtracts them as pre-existing debt inherited from base.
- composition capability_host_tests/approval_gates.rs (6): type positions in a
  test double whose body region scores 1 hit.

The last one is a finding, not just a waiver: that file is 100% test code
behind `#[cfg(test)] mod capability_host_tests;`, but the gate's
test_only_path() recognises /tests/, /test_support/, */tests.rs and *_tests.rs
and NOT a cfg(test) module DIRECTORY, so it measures it as production. It is
the only such directory in crates/ today.

Docs (target-architecture, same PR per the docs-truth rule):
- CHECKLIST WS10 gains the changed-lines gate beside the ratchet row, cross-
  referencing the WS2.1 note rather than restating it: percentages are not what
  fail a move; derive lines by byte-identical replay (--fetch-base-coverage
  silently degrades without --github-repo); and a stranded exemption path is an
  ABORT with no verdict, not a loud failure.
- CHECKLIST WS10 exception-ratchet row: the constant was cited at :4063 and
  sits at :4164 -- corrected by removing the line pin, since the file is edited
  every wave. Records that the baseline is a UNION across parallel WS3 lanes.
- families/contracts.md: records extension_contracts' new ownership of the
  runtime descriptor vocabulary -- the carve-out that let BOTH lanes drop the
  registry edge -- and the orphan-rule seam that keeps resolve_asset_under in
  the registry crate.
- families/lanes.md: two "Never" claims were reading as satisfied when they are
  not. ironclaw_mcp's "never depends on the resource-governor crate directly"
  is refuted (the compiled edge survives; #7067 tracks the narrow port), and
  ironclaw_sandbox's "no direct process spawning outside the transport seam" is
  aspirational -- script.rs:454 still builds Command::new("docker").

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion cost

Two review findings verified against the tree; three refuted with evidence in
the PR threads.

Valid — the sandbox wiring inventory was self-contradictory. `CLAUDE.md` said
"Two production call paths ... and both are plan validation" directly above a
list of THREE bullets, and `lib.rs` omitted the third entirely. The third is
real and is not validation: `host_runtime/src/process_output.rs:482` derives the
scoped saved-output directory through `RebornSandboxScopeKey::from_scope`. That
inventory is what tells a future agent which paths are live, so an undercount
invites deleting a production path as dead code. Both surfaces now say three and
no longer claim they are all plan validation (the `loop_host` capability-id
comparison never was either).

Valid, and recorded rather than redesigned — the registry carve-out cost a
type-level invariant. Replacing `package: &ExtensionPackage` with independent
`extension` / `capabilities` / `runtime` borrows is what deleted the
`mcp -> extensions` and `scripts -> extensions` exceptions, but it also means
the type no longer guarantees the three came from one package.
`execute_extension_json` re-checks the descriptor half
(`descriptor.provider == extension`); the runtime half cannot be re-derived,
because nothing in an `&ExtensionRuntime` names its owning extension. No caller
can trip it today -- there is exactly one production caller
(`runtime_adapters`) and it projects all three from one package in one
expression -- so this is a latent structural weakening, not a live defect.
Restoring the compile-time binding needs a sealed projection minted by the
package owner; a check inside the lane cannot express it, and re-taking the
registry edge would undo the carve-out. Both request types now carry the caller
obligation in their field docs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pport (WS3)

WS3's first-party-tools row, family 1 of 6: skill management / URL install.

`skill_url_install.rs` and its `bundle`/`github`/`zip_bundle` submodules,
plus the install-input normalizer, move out of
`ironclaw_host_runtime::first_party_tools` into
`ironclaw_extension_support::skills::{url_install, resolve_install_input}`,
where the skill executor half already lived. Move-only: no behavior change,
no test edited for content.

`ironclaw_host_runtime -> ironclaw_skills` is deleted from
LAYER_MATRIX_EXCEPTIONS — the edge is gone, not waived (exceptions 13 -> 12,
WS0_LAYER_MATRIX_EXCEPTION_BASELINE drops with it). `ironclaw_skills` and
`zip` survive as dev-dependencies for host_runtime's own tests; dev edges are
outside the matrix by construction.

Two doc ambiguities are resolved in the same diff, as dated PROPOSAL
amendments quoting the text they replace:

- §6.8.4's "the builtin first-party tool handlers absorbed from
  host_runtime/first_party_tools" contradicted §8.2's "kernel: ✗ (ports only)"
  row and the enforced BoundaryRule. Resolution: the seam splits executor from
  adapter — the executor moves behind a neutral request/error pair, the
  FirstPartyCapabilityHandler / CapabilityManifest / registry wiring stay
  host-side. Same shape the groupware and web-access tools already ship.
- §8.2's "ports only" cell now says what it means: contracts-layer ports the
  kernel also consumes, not permission to name a kernel trait.

Two cost corrections recorded for the remaining families:
`host_runtime -> extension_support` is not divisible family-by-family (mod.rs
holds it via `extension_support::coding`), and
`host_runtime -> ironclaw_extensions` is not reachable by this row at all.

PATH_TERM_COLLISIONS shrinks by two: the installer's github carve-outs now sit
inside a scan-exempt crate.

Test accounting (un-masking discipline), unfiltered `--list` over both crates:
1398 -> 1398, with exactly two tests renamed by module path and none lost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nothing

Review asked why the migrated docker_security test can pass with no daemon.
The skip is pre-existing (the file differs from its pre-merge original by one
import line); WS3 only enrolled it in the required Rust e2e lane, where it was
not run at all before.

The real defect the question surfaced is worse and also pre-existing: this
crate's tests/support/docker_gate.rs states that IRONCLAW_REQUIRE_DOCKER_TESTS=1
makes a missing daemon a hard failure and that "CI sets this" -- and nothing
sets it. Repo-wide the name occurs only in docker_gate.rs and
attribution_tests.rs, here and on main. So every real-Docker test in the crate
skips-and-passes everywhere, which is exactly the gap the gate's own comment
says let sandbox security bugs ship unnoticed. docker_security.rs additionally
open-codes its own check rather than using the gate, so it would stay fail-open
even once something did set the variable.

Recorded rather than fixed: setting the variable is a CI-behavior change that
would hard-fail any lane without a daemon or the ironclaw-worker image, which
is not verifiable from inside a move PR whose evidence claim is behavior
preservation. Filed as the #6945 guardrail-claim-vs-reality class with the
two-part fix stated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The crate's CLAUDE.md said "first-party runtime tools belong under
`first_party_tools/`" without saying that only the host half does. WS3 moves
each tool's executor into `ironclaw_extension_support`, which may not name this
crate, so the rule now names both halves and points at the skill-install family
as the worked example.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The moved executor returns `SkillManagementCapabilityError`, and routing it
through `skill_management_error` would have added a `debug!` line to a path
that had none before the move. A move-only change must not add one, so the
install-input arm maps the kind directly and the `dispatch` arm keeps the
record it already had.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…move

The ratchet does not run on `pull_request` (`reborn_pr_test_plan.py:21`; issue
#7036), so this PR's green checks were not evidence on this axis. A full-plan
`workflow_dispatch` run on this exact head reported:

  RATCHET FAIL: ironclaw_host_runtime
    observed: 88.59% (20485 / 23124 lines)
    floor:    88.23% ... floor_covered_lines: 20538 (effective floor 20518)

The percentage went UP while `floor_covered_lines` went DOWN — shedding
well-covered code lowers the absolute numerator, which is a separate assertion
from the percentage one. Re-captured to the observed numbers (floor raised
88.23 -> 88.59, not merely held). Verified locally against that run's own merged
lcov artifact: ENFORCING mode, 17 PASS / 0 FAIL, exit 0.

  run: https://github.com/nearai/ironclaw/actions/runs/30858257594
  head: e07b3b0

The destination crate is deliberately not floored, because it cannot be: every
crate under `crates/extensions/` is invisible to the coverage tooling —
`reborn_coverage_lcov.py:19`'s CRATE_RE still requires a crate directory
directly under `crates/`, which #7037's colocation broke. Filed as #7083 with
the measurement; the global floor is left alone rather than re-captured onto
that hole.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CHECKLIST WS4 + WS10 `wit/` rows. `wit/{tool,channel}.wit` moves from the
repo root to `crates/ironclaw_wasm/wit/` — the crate that owns the ABI —
per PROPOSAL §6.6.1. Behavior-free: same bytes, same generated bindings.

Wave-3 coordinates: the docs write the destination as
`crates/lanes/ironclaw_wasm/wit/`, but `crates/lanes/` does not exist until
WS7. Because the files now sit *inside* the crate, the WS7 family move
carries them with no further path edit anywhere — which is the whole point
of putting them there.

Ten wit-bindgen `path:` args repointed (the host plus nine guests: six under
`crates/extensions/packages/*/wasm-src/`, three under `test-tools/*/wasm-src/`
— the CHECKLIST row said six). All nine guests verified building against the
moved WIT on wasm32-wasip2.

The four `include_str!` readers of the ABI text do NOT get repointed
literals. Doing that would turn the two `ironclaw_host_runtime` sites from
repo-root reach-ins into *cross-crate* ones — §11.2.7's strict class, the
one WS2 turns into hard failures — taking the scan from 19 to 21 while
ticking a box that says "§11.2.7 scan passes". Instead the ABI text gets one
owner, `ironclaw_wasm::TOOL_WIT` (`src/config.rs`, beside `WIT_TOOL_VERSION`),
and all four sites read the const over cargo edges that already exist.
Measured with the scan: 133 -> 129 escaping sites, cross-crate 19 -> 19,
zero `wit/` entries remaining.

Path-keyed gates repointed: `scripts/check-version-bumps.sh` (both ABI
paths), `.githooks/pre-commit`, and `platform-and-compat.yml`'s
`has_direct_wasm_abi_risk` filter — where the bare `wit/` alternative is
*deleted* rather than rewritten, because the filter's existing
`crates/([^/]+/)*ironclaw_wasm/` alternative already matches both the
Wave-3 and the WS7 location. `scripts/ci/ws12_workflow_contracts.py`
anchored on that deleted string, so its anchor moves to
`build-wasm-extensions` and its in-scope probe now pins both locations.

`Dockerfile` loses two `COPY wit/ wit/` lines in the planner and builder
stages: both already run `COPY crates/ crates/`, so the files arrive with
the crate and the old line would COPY a path that no longer exists.

Docs: the WS4 row's `crates/lanes/wit/` destination was the only doc site
placing the directory beside the crate rather than inside it; corrected
there and in README's tree, with dated amendments in CHECKLIST, PROPOSAL
§6.6.1 and PLAN Wave 3 recording what the move found.

Test accounting (unfiltered `--list`, name-by-name, quiescent tree):
ironclaw_wasm 51 -> 51, ironclaw_host_runtime 1246 -> 1246,
ironclaw_architecture 198 -> 198. Zero diff, no test edited for content.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Forced by the previous commit, not incidental to it.
`scripts/ci/check-wasm-artifact-freshness.py` keys each package's committed
`wasm/<name>.wasm` to a digest of the `wasm-src/` tree that produced it, so
editing a guest's `wit_bindgen::generate!` `path:` — which the `wit/` move
requires in all six shipped guests — invalidates the recorded digest and
fails the gate.

The gate's own contract forbids the shortcut: "Re-record only after
`./scripts/build-wasm-extensions.sh --first-party` and committing the rebuilt
artifact — the digest asserts a claim about the artifact, and updating it
without rebuilding launders a stale one." So the artifacts are genuinely
rebuilt (`--first-party`, exit 0, 6 OK / 2 host-native SKIP), not re-recorded
in place.

Byte sizes move by more than the source change accounts for because these
builds are not reproducible by design — the guests pin no toolchain and
resolve their own `Cargo.lock` at build time, which is the documented reason
the gate hashes sources rather than artifact bytes.

Verified: `check-wasm-artifact-freshness.py` OK (6 packages), and
`cargo test -p ironclaw_extension_support` green (102/46/4) — that crate
`include_bytes!`s these artifacts, so it exercises the rebuilt components.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… edits

The `wit/` move had to rebuild six shipped WASM binaries because
`check-wasm-artifact-freshness.py` digests each guest's whole `wasm-src/`
tree. WS7 hits the same wall from the other direction: the six package
guests reach the ABI across two trees, so moving either `ironclaw_wasm` or
`extensions/packages` rewrites all six `path:` literals and forces the same
rebuild. Recorded on CHECKLIST WS10's `wit/` row (point 6), on the
loud-path-pattern row that owns the WS7 repoint (also corrected six -> nine
guests there), and on PLAN's Wave 5 block with the cheap mitigation: move
the two crates in one PR and pay it once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts:
#	docs/reborn/target-architecture/CHECKLIST.md
Reconciles this lane with #7064, which landed the parallel WS3/WS4 runner
sheds and edited the same coordination files.

Five conflicts, all in shared coordination surfaces — no code move in this
PR was altered (all 117 PR-only files are byte-identical to the pre-merge
tip; the 5 apparent diffs are deletions absent on both sides).

- `reborn_dependency_boundaries.rs`: the array auto-merged to the union of
  both sides' removals; only `WS0_LAYER_MATRIX_EXCEPTION_BASELINE`
  conflicted. Recomputed as `len()` of the merged list — 13 base, minus
  #7064's three (`hooks -> wasm_limiter`, `runner -> agent_loop`,
  `runner -> loop_host`) and this PR's three (`mcp -> extensions`,
  `scripts -> extensions`, `scripts -> resources`), plus this PR's
  justified `ironclaw_sandbox -> ironclaw_resources` = **8**. Counted by
  parsing only the entries between the const and its closing `];`, so the
  struct definition and the four test fixtures are excluded.
- `loop_host/Cargo.toml`: both sides added a `[dev-dependencies]` line;
  kept both (`http` from main, `ironclaw_sandbox` from this PR).
- `CHECKLIST.md`: kept both dated amendments in date order — #7064's
  `13 -> 10` and this PR's, with its count corrected from the
  authored-in-isolation `11` to the merged `8` exactly as the §11.2.2 row
  instructs. Also kept this PR's two new coverage-gate rows beside main's
  amended loud-inventory row.
- `reborn_pr_test_plan.py`: both sides made the same `.claude/` fix; took
  main's landed wording (`startswith` makes tuple order irrelevant).
- `test_reborn_pr_test_plan.py`: union of both sides' new tests, no name
  collisions — 46 tests pass.

Also repointed the one PR-authored `changed-coverage-exemptions.toml`
entry the merge shifted: `hosted_mcp.rs` moved +1 because main added a
doc-comment line, so its line-keyed exemption now resolves to
byte-identical source lines. The other stale entries in that manifest are
inherited and already stale on main; left untouched.

Verified: architecture suite 206 passed / 0 failed, `cargo check
--all-targets` clean, `cargo fmt` a no-op, zero conflict markers, and both
sides' `coverage-floor.toml` recaptures intact (runner 82.53 from #7064;
host_runtime 88.65 and the new ironclaw_sandbox 87.09 from this PR).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reconciles this lane with #7064, which landed the parallel WS3/WS4 runner
sheds and edited the same coordination files.

One conflict: `WS0_LAYER_MATRIX_EXCEPTION_BASELINE` in
`reborn_dependency_boundaries.rs`. The array itself auto-merged to the
union of both sides' removals — #7064's three (`hooks -> wasm_limiter`,
`runner -> agent_loop`, `runner -> loop_host`) and this PR's one
(`host_runtime -> skills`). Recomputed the constant as `len()` of the
merged list: main is at 10, so this slice takes it to **9**. Counted by
parsing only the entries between the const and its closing `];`, which
excludes the struct definition and the four test fixtures.

This slice was authored off 13 and computed `13 -> 12` in isolation; the
ratchet narrative and the two doc rows that quoted that figure (PLAN's
"First-party tools" bullet and CHECKLIST's W7-progress row) now read
`10 -> 9` and record why, per the union rule on the CHECKLIST §11.2.2 row.
The edge deleted is unchanged; only the total moved.

Everything else auto-merged and was verified rather than assumed: both
sides' `coverage-floor.toml` recaptures are intact (runner 82.53 and the
new `ironclaw_loop_host` 90.89 from #7064; `host_runtime` 88.59 from this
PR), and both sides' dated amendments survive in CHECKLIST, PROPOSAL and
PLAN. All 14 PR-only files are byte-identical to the pre-merge tip, so no
code move was altered.

Verified: architecture suite 206 passed / 0 failed, `cargo check
--all-targets` clean, `cargo fmt` a no-op, zero conflict markers, and the
changed-coverage manifest validates with no exemption stranded or shifted
by this merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`Detect Reborn test scope` exits 1 on any pull request whose diff holds a
path `reborn_pr_test_plan.py` has no rule for, which made this PR
unmergeable: it must edit `Dockerfile` (the moved directory's
`COPY wit/ wit/` no longer resolves) and `scripts/check-version-bumps.sh`
(the ABI gate would otherwise grep dead paths and silently stop
enforcing). 18 of its 46 paths were unclassified.

Same class as the `.claude/` gap #7064 fixed, and classified the same
way — one rule per class, recorded beside the constant:

  * `Dockerfile` / `.dockerignore` — `platform-and-compat.yml` keys
    `has_docker_risk` off exactly this pair and owns the image build.
  * `.githooks/**` — Code Style triggers on the tree and lints its
    contents (`test-ci-comm-locale-pin.sh`); no Reborn lane runs a hook.
  * `scripts/{build-wasm-extensions,check-version-bumps}.sh` —
    `platform-and-compat.yml`'s `has_direct_wasm_abi_risk` classifier
    both scopes and runs them.
  * markdown owned by no crate (`crates/AGENTS.md`,
    `test-tools/README.md`) — prose, like `docs/` and `.claude/`. A
    crate-resident doc still selects its own crate's lane.

The first-party extension package assets are deliberately NOT ignored.
`crates/extensions/packages/*/wasm/*.wasm` is a shipped artifact that
`ironclaw_extension_support` embeds with `include_bytes!`, and
`test-tools/*/manifest.toml` is `include_str!`d by
`ironclaw_extension_host`. Calling either prose would convert today's
loud failure into a silent under-schedule of a change to production
output — the WS10 failure mode. `EMBEDDED_ASSET_OWNERS` routes each tree
to the crate that compiles it instead, so this PR now additionally
schedules `ironclaw_extension_{support,host,manager}`: the crates that
consume the six rebuilt WASM artifacts.

Also fixes #7085 in a file this PR already touches. The WIT version
extractors used the GNU-only BRE `\+`, so on BSD sed (macOS) they matched
nothing, and because the `WIT_TOOL_VERSION` cross-check is guarded on a
non-empty version the hook printed "All version checks passed" having
compared nothing. `[[:space:]][[:space:]]*` is identical under GNU sed,
so the enforced Linux CI lane is unchanged; verified on BSD sed that both
`wit/tool.wit` (0.3.0) and `wit/channel.wit` (0.3.1) now extract.

Regression tests: every classified class gets a case in
`test_reborn_pr_test_plan.py`, including the paired assertion that the
embedded assets *select a lane* rather than merely being accepted (the
inverse of the `.claude/` prose test), and a staleness pin that fails if
an asset tree or its owning crate moves. All ten new cases fail against
the planner on `main`. `test_unclassified_build_input_fails_fast` moves
off `Dockerfile` onto a still-undecided input so the fail-closed arm
stays exercised.

Refs #7087, #7085

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ners (WS3)

`crates/ironclaw_host_runtime/src/obligations.rs` was 3,122 lines fusing the
three owners PROPOSAL §6.5.9 charters separately, held apart only by an
`// arch-exempt: large_file` waiver. It is now one module per owner:

- `obligations::handler` — which obligations apply and what each does
  before/after dispatch, plus the audit/redaction/ceiling/mount validation.
- `obligations::staged_handoffs` — material staged for a later consumer:
  the runtime-secret and network-policy stores and the credential-account
  resolver port.
- `obligations::process_store` — post-start handoff discard and reservation
  reconciliation.
- `obligations::mod` — only `BuiltinObligationServices`, the assembly seam,
  and deliberately the one place naming all three at once.

Every module is under the 1,500-line gate, so the waiver is deleted rather
than carried forward: re-fusing the owners now trips `pre-commit-safety.sh`.
`mod obligations;` stays private and the crate's `pub use obligations::{…}`
names are unchanged, so no consumer outside the crate sees this.

Behavior-free. Cross-owner access is `pub(super)` (three methods), not
`pub(crate)`. The split revealed one narrowing in the other direction:
`secret_present` was `pub(crate)` with no caller outside its own file and is
now private.

Also from the same CHECKLIST row, the bounded half of "shrink
`services/builder.rs` toward composition-facing factories": three builder
methods whose only callers are inside the crate's `src` narrow to
`pub(crate)`. The rest of that clause is measured and deferred in the
CHECKLIST amendment — 17 methods need a `test-support` cargo feature, three
are callerless and belong to WS8, and the remaining 33 are a redesign of the
fluent surface rather than a shrink of it. `+production_wiring` is refuted
there: it is readiness diagnostics, not assembly.

Two loud path-keyed gates fired and were repointed, not relaxed:
`reborn_host_runtime_services_do_not_expose_lower_substrate_handles` now
scans the whole `obligations/` directory and asserts it read ≥ 4 files
(`collect_runtime_rs` returns a count; both its callers now assert non-zero),
and `reborn_struct_test_support_ratchet`'s frozen per-file count moves to
`staged_handoffs.rs` with its count unchanged at 1.

Test accounting (un-masking discipline): `cargo test -p ironclaw_host_runtime
--all-targets -- --list` is 1,246 before and 1,246 after, name-by-name
identical — zero added, removed or renamed. `LAYER_MATRIX_EXCEPTIONS` is 10
before and after; an intra-crate split cannot move the register.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…t_contracts port (WS3)

`ironclaw_operator` is a products-tier crate and held `ironclaw_secrets`, the
substrate that owns CAS one-shot leases, AAD/crypto and the OS keychain master
key. PROPOSAL §8.2's product row says the products tier loses that edge, and
§12.1b requires the port replacement to land before the edge is removed. Both
happen here, in that order.

- Port: `ironclaw_product_contracts::operator_secrets::OperatorSecretValueStore`.
- Implementor: `ironclaw_reborn_composition::RuntimeOperatorSecretValueStore`,
  the same placement as `OperatorStatusService` — assembly is the only layer
  that may name both a products-tier port and a substrate. Registered in
  `INVERTED_PORTS` beside it.
- `ironclaw_secrets` is gone from the operator manifest under every dependency
  kind, and `"ironclaw_secrets"` is now in the crate's `boundary_rules()`
  forbidden list. That gate's comment previously said the entry was
  deliberately absent because "the row owns it"; the row now owns it.

The port is deliberately narrower than the substrate, so this is a tightening
rather than a relocation: it takes no `ResourceScope` (the implementor fixes
the operator scope, where the caller used to pass one), exposes no
lease/consume protocol, and carries only a `&'static str` classification
instead of the substrate's error `Display` — asserted, including that the
backend message and the handle name are both absent from what crosses.

Two tests travelled with the behavior rather than being pointed at a fake:
`read_is_repeatable_across_reloads` (repeatability is a property of the lease
protocol) and the #4673 production-store reproduction (its value is wiring the
store exactly as production does, which now means the real store *behind the
adapter*). Two `FaultInjecting`-over-real-store fixtures became per-operation
port fakes, with the substrate error mapping re-pinned at the adapter; a third
assertion got stronger — batched-vs-N+1 stored-key lookup is now observed at
the port rather than by counting filesystem ops.

Test accounting: operator 154 -> 153, product_contracts 142 -> 143,
composition 937 -> 942 with zero removed; name-by-name diffs on a quiescent
tree.

Two findings the row could not have anticipated, both recorded in the
CHECKLIST amendment:

- The `webui` half of the row was already closed and was never a production
  edge. `ironclaw_secrets` has been a dev-dependency of `ironclaw_webui` since
  the commit that added it (#6619), both src mentions are `#[cfg(test)]`, and
  webui's boundary rule already forbade it.
- `ironclaw_extension_manager` (layer `products`) still holds a normal
  `ironclaw_secrets` edge in `admin_configuration.rs`. §8.2 covers it; the row
  does not, because the crate landed with WS2.4 after the row was written, and
  the substrate sits in the service's type parameters so it is not a
  like-for-like swap. Filed as #7095.

`LAYER_MATRIX_EXCEPTIONS` is 10 before and after: `products -> substrates` is
matrix-legal, so this edge was always an §8.2 rule and never a layer exception.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review asked why the required Rust e2e lane can report `docker_security` as
passing with no daemon. Half of that is #7081 (nothing sets
IRONCLAW_REQUIRE_DOCKER_TESTS=1, so the switch is inert) and is not fixable
from here -- arming it hard-fails any lane lacking a daemon or the worker
image, which needs a runner guaranteed to have both.

The other half is fixable here and is fixed: docker_security.rs open-coded its
own `docker version` / `image inspect` checks with three bare `return`s, so it
sat entirely outside docker_gate and would have stayed fail-open even once
something did set the variable. It now takes both preconditions from
docker_gate::{docker_available, docker_image_available} and skips with the
visible `SKIP:` line that gate's module doc requires.

Measured, same machine, image absent:

  before, IRONCLAW_REQUIRE_DOCKER_TESTS=1 -> "skipping ..." / 1 passed
  after,  IRONCLAW_REQUIRE_DOCKER_TESTS=1 -> panic at docker_gate.rs:74 / FAILED
  after,  variable unset                  -> "SKIP: ..." / 1 passed

The third line is the no-op proof: the variable is set nowhere in this tree or
on main, so no lane's behavior changes today. The daemon-down path already
reached the image check and skipped there, so the outcome is identical; only
the branch it takes differs.

Two stale comments in docker_gate.rs corrected with it (they claimed
docker_security used its own gate, and that docker_image_available had no
consumer), and the crate's Known debt entry now splits the done half from the
#7081 half instead of describing both as open.

cargo test -p ironclaw_sandbox: 193 passed, 0 failed
cargo clippy -p ironclaw_sandbox --tests --all-features -- -D warnings: exit 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two review findings, both correct, both artifacts of this PR's own renames.

1. engine-v2-to-reborn-parity.md note 4 read "a native script/software
   execution lane (`ironclaw_sandbox`, `RuntimeKind::Script`) sandboxed via
   `ironclaw_sandbox`" -- self-referential after the merge collapsed
   ironclaw_scripts and ironclaw_process_sandbox into one crate, and it
   contradicts note 5 four paragraphs down ("no production execution backend
   is wired for it"). Re-stated as the typed runtime contract it is, citing
   the measurement: `with_script_runtime` has zero production callers
   (`rg` finds only the builder itself, docs, and 30 test call sites).

2. CHECKLIST WS10 ratchet note 2 said "raise the percentage floor ...; only
   the line count should fall". That generalises WS3's sandbox merge, where
   observed coverage happened to rise. It is wrong as guidance for WS7, and
   the counterexample is in this same file: the 2026-08-03 entry from #7064
   records ironclaw_runner falling 85.55% -> 82.53% because the shed removed
   the crate's better-covered half, holding the floor, and RATCHET FAILing in
   the merge queue. Note 2 now says re-capture from the merged artifact, and
   lower only with that entry's move-not-regression counterfactual (add the
   moved files back, confirm the union clears the old floor, plus a zero-tests-
   lost name set-diff).

cargo test -p ironclaw_architecture: 32 targets, 206 passed, 0 failed

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three review findings on the `wit/` move, each verified before it was acted on.

1. `ws12_workflow_contracts.py` probed `crates/ironclaw_wasm/wit/host.wit` and
   its nested twin. No `host.wit` exists in this repository — `git ls-files
   '*.wit'` returns only `tool.wit` and `channel.wit` — so both probes sat
   under the `crates/([^/]+/)*ironclaw_wasm/` alternative and re-asserted the
   crate-name term while saying nothing about the canonical ABI contracts. In
   a validator whose stated design is "probe derived from reality rather than
   from a guessed layout", a fabricated filename is a defect on its own terms.
   Replaced with a `crate_globs` entry, `("ironclaw_wasm", "wit/*.wit")`, which
   discovers the contracts on disk, requires each in scope, and synthesises the
   nested WS7 form — so a third contract, or the directory leaving the crate,
   fails the pin instead of passing on a stale name. Verified non-vacuous:
   narrowing the workflow alternative to `.../ironclaw_wasm/src/` now reports
   `tool.wit`, `channel.wit` and the nested probe as out of scope.

2. The embedded-asset routing test substituted `alpha`/`beta` owners so it
   could reuse the synthetic workspace. That exercised the real prefix strings
   through the real routing, but left the prefix->owner *pairing* — the table's
   entire semantic content — asserted nowhere: swapping
   `ironclaw_extension_support` and `ironclaw_extension_host` passed. Fixed in
   two halves. The routing test now drives the real `EMBEDDED_ASSET_OWNERS`
   against a workspace carrying the real owners' names and real manifest paths
   (the synthetic one could not: `build_plan` rejects a changed package outside
   the canonical set), asserting the real owner is selected. And the not-stale
   test now derives the same pairing from the tree instead of restating the
   constant: it resolves every literal `include_str!`/`include_bytes!` in every
   workspace crate through `crate_tree`, keeps the targets no crate owns — the
   ones that actually reach the table — and asserts that every crate compiling
   one of them is the routed owner or a dependent of it.

   That surfaced a property worth pinning: `crates/extensions/packages/` is
   embedded by four crates, not one. `ironclaw_extension_host`,
   `ironclaw_extension_manager` and `ironclaw_reborn_composition` reach into it
   alongside `ironclaw_extension_support`, and routing to the support crate
   covers them only because each depends on it. If that edge goes, a shipped
   artifact change stops scheduling a crate that embeds it — the silent
   under-schedule the table exists to prevent.

   Regression coverage verified red by sabotage, all three wrong tables:
   owners swapped (7 failures), `packages/` -> `ironclaw_llm` ("embeds nothing
   from it"), and the hardest case, `packages/` -> `ironclaw_reborn_composition`
   — a real embedder that the other embedders do not depend on
   ("...does not depend on..., so routing there never schedules it").

3. CHECKLIST WS10 claimed each of the nine `wit_bindgen` guest edits forces a
   committed WASM artifact rebuild. Only six do:
   `scripts/ci/check-wasm-artifact-freshness.py` scans
   `crates/extensions/packages/*/wasm-src` alone, `wasm-src-digests.toml` holds
   exactly six entries, and `git ls-files '*.wasm'` returns exactly those six.
   The three `test-tools/*/wasm-src/` guests commit no artifact; the tenth site
   is the host's `bindings.rs`, not a guest. Corrected, and the `wit/` row now
   states the boundary rather than implying it.

Guest paths, `wit/` contents and the six rebuilt artifacts are untouched.

Verified: `test_reborn_pr_test_plan.py` 46/46, `test_ws12_workflow_contracts.py`
25/25, `ws12_workflow_contracts.py` green on the real tree,
`cargo test -p ironclaw_architecture` 206/206 across 32 binaries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Re-fold of `ws2/da-factory-port` 5db7257 -> 3108d22: the 22 CodeRabbit
round-1 review fixes (auth, mcp, triggers, webui, extension_contracts, the
architecture gates) plus a changed-coverage repoint.

Git followed all 12 directory renames on its own this time, so the plain merge
produced only **5 content conflicts and 0 rename/location conflicts** — but on
a rename branch a low conflict count is the trap, not the win, so the old-name
scan was run over the merged tree regardless. It found the two real hazards
that auto-merged silently (below).

Conflicts, incoming semantics winning with the rename map re-applied:

- `changed-coverage-exemptions.toml` — both sides had independently repointed
  exemption #113 to 975, and the appended rationale is **byte-identical**; only
  our leading clause differs, naming the exact construct
  (`PostAdmissionObserver::observe_error`'s `error` parameter) where theirs
  says "a function parameter". Kept ours: it is theirs plus precision, and
  loses nothing. Re-verified that line 975 still lands on that parameter after
  the merge (file is 1098 lines).
- `composition/src/runtime/tests/core.rs` — union: our renamed
  `ironclaw_config` import + their new `ironclaw_triggers::{TriggerFireAccess*}`.
- `composition/src/runtime_input.rs` — the fmt-reorder pattern. Ours looked
  like a deletion because the rename changed the import's sort position and
  `cargo fmt` moved it; theirs added `TriggerFireAccessChecker` beside it at
  the old spot. Both symbols are used in the file, so both imports are kept,
  with the new one in the sorted position.
- `composition/src/lib.rs` — theirs wholesale (it records that the relocated
  check contract is deliberately NOT forwarded, §11.2.4 — a rule ours did not
  carry), rename applied to `ironclaw_reborn_cli` and `ironclaw_product`.
- `reborn_extension_host_port_inversion.rs` — theirs wholesale. Verified
  `ours == R(base)` first: our side contributed *only* the rename here, so
  there was no ledger amendment of ours to union in, and theirs' richer WS5
  5 -> 2 -> 0 account replaces it without loss.

Caught by the name scan, not by a conflict — both merged clean and would have
shipped stale:

- `extension_contracts/src/product_adapter_section.rs` — the new WS5 file's
  doc names `ironclaw_product::adapter_registry`; repointed to
  `ironclaw_assistant::adapter_registry`.
- `reborn_composition_boundaries.rs` — two synthetic `// consumer:` fixture
  strings naming `ironclaw_reborn_cli`. Inert (they sit beside a fake
  `ironclaw_thing::Thing`), but renamed anyway so the code-zone invariant stays
  a true 0 rather than accumulating known-benign exceptions.

Code/config/CI zone old-name count after the fold: **0**.
…sence fixture

The hermetic env-mutation guard rejects raw set_var/remove_var without
lock_env(); the fixture now holds the guard across both mutations.
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7152 August 5, 2026 05:58 Destroyed
@BenKurrek

Copy link
Copy Markdown
Collaborator Author

Re-folded onto the moved base — ws2/da-factory-port 5db7257 → 43ca772

Two folds, tip 27a557106. Picks up the ws/review-7181-round1 merge (22 CodeRabbit round-1 fixes across auth, mcp, triggers, webui, extension_contracts and the architecture gates), the changed-coverage repoint, and the follow-up hermetic-env fix in the triggers fixture.

Git followed the renames this time — which is exactly when to distrust the conflict count

Unlike the main fold (224 conflicts, needing the synthetic-rename base), the plain merge here produced 5 content conflicts and 0 rename/location conflicts, and resurrected 0 old crate directories. The delta is ~35 files and git's rename detection tracked all 12 renamed crates unaided, so the synthetic-base technique wasn't needed.

But a low conflict count on a rename branch is the trap this PR already records, not the win: incoming content that names an old crate merges with zero conflicts and then fails to compile, or silently stops a path-keyed gate from matching. So the old-name scan was run over the merged tree anyway — and it caught two hazards that no conflict would have surfaced:

  • extension_contracts/src/product_adapter_section.rs — the new WS5 file's doc names ironclaw_product::adapter_registry. Repointed to ironclaw_assistant::adapter_registry.
  • reborn_composition_boundaries.rs — two synthetic // consumer: fixture strings naming ironclaw_reborn_cli. These are inert (they sit inside string literals beside a fake ironclaw_thing::Thing), but renamed anyway: the code-zone invariant is only worth having if 0 means 0, rather than accumulating known-benign exceptions.

The scan itself needed a fix first — my initial pattern had a trailing \b but no leading one, so it matched the tail of deliberate negative fixtures like my_ironclaw_product and reported 7. With (?<![A-Za-z0-9_]) added, the real count was 3.

Code/config/CI zone after the fold: 0. Docs zone 17 → 17 (measured on the same pattern at 442d88a69 and at HEAD — no reintroduction).

The 5 conflicts

File Reconciliation
changed-coverage-exemptions.toml Both sides had independently repointed exemption #113 to line 975, and the appended rationale is byte-identical — useful corroboration that 975 is right. Only the leading clause differs: ours names the exact construct (PostAdmissionObserver::observe_error's error parameter), theirs says "a function parameter". Kept ours — it is theirs plus precision. Re-verified after merging that line 975 still lands on that parameter (file is 1098 lines), and --validate-manifest-only passes: 194 exemptions, floor 90.0%.
composition/src/runtime/tests/core.rs Union: our renamed ironclaw_config import + their new ironclaw_triggers::{TriggerFireAccessCheck, Checker, Decision, Error}.
composition/src/runtime_input.rs The fmt-reorder pattern again. Our side looked like a deletion because the rename changed the import's sort position and cargo fmt moved it up; theirs added TriggerFireAccessChecker beside it at the old spot. Both symbols are genuinely used in the file (checked before resolving — TriggerPollerWorkerConfig at 270/287/303, TriggerFireAccessChecker at 345/601), so both imports are kept, the new one in the sorted position.
composition/src/lib.rs Theirs wholesale — it records that the relocated check contract is deliberately not forwarded (ironclaw_triggers is its one import path, §11.2.4), a rule our side did not carry. Rename applied to ironclaw_reborn_cli → ironclaw_cli and ironclaw_product → ironclaw_assistant.
reborn_extension_host_port_inversion.rs Theirs wholesale — but only after checking that ours == R(base), i.e. our side contributed only the rename to this ledger paragraph. So there was no amendment of ours to union in, and theirs' richer WS5 5 → 2 → 0 account replaces it with no loss. (This is the check that distinguishes "safe to take theirs" from the ledger-union case on CHECKLIST.md/PROPOSAL.md.)

The second fold (43ca7722c, holding ironclaw_common::env_helpers::lock_env() across the non-UTF-8 presence fixture) merged clean, as expected — ironclaw_triggers is not renamed by this PR.

Verification (final tree, 27a557106)

Gate Result
cargo clippy --all --tests --examples -- -D warnings 0
cargo clippy --all --tests --examples --all-features -- -D warnings 0
cargo test -p ironclaw_architecture_tests 37 binaries / 263 tests, 0 failed (261 → 263; the fold adds two)
-p auth, extension_contracts, extension_host, mcp, triggers, identity, trace_commons 1,298 passed, 0 failed
-p assistant, composition, webui 2,314 passed, 0 failed
cargo fmt --all --check clean
scripts/ci/check-hermetic-env.sh OK (no unguarded env mutation)
changed-coverage --validate-manifest-only 194 exemptions, floor 90.0%
Old-name scan code/CI 0; docs 17 → 17
Planner over the real 1,250-path diff plans, 0 unclassified

The architecture suite was re-run after the second fold so the reported pass is on the final tree, not the intermediate one.

Base automatically changed from ws2/da-factory-port to main August 5, 2026 11:37
# Conflicts:
#	crates/ironclaw_architecture_tests/tests/reborn_composition_boundaries.rs
#	crates/ironclaw_architecture_tests/tests/reborn_extension_host_port_inversion.rs
#	crates/ironclaw_architecture_tests/tests/reborn_same_layer_edge_inventory.rs
#	crates/ironclaw_assistant/src/channel_workflow.rs
#	crates/ironclaw_assistant/tests/product_command_surface_contract.rs
#	crates/ironclaw_assistant/tests/product_surface_contract.rs
#	crates/ironclaw_assistant/tests/run_delivery_contract.rs
#	crates/ironclaw_composition/src/extension_host_assembly.rs
#	crates/ironclaw_composition/src/lib.rs
#	crates/ironclaw_composition/src/runtime.rs
#	crates/ironclaw_composition/src/runtime/tests/core.rs
#	crates/ironclaw_composition/src/runtime_input.rs
#	crates/ironclaw_extension_contracts/src/product_adapter_section.rs
#	crates/ironclaw_extension_host/Cargo.toml
#	crates/ironclaw_extension_host/src/channel_host/e2e_tests.rs
#	crates/ironclaw_product_contracts/src/binding.rs
#	crates/ironclaw_product_contracts/src/channel_workflow.rs
#	crates/ironclaw_product_contracts/src/error.rs
#	docs/extensions/building-a-tool.md
#	docs/reborn/target-architecture/CHECKLIST.md
#	scripts/ci/reborn_pr_test_plan.py
#	tests/integration/changed-coverage-exemptions.toml
#	tests/integration/extension_delivery.rs
#	tests/integration/support/builder.rs
#	tests/integration/support/group.rs
#	tests/integration/support/product_surface.rs
#	tests/support/reborn_parity_qa/binary_e2e.rs
@BenKurrek
BenKurrek enabled auto-merge August 5, 2026 11:55
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7152 August 5, 2026 11:55 Destroyed
@BenKurrek

Copy link
Copy Markdown
Collaborator Author

Refreshed onto post-#7202 main at 934110738: 27 conflicts, all resolved by provenance (the branch already carried #7202's final content through the rename map — verified with a zero-hit whole-word old-name scan over all code zones after the merge). Full queue-exact bar green locally: both clippy lanes 0 errors, arch suite 37/37, unfiltered suites for assistant/composition/extension_host/product_contracts/extension_contracts, fmt clean, composition budget OK. Auto-merge armed — one approval merges it. #7136 (content-verified contained) will be closed when this lands.

🤖 Generated with Claude Code

… stricter planner enumerates

Main's #7215 committed .codebase-memory/ and scripts/codebase-graph.sh
with matching planner rules; this branch's evolved planner kept its own
rule set through the merge and lost those two. Ported both, with the
same rationale comments. Self-tests 59/59.
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7152 August 5, 2026 12:00 Destroyed
@BenKurrek
BenKurrek added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 8a40978 Aug 5, 2026
52 checks passed
@BenKurrek
BenKurrek deleted the ws6/wave4-part2 branch August 5, 2026 13:15
@ironclaw-ci ironclaw-ci Bot mentioned this pull request Aug 5, 2026
BenKurrek added a commit that referenced this pull request Aug 6, 2026
…oven, 2 red-first) + 6 doc-truth corrections

Code, each verified red-first or by sabotage matrix:
- sealed-mint ratchet: per-name sighting floor for TEST_SEAM_MINT_FNS
  (closed path #12). Proven: renaming test_verified_for_tenant away plus one
  extra legitimate sibling mention passed the old aggregate floor (silent
  disarm) and fails the new per-name floor naming the constructor; suite
  23/23 after revert. (CodeRabbit's claimed baseline ">2 mentions today" is
  wrong — each name has exactly one kept sighting — but the doc/enforcement
  mismatch and at-threshold fragility were real.)
- trace credit: non-finite novelty_score/duplicate_score are treated as
  absent before clamping (clamp preserves NaN, which poisoned online_score
  and credit_points_estimate); NaN cases added to the #7144 regression test,
  red first.
- trace submission: a 2xx whose body stream dies mid-read now maps through
  request_failed (network telemetry kind, true I/O cause) instead of
  collapsing to an empty body that the #7144 strict parse misreported as
  response_invalid/Submission; truncated-body regression test, red first.
- Postgres contract suites (event store + assistant ledger): isolated-DB
  names now carry a creation epoch and the once-per-binary sweep is
  age-gated (1h), closing the cross-process window where a sibling's fresh
  zero-backend database (between CREATE DATABASE and first connection) was
  sweepable; legacy pid-scheme leftovers still collect immediately. Proven
  on live Postgres 16: planted stale name swept, planted fresh name
  survives, 13/13 x2 and 20/20 x2 with zero leftovers.

Docs (target-architecture truth pass):
- PROPOSAL section 9: the WS6 rename sweep (#7152) had rewritten the source
  column of the 12 renamed rows to their post-rename names, turning their
  rename dispositions into no-ops (rows 13/14/28/30/49/51/59/61/64/66/67/70);
  pre-restructure names restored with a dated footnote.
- PROPOSAL:69: removed the superseded 3->5->6->7 recount sentence (the
  corrected 3->5->6->7->8 passage subsumes it).
- PROPOSAL row 34: ToolPermissionOverrideStorePort deletion marked landed
  (2026-08-05 WS8, matching section 6.5.3; zero workspace hits).
- CHECKLIST:631: dated note recording that the WS12 F3 gsuite join landed in
  this batch (scenario_uninstalled_tool_call_denied_until_active.rs asserts
  the seeded google token on the gmail.googleapis.com wire; suite run green).
- CHECKLIST:632: dated note spending F4 (the audit's 19 was correct at its
  SHA; the ratchet file now holds 23 tests, re-counted at lines 552/597).
- ws12-gauntlet-report P6 heading: first of TWO real failures (one class),
  matching P8 and the report's own summary.
- ws12-mapping-audit rows 49/137: dated D-S closure notes (await-edge store
  half = journal projection already; resolver retained loop-tier; no shed
  owed) so the backlog register no longer lists it as in-flight.

Not fixed, with evidence: the span-helper macros gate suggestion
(info_span!(target = ...) is a hard compile error, E0425 — no silent trap),
the webui tracing-subscriber workspace-dep suggestion (no
[workspace.dependencies] entry exists; suggestion would not build; 8
siblings use the identical direct shape), and the mapping-audit
regeneration (the audit is accurate at its pinned SHA; the in-batch F1 fix
is recorded in its dated coordinator note).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ogarciarevett pushed a commit to ogarciarevett/ironclaw that referenced this pull request Aug 6, 2026
…2 100% gate (nearai#7263)

* docs(target-arch): resolve the await-edge design question by measurement (D-S) and re-walk the WS9 verify row

Appends §12.13 D-S under delegated authority at owner direction, flagged
for post-hoc review by Illia Polosukhin (nearai#6696's author): the await-edge
store is measured to be a pure projection over ProcessDependencyPort
(that half of the shed happened inside nearai#6696 itself), and the resolver
is a genuine loop-tier responsibility journal edges cannot express
(owner recovery, sanitized transcript result materialization, batch-gate
resume-once drain, BlockedDependentRunGate resume policy). §6.7.3 is
amended (scheduler DONE / store DONE / resolver KEEP) instead of the
shed being executed; the 2.9k figure is corrected to 1,459 production +
1,448 cfg(test) lines. The §12.10 bullet, §2 divergence flag, §9 row 49,
§13 validation row, CHECKLIST header/WS4 pointer, README and PLAN all
carry the dated resolution.

WS9 verify row ticked with evidence: one lifecycle authority (the
process journal; TurnRunState/TurnRunRecord are projections via
AgentTurnProcessRuntime, ProcessRecord is a capability-invocation view,
no bare RunRecord exists) and §7 T4 re-walked clause-by-clause against
merged code — matches, including the checkpoint-gated no-auto-retry
mechanism (BeforeModel precedes ModelStage; requeue only when
checkpoint-free under the 3-claim cap).

Docs-only; no code, no tests, no gates touched.

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

* docs(ws12): rows 1-2 — package-set tick (64==64/1/0, gate+selftest+independent rederivation) and the 74-row §9 mapping audit (45 L / 15 L-A / 14 OBD / 0 NOT-LANDED; 3 findings recorded)

Row 1: check-target-tree.py reports 64 workspace members == 64 documented
packages, 1 documented exclusion (tools/ironclaw_silk_decoder), 0 owned
exceptions (EXCEPTIONS table empty — §5 steady state); self-test 17/17;
cargo-metadata name set diffed empty against an independent §5 parse.

Row 2: docs/reborn/target-architecture/ws12-mapping-audit.md is the audit
record — per-row executed-evidence, delete-clauses read against WS8's
execution notes, all 14 open rows cite their owning CHECKLIST/PROPOSAL
row or issue. Findings (recorded, not fixed): F1 prompt_envelope
manifest-description fix has no owner row; F2 WS6:429's 'nearai#5618 residue
deleted' overstates vs the live adopt_migrated_identity + open WS8:523;
F3 stale-docs cluster where the tree is ahead of the prose (trace
re-export drop, TurnRunTransitionPort, processes->resources).

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

* fold(7154): squash-port fix/red-main-7119 onto family-world main — defect train nearai#7146/nearai#7115/nearai#7104/nearai#7103/nearai#7144 (+nearai#7119 CI lane), 34-hunk contribution.rs port into the split modules, planner entrypoint classification, D-R loopback exception on the widened HTTPS credential guard

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

* test(extractors): issue-number + assertion-rationale doc refinement (rescued 844964f from rescue/7154-parked-guard)

Ports only the doc/assertion refinement commit; the guard-parking commit
e8f5a31 on that branch is deliberately NOT taken — superseded by the
D-R loopback ruling.

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

* docs(target-arch): record D-R — the loopback credential-guard ruling, wiring choice, and regression pins (PROPOSAL §12.13, 2026-08-05)

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

* review(7154): CodeRabbit round-1 triage — fail-closed tracing-target scan traversal (+node_modules), bounded sidecar output draining (capped capture + discard drain), deadlock regression asserts successful redaction (no seq), XLSX/DOCX empty-classification via extract_document, raise_for_status annotations

Threads already addressed by the fold: latency.rs caller-contract wording
(merged doc scopes the requirement to latency-trace callers), BodyJsonPointer
coverage (the plaintext-refusal test drives all four injection shapes).
Deliberately not taken: un-xfailing the four Slack-catalog projections —
the xfail is a documented tripwire (unexpected-pass goes red) and clearing
them is the nearai#6520 projection-modeling follow-on its comment specs.

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

* fix(assistant): re-point the one field-form tracing target the nearai#7146 gate caught — main's relocated triggered_run_delivery_services carried the drift the PR fixed at its old channel_host address

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

* closure fixups: execute the mapping-audit findings — prompt_envelope manifest description (F1), dated ✎ corrections for the nearai#5618 overstatement (F2) and the stale-prose cluster (F3)

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

* docs(ws12): second-reviewer security spot-audit + extension-journey re-verification (rows 5-6)

Adversarial second-reviewer pass over PROPOSAL §12.1a/b/c and the batch's own
§12.13 D-R loopback carve-out, plus a re-run of the five extension journeys.
Attacks were executed rather than argued: two sabotage files and a 38-shape
hostile-URL probe were planted, run, and reverted.

Verdicts — mint consolidation HOLDS-WITH-RESIDUAL, secrets tightening
HOLDS-WITH-RESIDUAL, host/verifier colocation HOLDS, D-R HOLDS. No HOLE.

Four findings recorded rather than fixed (report-not-repair):
- F1 test_verified/_for_tenant are ungranted mint constructors gated only by
  the `test-support` feature, in no mint-name table, with nothing pinning the
  feature to [dev-dependencies]; the shipped binary is measured feature-free.
- F2 §12.1b's products-layer residue undercounts by one (ironclaw_assistant).
- F3 journey coverage hole: gsuite-with-credential-injection is proven in two
  halves that no committed test joins.
- F4 both recorded census evasions and both fail-open reads are CLOSED on this
  tree, so §11.2.5/§12.1a/CHECKLIST:552/:597 now understate the seal.

Rows 5-6 ticked; only lines 631-632 of CHECKLIST touched so the concurrent
rows 3-4 edit folds cleanly.

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

* ratchet(closure): lock the budget gate at the program's end state

Dispatch ceiling 1122 -> 814 (today's observed, nudge taken; WS0 record 827
stays within effective 829). Mass-share ceiling 2398 -> 658 bp (the WS0
baseline floor — the arch-test assert refuses lower, and observed 578 bp sits
inside the nudge window). Absolute LOC re-equalized at 40423: nearai#6831 added 4
governed LOC through the queue's tolerance window; ceiling, observed, and
COMPOSITION_ABSOLUTE_SRC_LOC move together here. Both tightenings
sabotage-verified red (dispatch 9-over at 790; abs 73-over at 40200).

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

* docs(ws12): gauntlet report — row 3 ticked (full gauntlet green, 0 REAL in scope), row 4 verified-but-open on two pre-existing Postgres-leg test-isolation defects

WS12 rows 3-4 verification on the assembled batch tip 0c6c0cf:

Row 3 (ticked): fmt, clippy default/all-features/--lib --bins, workspace
tests (495 targets, 15,203 passed, 0 failed; the smoke.rs:3132
CPU-saturation flake passed first try), arch suite 285/0, the
integration-feature lane 1,665/0, recorded-fixture QA (61 fixtures clean,
41/0), frontend (typecheck 1,588 files; vitest 1,088/0; build + bundle
budgets), e2e smoke = the CI browser lane under the hermetic wrapper
(50 + 21 + 5 passed), and all 41 scripts/ci self-tests (two mapfile/bash-3.2
casualties green under bash 5, the CI shape).

Row 4 (stays open, dated note added): both-backend parity proven with
legs demonstrably executed for the fabric (57 pg + 81 libsql), triggers
(ADR 0003, REQUIRE_POSTGRES), hooks (ADR 0004, all three backends),
composition, processes journal, extension-registry, host-runtime libSQL
restart, and the backend matrix; fabric-delegated domains enumerated.
Two REAL blockers (one class): the Postgres legs of the event-store and
assistant-ledger contract suites assert against shared-database state and
cannot pass as-written (each failing test passes alone on a virgin
database; files byte-identical to origin/main; no CI lane sets their env
vars). Full evidence: docs/reborn/target-architecture/ws12-gauntlet-report.md.

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

* fix(tests): per-test isolated Postgres databases for the two WS12 parity-blocking contract suites

The WS12 gauntlet (ws12-gauntlet-report.md §P6/§P8) measured the Postgres
legs of ironclaw_event_store's durable_event_store_contract and
ironclaw_assistant's durable_ledger_contract as test-isolation-defective:
absolute database-global asserts (event cursors; settled-entry prune
bookkeeping) run against the single external database named by their
IRONCLAW_*_POSTGRES_URL env vars. Every failing test passes alone on a
virgin database - store semantics correct, suites not self-isolating
(PROPOSAL §12.13 D-T).

Fix: each affected test provisions a private database on the configured
server - the fabric contract's IsolatedDatabase pattern
(db_root_filesystem_contract.rs) ported locally into each suite: CREATE
DATABASE per test, store/pool + migrations against it, courtesy
DROP ... WITH (FORCE), and a once-per-binary stale-name sweep. Every
assertion preserved byte-identical; libsql/jsonl twins untouched. In the
ledger suite only the two retention tests move - the other six Postgres
tests keep their proven fingerprint-suffix isolation.

Regression pins are the fixed tests themselves:
- postgres_replay_advances_next_cursor_past_trailing_filtered_records
- postgres_runtime_and_audit_logs_survive_rebuild_with_filtered_cursor_semantics
- postgres_settled_entry_limit_prunes_oldest_when_configured
- postgres_settled_prune_interval_defers_until_interval_when_configured
Green proven on a shared dirty database twice in a row (parallel default
threading) and serially on a virgin database; red-first reproduction
captured before the fix.

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

* docs(reborn): record §12.13 D-T (parity-suite isolation ruling) and close CHECKLIST WS12 row 4

D-T (after D-S): the WS12 gauntlet's two REAL findings were one defect
class — absolute database-global asserts against the single shared
env-var Postgres database — in two suites (event store cursor contract,
assistant settled-ledger retention). Ruling executed in commit 864d93e:
per-test isolated databases via the fabric contract's IsolatedDatabase
pattern, assertions preserved; alternatives (baseline-relative asserts,
serial-only, leave-open) recorded with why they lost; regression pin =
the four fixed tests themselves.

CHECKLIST WS12 backend-parity row ticks [x] with a dated addendum: red-first
reproduction, the three green isolation runs (dirty shared DB twice in
parallel; failing pairs serial on virgin), parity now green 10/10.

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

* docs(target-arch): three measured corrections surfaced by the guidance program

memory packages are substrates-layer, not products (families/extensions.md);
memory_native declares no extension_contracts dep (PROPOSAL §6.8.4); wasm's
extension_contracts edge is dev-only and the wasm 'never depends on' bullet is
lane-scoped, not family-wide (families/lanes.md).

Three further reported defects were checked and NOT corrected — they were
misreads: the sandbox 'never above the runtime tier' rule holds (substrates sit
below it), and PROPOSAL's safety consumer count already reads 17, matching the
tree.

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

* docs(target-arch): repair the corrupted kernel bullet and correct two family laws

kernel.md: ironclaw_authorization's 'Security & authority role' bullet has been
textually corrupted since nearai#6918 — an approvals sentence was spliced into it
mid-clause, orphaning its continuation line. Reconstructed, with the spliced
sentence restored to the approvals entry where it is true.

lanes.md: 'a lane never depends on a substrate' is false as a family-wide law
(ironclaw_sandbox holds network/safety/secrets normal deps, which its own entry
licenses); the accurate law is the layer ladder, and the narrow claim holds for
ironclaw_wasm alone.

lanes.md + events.md: the 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement is superseded by docs/reborn/guidance-conventions.md — two files
restating one rule is the drift the guidance program removes.

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

* test(arch): govern the ProtocolAuthEvidence test seam — WS12 audit F1

Two new gates in reborn_sealed_evidence_mint_ratchet (closed paths nearai#12/nearai#13),
per the audit's remedy spec:
(a) TEST_SEAM_MINT_FNS governs test_verified/test_verified_for_tenant — any
    production-text call site outside ironclaw_host_api is an offender
    (comments/strings stripped, #[cfg(test)] blocks stripped, tests.rs /
    *_tests.rs and cfg-test-only files excluded via the shared census);
(b) test-support may appear in no normal dependency table workspace-wide
    (dependencies / build-dependencies / target.* variants /
    workspace.dependencies), and no [features] key other than test-support
    may forward to it — the laundering shape that would evade (b) by one
    rename. [dev-dependencies] enablement stays legal (cargo-features.md
    bar 4, the sanctioned dev seam).

Measured zero offenders on this tree in both directions before pinning;
sabotage-proven red->green both ways (planted production call named with
file:line-text; [dependencies] enablement named with its table path).
Self-tests drive the same pipelines the gates run (zero-match principle);
the definition-location and partition tests now cover the new table.

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

* test(integration): join the gsuite credential-injection journey — WS12 audit F3

WS12 row 5 leg 3 was verified in two halves no committed test joined: gsuite
handler -> staged credential (crate tier) and staged obligation -> wire
(GitHub/Slack only). Scenario 5 already drives gmail.list_messages through
production dispatch on a Google-OAuth-configured group; it now also asserts
the JOIN: the seeded google account's token (itest-google-token) lands on
the recorded outbound gmail.googleapis.com request as
'authorization: Bearer ...', injected at the host egress chokepoint
(apply_credential_injection) per the gmail manifest's declared recipe —
store -> dispatch-time staging -> chokepoint -> wire, through the caller.

Sabotage-proven: disabling the Header injection arm reds exactly this
scenario with 'no network egress request matching url gmail.googleapis.com
has header authorization' while the request itself still reaches the wire
(headers seen: content-type only) — the injection reason, not a setup
error; restore -> green.

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

* docs(target-arch): correct five measured dependency claims in families/domains.md

conversations does not depend on safety (its BoundaryRule now forbids it);
triggers depends on libsql_runtime + safety and NOT filesystem, so its
'filesystem-routed persistence path alongside SQL' is one path, not two;
memory's live set is host_api alone (prompt_envelope is allowlisted, unused);
auth was short by extension_contracts + product_contracts.

Each verified against the manifest before editing.

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

* docs(target-arch): record the closed scan evasions (F4) and the secrets-consumer correction (F2)

The sealed-mint census weaknesses PROPOSAL §11.2.5/§12.1a and CHECKLIST recorded
as live and owed to WS10 are all closed on this tree, verified by re-attacking
the seam with both evasions at once; the docs understated the seal. Ratchet is
23 tests. One residual replaces them: the test_verified test-seam constructors,
now pinned by two gates.

§12.1b's 'only products-layer crate with the edge' is false by one —
ironclaw_assistant carries ironclaw_secrets as port-declaration vocabulary with
no expose_secret call. Not a value-reach bypass; joins nearai#7095's inventory.

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

* docs(target-arch): correct the app-family layer, config's consumer set, and the webui route count

ironclaw_config declares layer=substrates while living in crates/app/;
its consumers include operator, extension_manager and extension_host, not just
the assembly crate and the binary; webui is 93 contract-locked routes, not 92
(nearai#6780 landed after the last recount).

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

* docs(ws12): tick row 7 — the fresh-agent placement probe passed on the final tree

All three placements correct with high confidence, each naming the trait, the
tests, and the tempting wrong place it rejected. The probe doubled as a docs
audit and independently hit four defects, three of which the stacked guidance
PR fixes — it succeeded despite them.

WS12 is now 7/7. The restructure is complete.

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

* docs(target-arch): the product→loop_host recount was wrong on the day it was written

Eight importing files across four seams, not seven across three — the fourth
being a skill-activation-observer seam (projection.rs, projection/live_progress.rs)
this bullet never named, which §6.4.7's own same-day note already implied.
Surfaced by the plan-conformance audit.

The recount history is 3→5→6→7→8, wrong at four of five attempts. That retires
the prose count as a method: the sever slice should land an inventory ratchet
before or with the move, not another number.

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

* review(7263): CodeRabbit round-1 triage — 4 code fixes (2 sabotage-proven, 2 red-first) + 6 doc-truth corrections

Code, each verified red-first or by sabotage matrix:
- sealed-mint ratchet: per-name sighting floor for TEST_SEAM_MINT_FNS
  (closed path nearai#12). Proven: renaming test_verified_for_tenant away plus one
  extra legitimate sibling mention passed the old aggregate floor (silent
  disarm) and fails the new per-name floor naming the constructor; suite
  23/23 after revert. (CodeRabbit's claimed baseline ">2 mentions today" is
  wrong — each name has exactly one kept sighting — but the doc/enforcement
  mismatch and at-threshold fragility were real.)
- trace credit: non-finite novelty_score/duplicate_score are treated as
  absent before clamping (clamp preserves NaN, which poisoned online_score
  and credit_points_estimate); NaN cases added to the nearai#7144 regression test,
  red first.
- trace submission: a 2xx whose body stream dies mid-read now maps through
  request_failed (network telemetry kind, true I/O cause) instead of
  collapsing to an empty body that the nearai#7144 strict parse misreported as
  response_invalid/Submission; truncated-body regression test, red first.
- Postgres contract suites (event store + assistant ledger): isolated-DB
  names now carry a creation epoch and the once-per-binary sweep is
  age-gated (1h), closing the cross-process window where a sibling's fresh
  zero-backend database (between CREATE DATABASE and first connection) was
  sweepable; legacy pid-scheme leftovers still collect immediately. Proven
  on live Postgres 16: planted stale name swept, planted fresh name
  survives, 13/13 x2 and 20/20 x2 with zero leftovers.

Docs (target-architecture truth pass):
- PROPOSAL section 9: the WS6 rename sweep (nearai#7152) had rewritten the source
  column of the 12 renamed rows to their post-rename names, turning their
  rename dispositions into no-ops (rows 13/14/28/30/49/51/59/61/64/66/67/70);
  pre-restructure names restored with a dated footnote.
- PROPOSAL:69: removed the superseded 3->5->6->7 recount sentence (the
  corrected 3->5->6->7->8 passage subsumes it).
- PROPOSAL row 34: ToolPermissionOverrideStorePort deletion marked landed
  (2026-08-05 WS8, matching section 6.5.3; zero workspace hits).
- CHECKLIST:631: dated note recording that the WS12 F3 gsuite join landed in
  this batch (scenario_uninstalled_tool_call_denied_until_active.rs asserts
  the seeded google token on the gmail.googleapis.com wire; suite run green).
- CHECKLIST:632: dated note spending F4 (the audit's 19 was correct at its
  SHA; the ratchet file now holds 23 tests, re-counted at lines 552/597).
- ws12-gauntlet-report P6 heading: first of TWO real failures (one class),
  matching P8 and the report's own summary.
- ws12-mapping-audit rows 49/137: dated D-S closure notes (await-edge store
  half = journal projection already; resolver retained loop-tier; no shed
  owed) so the backlog register no longer lists it as in-flight.

Not fixed, with evidence: the span-helper macros gate suggestion
(info_span!(target = ...) is a hard compile error, E0425 — no silent trap),
the webui tracing-subscriber workspace-dep suggestion (no
[workspace.dependencies] entry exists; suggestion would not build; 8
siblings use the identical direct shape), and the mapping-audit
regeneration (the audit is accurate at its pinned SHA; the in-batch F1 fix
is recorded in its dated coordinator note).

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

* review(7263): CodeRabbit round-2 — rejection-body read keeps its cause; 200 {} is not a submission acknowledgement; lanes.md family dep rule matches measured Cargo.tomls

- submission.rs non-2xx path: a failed rejection-body read no longer collapses
  to an empty detail via .unwrap_or_default() (banned by
  .claude/rules/error-handling.md); the read error folds into the
  http_rejection detail so the received status keeps driving the 401/403
  auth-retry and the Credential/HttpRejection telemetry split.
  Regression: submit_preserves_rejection_body_read_failure_cause_with_status.

- TraceSubmissionReceipt.status: serde default removed — it fabricated
  status "submitted" from a proxy's 200 {} (the nearai#7144 synthesis, resurfacing
  through the wire type's defaults), after which the flush caller recorded
  Submitted and deleted the only retryable queued copy. The acknowledgement is
  the server naming what happened to the submission — every workspace fixture
  sends status and callers persist it unconditionally as server_status — so a
  status-less 2xx body now fails the strict receipt parse as response_invalid.
  Regression: submit_rejects_success_response_without_explicit_server_status
  (covers 200 {} and a status-less non-empty object).

- docs(lanes.md): the family Dependency-direction rule no longer claims every
  lane takes the extension-surface vocabulary crate — measured across
  crates/lanes/*/Cargo.toml: mcp + sandbox hold ironclaw_extension_contracts
  under [dependencies], wasm only under [dev-dependencies]; dated ✎
  cross-references the ironclaw_wasm entry's 2026-08-05 correction.

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

* review(7263): CodeRabbit round-3 — shared Postgres test provisioner (the "new dep edge" premise measured false), entrypoint self-test armed (sabotage-proven), six doc self-contradictions reconciled

Code:
- ironclaw_filesystem gains a `postgres_isolation` test-support module — the
  single home of the per-test isolated-database scaffolding (once-per-binary
  age-gated stale sweep, epoch-in-name convention, DROP WITH (FORCE) cleanup),
  parameterised by suite/env-var/prefix/unreachable-policy. Zero new
  production edges: event_store already normal-deps filesystem, filesystem
  already owns tokio-postgres, and the dev-dep+feature pattern is the one 17
  crates already use. The event-store and product-workflow-ledger suites
  migrate onto it; both Postgres legs proven live against postgres:16 (12
  tests, zero leftover databases). The fabric original keeps its older
  variant with the differences documented at its IsolatedDatabase.
- ironclaw_event_store drops the duplicate tokio-postgres dev-dep (the normal
  dep already reaches tests).
- test-reborn-docker-entrypoint.sh: the missing-argv check now exits the
  command-substitution subshell instead of incrementing a counter the parent
  never sees — red-proven (a migrate-but-never-exec entrypoint passed with 7
  FAIL lines printed), green after the fix both sabotaged and restored.
- trace_commons submission test additionally pins !auth_rejection() for the
  503 rejection (the structural assert the API affords; the prescribed
  payload asserts are refuted — status is private and source is None by
  design, with the message derived from the structured status in the same
  constructor).

Docs (each reconciled to one canonical statement, measured):
- kernel.md: lease ownership decided from code — authorization stores,
  matches, and expires leases (CapabilityLeaseStore + port + expiry all live
  there); approvals constructs and issues into that store. The round-1
  re-homing of the spliced sentence into approvals was wrong and is corrected
  in the dated repair note.
- app.md: "nothing depends on app" scoped to the three app-layer crates;
  ironclaw_config's consumers restated by dependency kind (normal:
  composition, cli, operator, extension_host; dev-only: extension_manager,
  root integration-tests package).
- lanes.md: the mediated-services sentence now states the family law as
  layer-ladder + injected authority; the no-secrets/network/filesystem-dep
  claim is scoped to ironclaw_wasm, matching the file's own corrections.
- CHECKLIST 429/430: the one open traces clause is named (ScopedFilesystem
  adoption); the stale "other two" count corrected against the F3a strike.
- PROPOSAL:69 + CHECKLIST:72: the project-create route repointed —
  first_party_extension_ports dissolved into loop_host::skill_activation
  (WS8, §9 row 55) — still unattempted.
- PROPOSAL §9 rows 57/62 synced to §6.8.4 (telegram: dependency-set equality
  with Slack's four contract-tier crates) and §6.9.4 (webui -> assistant is a
  charter-permanent edge, §12.11 D-B).
- PLAN top summary records Wave 6's design question as resolved (D-S,
  2026-08-05).
- deploy-reborn-cli-docker.md: the two migration paragraphs unified on the
  entrypoint's actual behavior — only enabled = false beside
  signing_secret_env/bot_token_env is migrated; every other retired-key shape
  fails startup with the migration pointer.
- composition-budget.toml: the stale "2398 bp, a true ratchet" header
  replaced with the WS0-floor truth the baselines test asserts.

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

* docs: move the guidance convention into this PR so its citations resolve

families/lanes.md and families/events.md cite docs/reborn/guidance-conventions.md
when superseding their 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement, but the file was only on the stacked guidance branch — a forward
reference that dangles if this PR merges alone. The convention is the rule those
notes invoke, so it belongs with them.

Caught by the CodeRabbit round-3 pass.

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

* fix(ci): give the hoisted postgres provisioner its safety rationales

The round-3 hoist moved test provisioning into a production src/ path, so
check_no_panics flagged its four panic/expect sites and reddened Code Style via
fast-checks. The gate is right to flag them: it deliberately does NOT exempt
#[cfg(feature = "test-support")] modules, because a cargo feature is not a
privilege boundary in this workspace (PROPOSAL 12.1a proved exactly that) —
so a test-support module still compiles into a build where any sibling enables
the feature.

Suppressed with the gate's documented inline rationale, which must trail the
statement rather than precede it. The panics themselves stay: a configured but
unusable Postgres must fail the suite loudly rather than skip it, which is the
inert-guard rule the isolation fix exists to serve.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
pull Bot pushed a commit to bryanwills/ironclaw that referenced this pull request Aug 6, 2026
…ry crate, and a repo-wide stale sweep (nearai#7264)

* docs(target-arch): resolve the await-edge design question by measurement (D-S) and re-walk the WS9 verify row

Appends §12.13 D-S under delegated authority at owner direction, flagged
for post-hoc review by Illia Polosukhin (nearai#6696's author): the await-edge
store is measured to be a pure projection over ProcessDependencyPort
(that half of the shed happened inside nearai#6696 itself), and the resolver
is a genuine loop-tier responsibility journal edges cannot express
(owner recovery, sanitized transcript result materialization, batch-gate
resume-once drain, BlockedDependentRunGate resume policy). §6.7.3 is
amended (scheduler DONE / store DONE / resolver KEEP) instead of the
shed being executed; the 2.9k figure is corrected to 1,459 production +
1,448 cfg(test) lines. The §12.10 bullet, §2 divergence flag, §9 row 49,
§13 validation row, CHECKLIST header/WS4 pointer, README and PLAN all
carry the dated resolution.

WS9 verify row ticked with evidence: one lifecycle authority (the
process journal; TurnRunState/TurnRunRecord are projections via
AgentTurnProcessRuntime, ProcessRecord is a capability-invocation view,
no bare RunRecord exists) and §7 T4 re-walked clause-by-clause against
merged code — matches, including the checkpoint-gated no-auto-retry
mechanism (BeforeModel precedes ModelStage; requeue only when
checkpoint-free under the 3-claim cap).

Docs-only; no code, no tests, no gates touched.

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

* docs(ws12): rows 1-2 — package-set tick (64==64/1/0, gate+selftest+independent rederivation) and the 74-row §9 mapping audit (45 L / 15 L-A / 14 OBD / 0 NOT-LANDED; 3 findings recorded)

Row 1: check-target-tree.py reports 64 workspace members == 64 documented
packages, 1 documented exclusion (tools/ironclaw_silk_decoder), 0 owned
exceptions (EXCEPTIONS table empty — §5 steady state); self-test 17/17;
cargo-metadata name set diffed empty against an independent §5 parse.

Row 2: docs/reborn/target-architecture/ws12-mapping-audit.md is the audit
record — per-row executed-evidence, delete-clauses read against WS8's
execution notes, all 14 open rows cite their owning CHECKLIST/PROPOSAL
row or issue. Findings (recorded, not fixed): F1 prompt_envelope
manifest-description fix has no owner row; F2 WS6:429's 'nearai#5618 residue
deleted' overstates vs the live adopt_migrated_identity + open WS8:523;
F3 stale-docs cluster where the tree is ahead of the prose (trace
re-export drop, TurnRunTransitionPort, processes->resources).

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

* fold(7154): squash-port fix/red-main-7119 onto family-world main — defect train nearai#7146/nearai#7115/nearai#7104/nearai#7103/nearai#7144 (+nearai#7119 CI lane), 34-hunk contribution.rs port into the split modules, planner entrypoint classification, D-R loopback exception on the widened HTTPS credential guard

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

* test(extractors): issue-number + assertion-rationale doc refinement (rescued 844964f from rescue/7154-parked-guard)

Ports only the doc/assertion refinement commit; the guard-parking commit
e8f5a31 on that branch is deliberately NOT taken — superseded by the
D-R loopback ruling.

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

* docs(target-arch): record D-R — the loopback credential-guard ruling, wiring choice, and regression pins (PROPOSAL §12.13, 2026-08-05)

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

* review(7154): CodeRabbit round-1 triage — fail-closed tracing-target scan traversal (+node_modules), bounded sidecar output draining (capped capture + discard drain), deadlock regression asserts successful redaction (no seq), XLSX/DOCX empty-classification via extract_document, raise_for_status annotations

Threads already addressed by the fold: latency.rs caller-contract wording
(merged doc scopes the requirement to latency-trace callers), BodyJsonPointer
coverage (the plaintext-refusal test drives all four injection shapes).
Deliberately not taken: un-xfailing the four Slack-catalog projections —
the xfail is a documented tripwire (unexpected-pass goes red) and clearing
them is the nearai#6520 projection-modeling follow-on its comment specs.

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

* fix(assistant): re-point the one field-form tracing target the nearai#7146 gate caught — main's relocated triggered_run_delivery_services carried the drift the PR fixed at its old channel_host address

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

* closure fixups: execute the mapping-audit findings — prompt_envelope manifest description (F1), dated ✎ corrections for the nearai#5618 overstatement (F2) and the stale-prose cluster (F3)

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

* docs(ws12): second-reviewer security spot-audit + extension-journey re-verification (rows 5-6)

Adversarial second-reviewer pass over PROPOSAL §12.1a/b/c and the batch's own
§12.13 D-R loopback carve-out, plus a re-run of the five extension journeys.
Attacks were executed rather than argued: two sabotage files and a 38-shape
hostile-URL probe were planted, run, and reverted.

Verdicts — mint consolidation HOLDS-WITH-RESIDUAL, secrets tightening
HOLDS-WITH-RESIDUAL, host/verifier colocation HOLDS, D-R HOLDS. No HOLE.

Four findings recorded rather than fixed (report-not-repair):
- F1 test_verified/_for_tenant are ungranted mint constructors gated only by
  the `test-support` feature, in no mint-name table, with nothing pinning the
  feature to [dev-dependencies]; the shipped binary is measured feature-free.
- F2 §12.1b's products-layer residue undercounts by one (ironclaw_assistant).
- F3 journey coverage hole: gsuite-with-credential-injection is proven in two
  halves that no committed test joins.
- F4 both recorded census evasions and both fail-open reads are CLOSED on this
  tree, so §11.2.5/§12.1a/CHECKLIST:552/:597 now understate the seal.

Rows 5-6 ticked; only lines 631-632 of CHECKLIST touched so the concurrent
rows 3-4 edit folds cleanly.

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

* ratchet(closure): lock the budget gate at the program's end state

Dispatch ceiling 1122 -> 814 (today's observed, nudge taken; WS0 record 827
stays within effective 829). Mass-share ceiling 2398 -> 658 bp (the WS0
baseline floor — the arch-test assert refuses lower, and observed 578 bp sits
inside the nudge window). Absolute LOC re-equalized at 40423: nearai#6831 added 4
governed LOC through the queue's tolerance window; ceiling, observed, and
COMPOSITION_ABSOLUTE_SRC_LOC move together here. Both tightenings
sabotage-verified red (dispatch 9-over at 790; abs 73-over at 40200).

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

* docs(ws12): gauntlet report — row 3 ticked (full gauntlet green, 0 REAL in scope), row 4 verified-but-open on two pre-existing Postgres-leg test-isolation defects

WS12 rows 3-4 verification on the assembled batch tip 0c6c0cf:

Row 3 (ticked): fmt, clippy default/all-features/--lib --bins, workspace
tests (495 targets, 15,203 passed, 0 failed; the smoke.rs:3132
CPU-saturation flake passed first try), arch suite 285/0, the
integration-feature lane 1,665/0, recorded-fixture QA (61 fixtures clean,
41/0), frontend (typecheck 1,588 files; vitest 1,088/0; build + bundle
budgets), e2e smoke = the CI browser lane under the hermetic wrapper
(50 + 21 + 5 passed), and all 41 scripts/ci self-tests (two mapfile/bash-3.2
casualties green under bash 5, the CI shape).

Row 4 (stays open, dated note added): both-backend parity proven with
legs demonstrably executed for the fabric (57 pg + 81 libsql), triggers
(ADR 0003, REQUIRE_POSTGRES), hooks (ADR 0004, all three backends),
composition, processes journal, extension-registry, host-runtime libSQL
restart, and the backend matrix; fabric-delegated domains enumerated.
Two REAL blockers (one class): the Postgres legs of the event-store and
assistant-ledger contract suites assert against shared-database state and
cannot pass as-written (each failing test passes alone on a virgin
database; files byte-identical to origin/main; no CI lane sets their env
vars). Full evidence: docs/reborn/target-architecture/ws12-gauntlet-report.md.

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

* docs(guidance): set the crate/family guidance convention

The base commit for the family-guidance program: one canonical home per fact,
measured-not-aspirational claims, boundaries stated as exclusions, and the note
that guidance files can be gate-pinned. Every family/crate document written on
top of this branch follows this shape.

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

* fix(tests): per-test isolated Postgres databases for the two WS12 parity-blocking contract suites

The WS12 gauntlet (ws12-gauntlet-report.md §P6/§P8) measured the Postgres
legs of ironclaw_event_store's durable_event_store_contract and
ironclaw_assistant's durable_ledger_contract as test-isolation-defective:
absolute database-global asserts (event cursors; settled-entry prune
bookkeeping) run against the single external database named by their
IRONCLAW_*_POSTGRES_URL env vars. Every failing test passes alone on a
virgin database - store semantics correct, suites not self-isolating
(PROPOSAL §12.13 D-T).

Fix: each affected test provisions a private database on the configured
server - the fabric contract's IsolatedDatabase pattern
(db_root_filesystem_contract.rs) ported locally into each suite: CREATE
DATABASE per test, store/pool + migrations against it, courtesy
DROP ... WITH (FORCE), and a once-per-binary stale-name sweep. Every
assertion preserved byte-identical; libsql/jsonl twins untouched. In the
ledger suite only the two retention tests move - the other six Postgres
tests keep their proven fingerprint-suffix isolation.

Regression pins are the fixed tests themselves:
- postgres_replay_advances_next_cursor_past_trailing_filtered_records
- postgres_runtime_and_audit_logs_survive_rebuild_with_filtered_cursor_semantics
- postgres_settled_entry_limit_prunes_oldest_when_configured
- postgres_settled_prune_interval_defers_until_interval_when_configured
Green proven on a shared dirty database twice in a row (parallel default
threading) and serially on a virgin database; red-first reproduction
captured before the fix.

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

* docs(reborn): record §12.13 D-T (parity-suite isolation ruling) and close CHECKLIST WS12 row 4

D-T (after D-S): the WS12 gauntlet's two REAL findings were one defect
class — absolute database-global asserts against the single shared
env-var Postgres database — in two suites (event store cursor contract,
assistant settled-ledger retention). Ruling executed in commit 864d93e:
per-test isolated databases via the fabric contract's IsolatedDatabase
pattern, assertions preserved; alternatives (baseline-relative asserts,
serial-only, leave-open) recorded with why they lost; regression pin =
the four fixed tests themselves.

CHECKLIST WS12 backend-parity row ticks [x] with a dated addendum: red-first
reproduction, the three green isolation runs (dirty shared DB twice in
parallel; failing pairs serial on virgin), parity now green 10/10.

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

* docs(extensions): family guidance layer — AGENTS.md rewrite to the guidance-conventions shape, READMEs for all 4 family crates and 14 packages, duplicate-guidance consolidation

The family AGENTS.md now teaches the unified extension model (extension =
the only product object; channel/tool/auth are manifest surfaces; runtime
is loading, never taxonomy; ExtensionId vs VendorId; retired vocabulary
pinned by reborn_retired_taxonomy.rs), carries the self-containment and
package-to-crate rules from families/extensions.md, the four-responsibility
lookup, the measured package catalog, the exclusion list, and the armed
gates by test name.

Every crate and package gains a README.md (ironclaw_extension_host had no
guidance of any kind). ironclaw_extension_registry and memory-native each
had both an AGENTS.md and a CLAUDE.md saying overlapping things: AGENTS.md
is now canonical, CLAUDE.md a pointer, and memory-native's stale v1
references (src/workspace, src/db/libsql) are dropped in the merge. The
slack/telegram agent maps get package framing and a contracts-tier pointer
in place of the stale ironclaw_assistant one. Every path literal verified
to resolve on disk; all figures (tool counts, dep sets, consumers, layer
declarations) measured from the tree at 8d13454.

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

* docs(guidance): substrates + lanes family guidance per guidance-conventions.md

Family AGENTS.md rewritten to the spec shape for crates/substrates/ and
crates/lanes/: boundary, crate table, exclusion lists (mechanism-not-authority
for substrates; kernel-decides-lane-executes for lanes), armed gates by test
name, and measured deviations stated as deviations (sandbox's three substrate
deps, script.rs direct spawn). The lanes wit/-is-load-bearing note is kept.

A README.md for every crate in both families (10 new), measured against
cargo metadata 2026-08-05: public surface, workspace edges, consumer counts,
and enforced invariants each citing their gate. ironclaw_libsql_runtime and
ironclaw_wasm_limiter previously had no guidance of any kind; their READMEs
carry the sole-pool-home rule (ADDITIONAL_DRIVER_ALLOWLISTS: deadpool =
{filesystem, libsql_runtime}) and the outbound-only limiter gate
(wasm_sandbox_core_module_stays_domain_free_v1_parity_kernel; no BoundaryRule
names the limiter).

Duplicate guidance consolidated per rule 1: for the six crates holding both
AGENTS.md and CLAUDE.md (filesystem, network, secrets, mcp, sandbox, wasm),
CLAUDE.md stays canonical (module spec for filesystem; gate-pinned wording for
mcp and wasm) and AGENTS.md becomes a short pointer. No gate-pinned file was
edited. Stale reference removed: safety AGENTS.md pointed at
src/NETWORK_SECURITY.md, which exists nowhere in the tree.

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

* docs(crates-map): rewrite the three top-level maps family-first after the restructure

crates/AGENTS.md (264 -> 175 lines): routing map only — the ten families,
the read order (family AGENTS.md -> crate README.md -> working rules/module
spec -> docs/reborn/contracts/), the enforced seven-layer matrix with the
family/layer divergences, measured workspace facts (64 packages, 1 documented
exclusion, 0 owned exceptions per scripts/ci/check-target-tree.py), and a
verified command block. The 40-row per-crate map is gone: family AGENTS.md
files own crate routing per docs/reborn/guidance-conventions.md.

crates/README.md (141 -> 119 lines): human map — mental model in family
vocabulary, the ten families with measured crate counts, the 14 extension
packages (4 crates + 10 data-only), and the two workspace members outside
crates/.

crates/Architecture.md (1019 -> 1059 lines): audited against the live tree;
every named symbol/path re-verified 2026-08-05. Corrected: retired
ProductAdapter vocabulary (zero residue in code), the stale pre-rename
dependency ladder that still cited the deleted gateway/TUI crates, run-state
store mentions, lane-table crate anchors (sandbox/extension_support),
declared-in vs minted-by owners in the core data model, and the subagent
deny-filter status note (re-verified). Marked the pre-restructure
'partial or evolving' list as unmeasured rather than asserting it.

Also documents that scripts/check-boundaries.sh fails on a clean tree
(check-5 grep false positives) and greps the deleted v1 src/ in 4 of 6
checks — boundary enforcement for crates/ is the architecture suite.

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

* docs(crates-map): package directories carry their own README.md (coordinator sync with extensions-family agent)

Every extensions package dir — the 10 data-only ones included — now ships a
README.md, so both maps extend the read order to package level. The sibling
branch also confirmed what this map already derived per-crate: packages/ is
not uniformly products-layer (memory-native and mem0 declare substrates).
The other two coordinator corrections targeted rows of the old per-crate
map, which this rewrite deleted wholesale; nothing here cites
memory-native's CLAUDE.md or claims ironclaw_extension_host lacks guidance.

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

* docs(guidance): contracts + events family guidance layer per docs/reborn/guidance-conventions.md

- crates/contracts/AGENTS.md and crates/events/AGENTS.md rewritten to the
  family shape: exclusion lists with destinations, armed gates by test name,
  layer-matrix rows, crossing guide, measured header counts.
- README.md added for all 10 crates (ironclaw_prompt_envelope previously had
  no guidance of any kind — the CHECKLIST WS11 gap).
- One canonical guidance file per crate, other file a pointer:
  A+C merges for ironclaw_host_api, ironclaw_event_log,
  ironclaw_event_projections, ironclaw_event_streams; CLAUDE-only content
  moved to AGENTS.md for ironclaw_loop_contracts,
  ironclaw_extension_contracts, ironclaw_product_contracts (none of these are
  root module-spec crates, so AGENTS.md is the working-rules home).
- Stale guidance fixed against the live tree:
  * loop_contracts dep list contradicted the enforced allowlist (manifest is
    host_api + extension_contracts; common/prompt_envelope are permitted,
    unused).
  * event_log still documented the deleted jsonl parse/replay helpers.
  * event_projections still claimed EventStreamManager,
    DurableMemoryAuditSink, MemoryAuditProjectionMetadata, and
    PendingGateProjection — all deleted per PROPOSAL 6.3.3.
  * product_contracts still carried the pre-D-E open vendor decision under
    the nonexistent module name llm_config, and a Deferred section
    contradicting its own operator_llm/operator_service rows.
  * extension_contracts module table was missing the WS3 runtime module
    while counting 18.
  * common's llm_costs note carried the ModelCostTable seam claim refuted by
    PROPOSAL 12.11 D-F; now cites the pricer-port ruling and the vendor
    census residue.
- Deleted crates/events/ironclaw_event_projections/PENDING_GATE_PROJECTION.md:
  every claim in it referenced deleted symbols or the removed v1 src/ tree,
  and its only inbound reference was the crate's own CLAUDE.md.

Verified: all consumer counts reproduce via the printed grep commands; 147
path literals across the 28 touched files resolve on disk; no architecture
test reads any of these files by name; conflict-marker scan clean.

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

* docs(target-arch): three measured corrections surfaced by the guidance program

memory packages are substrates-layer, not products (families/extensions.md);
memory_native declares no extension_contracts dep (PROPOSAL §6.8.4); wasm's
extension_contracts edge is dev-only and the wasm 'never depends on' bullet is
lane-scoped, not family-wide (families/lanes.md).

Three further reported defects were checked and NOT corrected — they were
misreads: the sandbox 'never above the runtime tier' rule holds (substrates sit
below it), and PROPOSAL's safety consumer count already reads 17, matching the
tree.

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

* docs(kernel): family guidance layer — perimeter AGENTS.md, nine crate READMEs, AGENTS/CLAUDE consolidation

Family-guidance program, kernel family (guidance-conventions.md shape):

- crates/kernel/AGENTS.md rewritten to the family shape: the nine-stage
  effect pipeline with stage ownership, the sealed-mint table (witness /
  trust ceiling / approval lease / verified-inbound evidence, each with its
  mint site and its seal mechanism), the per-stage fail-closed table with
  file:line or test citations, the sharp exclusion list, and the armed
  gates by test name (authorized-seal ratchet, sealed-evidence mint
  ratchet, BoundaryRules, same-layer edge inventory at 21 kernel edges,
  empty LAYER_MATRIX_EXCEPTIONS register, driver boundary, process storage
  scan, origin-gate matrix ratchet).
- A README.md for each of the nine crates, per the crate shape: measured
  workspace deps and consumer counts (cargo metadata), public surface with
  verified citations, enforced invariants naming their gates.
  ironclaw_processes states the single-lifecycle-authority direction of
  truth (journal = store; TurnRunState/ProcessRecord/await-edge =
  projections; PROPOSAL §12.13 D-S); ironclaw_host_runtime documents the
  D-R literal-loopback carve-out and names its two regression tests.
- Duplicate guidance reconciled in all nine crates: AGENTS.md is canonical
  (guardrails absorbed), CLAUDE.md reduced to a pointer; ironclaw_trust's
  CONTRACT.md untouched as the co-located cross-crate contract.
- Stale references fixed inside owned paths: the deleted capability-profile
  conformance module (evaluate_profile_conformance — zero hits
  workspace-wide) removed from ironclaw_capabilities guidance; trust's
  'staging branch' / 'PR3' phrasing updated; capabilities' 'later
  obligation slices' updated to the landed host_runtime obligations split;
  cross-crate path mentions fully qualified. Every path literal in all 29
  kernel .md files verified to resolve on disk; every named symbol swept
  against crate sources.

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

* docs(domains): family guidance layer — AGENTS.md boundary doc, 12 crate READMEs, duplicate-guidance consolidation, stale-path fixes

Family guidance for crates/domains/ per docs/reborn/guidance-conventions.md:

- crates/domains/AGENTS.md rewritten to the family shape: charter table with
  go-here-when routing, the exclusion list, every armed gate named by test
  (BoundaryRules + identity/memory allowlists, the 5-entry in-family edge
  inventory, the naming gates, trusted-trigger ownership, the memory-provider
  residue ledger, persistence-driver boundary, the two module-charter gates).
- A measured README.md for each of the 12 crates: charter, use-when /
  don't-use-when routing, public surface, measured normal deps + named
  consumers, enforced invariants with their gates, exact test commands.
  ironclaw_attachments and ironclaw_identity had no guidance of any kind;
  identity's README points at CONTRACT.md (the module spec), llm's at its
  CLAUDE.md module spec.
- Duplicate guidance consolidated to one canonical file + pointer per crate:
  threads/conversations/memory/outbound rules now live in AGENTS.md (CLAUDE.md
  is a pointer); auth/llm keep CLAUDE.md canonical because their
  tests/module_charter.rs gates read it (AGENTS.md is the pointer). One
  misstatement fixed in the conversations merge: transcript content belongs to
  ironclaw_threads' SessionThreadService, not InboundConversationService.
- Staleness fixed inside the family: identity CONTRACT.md two-edge allowlist
  claim reconciled with D-Q's three entries; trace_commons CLAUDE.md gains the
  capture module row and strikes its two discharged Known Gaps (recording/paths
  shims deleted, rename done); llm CLAUDE.md reasoning.rs caller corrected to
  crates/loop/ironclaw_loop_host; triggers lib.rs 'feature-gated' repo doc
  comments corrected; pre-family path literals in comments repointed
  (kernel/approvals+processes, loop/hooks, app/architecture_tests,
  domains/auth) and the deleted-v1-engine references in skills marked
  historical.

Verified: cargo test -p ironclaw_llm --no-fail-fast (922 passed, exit 0 —
CLAUDE.md is gate-pinned); cargo check --all-targets on all six crates with
source edits; every cited path literal resolves on disk; conflict-marker scan
clean.

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

* docs(target-arch): repair the corrupted kernel bullet and correct two family laws

kernel.md: ironclaw_authorization's 'Security & authority role' bullet has been
textually corrupted since nearai#6918 — an approvals sentence was spliced into it
mid-clause, orphaning its continuation line. Reconstructed, with the spliced
sentence restored to the approvals entry where it is true.

lanes.md: 'a lane never depends on a substrate' is false as a family-wide law
(ironclaw_sandbox holds network/safety/secrets normal deps, which its own entry
licenses); the accurate law is the layer ladder, and the narrow claim holds for
ironclaw_wasm alone.

lanes.md + events.md: the 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement is superseded by docs/reborn/guidance-conventions.md — two files
restating one rule is the drift the guidance program removes.

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

* test(arch): govern the ProtocolAuthEvidence test seam — WS12 audit F1

Two new gates in reborn_sealed_evidence_mint_ratchet (closed paths #12/#13),
per the audit's remedy spec:
(a) TEST_SEAM_MINT_FNS governs test_verified/test_verified_for_tenant — any
    production-text call site outside ironclaw_host_api is an offender
    (comments/strings stripped, #[cfg(test)] blocks stripped, tests.rs /
    *_tests.rs and cfg-test-only files excluded via the shared census);
(b) test-support may appear in no normal dependency table workspace-wide
    (dependencies / build-dependencies / target.* variants /
    workspace.dependencies), and no [features] key other than test-support
    may forward to it — the laundering shape that would evade (b) by one
    rename. [dev-dependencies] enablement stays legal (cargo-features.md
    bar 4, the sanctioned dev seam).

Measured zero offenders on this tree in both directions before pinning;
sabotage-proven red->green both ways (planted production call named with
file:line-text; [dependencies] enablement named with its table path).
Self-tests drive the same pipelines the gates run (zero-match principle);
the definition-location and partition tests now cover the new table.

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

* test(integration): join the gsuite credential-injection journey — WS12 audit F3

WS12 row 5 leg 3 was verified in two halves no committed test joined: gsuite
handler -> staged credential (crate tier) and staged obligation -> wire
(GitHub/Slack only). Scenario 5 already drives gmail.list_messages through
production dispatch on a Google-OAuth-configured group; it now also asserts
the JOIN: the seeded google account's token (itest-google-token) lands on
the recorded outbound gmail.googleapis.com request as
'authorization: Bearer ...', injected at the host egress chokepoint
(apply_credential_injection) per the gmail manifest's declared recipe —
store -> dispatch-time staging -> chokepoint -> wire, through the caller.

Sabotage-proven: disabling the Header injection arm reds exactly this
scenario with 'no network egress request matching url gmail.googleapis.com
has header authorization' while the request itself still reaches the wire
(headers seen: content-type only) — the injection reason, not a setup
error; restore -> green.

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

* docs(target-arch): correct five measured dependency claims in families/domains.md

conversations does not depend on safety (its BoundaryRule now forbids it);
triggers depends on libsql_runtime + safety and NOT filesystem, so its
'filesystem-routed persistence path alongside SQL' is one path, not two;
memory's live set is host_api alone (prompt_envelope is allowlisted, unused);
auth was short by extension_contracts + product_contracts.

Each verified against the manifest before editing.

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

* docs(guidance): family AGENTS.md + crate READMEs + guidance consolidation for loop/product/app

Family-guidance program, families 8-10 (the top of the stack), per
docs/reborn/guidance-conventions.md:

- Rewrite crates/{loop,product,app}/AGENTS.md from routing stubs to the
  spec's family shape: exclusion lists, armed gates by test name, layer
  rows, crossing guides. Loop carries the trust story + the declared
  Loop*Port decorator chain; product carries the frozen-surface rule,
  the transports-consume-contracts rule (with the D-B frozen-constant
  qualification), the evidence-mint prohibition, and the two vendor
  exceptions; app carries the wires-owners-never-becomes-one charter,
  the binary-names-packages rule, config's zero-dep guarantee, and the
  composition mass ratchet (loc 40423 / Arc<dyn> 814).
- Add a README.md to all 13 crates (12 new; webui's rewritten to the
  spec shape) with measured public surface, deps, and consumer counts.
- Consolidate duplicate AGENTS.md/CLAUDE.md per spec rule 1: AGENTS.md
  is canonical and CLAUDE.md a pointer for agent_loop, loop_host,
  turn_runner, hooks, host_ingress, openai_compat, operator, and
  architecture_tests; CLAUDE.md stays canonical (module spec /
  gate-pinned) for webui, composition, and assistant, with
  composition's AGENTS.md reduced to the pointer.
- Fix stale references in owned paths: hooks' dependency diagram and
  AgentLoopDriver home (ironclaw_loop_contracts, not ironclaw_turns),
  loop_host/agent_loop port-home claims, turn_runner's pre-nearai#6696
  scheduler description, webui's ProductSurface path
  (product_contracts, not host_api), route count (93, measured), and
  webui's allowed-dependency list (7 of 10 were listed), the D-S
  await-edge ruling reflected in turn_runner guidance, composition's
  llm_admin residue (nearai_login_serve left for operator).

Verified: cargo test -p ironclaw_architecture_tests --no-fail-fast
(39 binaries, 0 failures — covers the CLI AGENTS.md phrase pin and the
composition guidance-markdown scan), scripts/ci/check-target-tree.py,
path-literal resolution over all 37 changed files, conflict-marker scan.

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

* docs(target-arch): record the closed scan evasions (F4) and the secrets-consumer correction (F2)

The sealed-mint census weaknesses PROPOSAL §11.2.5/§12.1a and CHECKLIST recorded
as live and owed to WS10 are all closed on this tree, verified by re-attacking
the seam with both evasions at once; the docs understated the seal. Ratchet is
23 tests. One residual replaces them: the test_verified test-seam constructors,
now pinned by two gates.

§12.1b's 'only products-layer crate with the edge' is false by one —
ironclaw_assistant carries ironclaw_secrets as port-declaration vocabulary with
no expose_secret call. Not a value-reach bypass; joins nearai#7095's inventory.

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

* docs(target-arch): correct the app-family layer, config's consumer set, and the webui route count

ironclaw_config declares layer=substrates while living in crates/app/;
its consumers include operator, extension_manager and extension_host, not just
the assembly crate and the binary; webui is 93 contract-locked routes, not 92
(nearai#6780 landed after the last recount).

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

* docs(stale-sweep): fix agent guidance outside crates/ for the family restructure

Audit-and-fix pass over every stale document outside crates/ (PR 2 of the
family-guidance program). Live guidance verified against the tree; records
kept with dated notes instead of rewrites.

Guidance fixes (verified against HEAD before writing):
- .claude/commands/trace.md: MCP tool prefix codebase-memory -> codebase-memory-mcp
  (allowed-tools never matched the real server), ProductSurface home ->
  ironclaw_product_contracts, capabilities host.rs -> host/ module split,
  scripts lane -> script-sandbox; deleted the redundant v1-anchors section.
- .claude/commands/add-sse-event.md: deleted the banner-quarantined v1 scaffold
  steps (every path deleted with the monolith); now an honest redirect to the
  Reborn projection/SSE path. Frontmatter no longer advertises a working scaffold.
- .claude/commands/deslop-reborn.md: three dead crates/*/Cargo.toml globs (family
  layout added a level), ls crates/ -> family-aware listing, v1-only consumer
  logic retired, per-crate --features integration phrasing.
- .claude/rules/type-placement.md: crates/*/src globs matched nothing; recipes
  re-pointed and numbers re-measured 2026-08 (3,495 structs/enums, 385 traits,
  fan-in host_api 53 / common 20 / turns 12).
- .claude/rules/skills.md: paths trigger pointed at a nonexistent
  bundled_skills.rs (rule never fired); SKILLS_REGEX_ACTIVATION_ENABLED /
  SKILLS_MAX_TOKENS env vars are read by nothing -> documented the real
  config-file setting and DEFAULT_MAX_SKILL_CONTEXT_TOKENS.
- .claude/rules/testing.md, ironclaw-reborn-testing skill, CONTRIBUTING.md,
  .github/pull_request_template.md, testing-playbook, deslop: the workspace-root
  `integration` feature is empty with zero consumers - all "cargo test
  --features integration" guidance re-pointed to crate-level suites.
- .claude/skills/reborn-extension-surfaces: four pre-colocation assets/ paths,
  CapabilitySurfaceKind home, conformance-suite move to
  ironclaw_extension_contracts, ingestion test move to the registry crate,
  gate-banned migration exemplar replaced with the live behavioral pin, [mcp]
  instead-of claim softened (nearai-mcp pins a static [[tools]]).
- .claude/skills/ironclaw-reborn-orientation: turn_runner labels, prompt-crate
  list re-derived (turns/first_party_extension_ports out; host_api,
  loop_contracts, assistant in), consumer-grep glob fixed.
- .claude/skills/reborn-feature + docs/reborn/how-to-port-channel-to-reborn.md:
  ProductSurface/ProductView/descriptors/caller types live in
  ironclaw_product_contracts; recipes re-pointed.
- CLAUDE.md: dead root --features integration line replaced; project tree
  redrawn with the ten families; trait homes corrected; ProviderId -> VendorId;
  CapabilitySurfaceKind + ChannelAdapter homes; [channel.config] ->
  [channel.connection]/[admin_configuration]; v1 Job State Machine section
  deleted (no such machine in Reborn); prompt-crates recipe fixed; MCP server
  name; LLM backend list re-derived from LlmBackendKind.
- docs/extensions/building-a-tool.md: product-adapter crates row -> channel
  surface model; package registration -> PACKAGES collector in
  ironclaw_extension_support (available_extensions.rs is being dissolved);
  hosted-MCP policy home -> ironclaw_extension_host/src/mcp.rs; dead v1 bullets
  dropped.
- docs/internal/mutation-audit.md: runnable command blocks re-pointed (family
  paths; ironclaw_dispatcher example replaced - crate deleted in WS0).
- docs/reborn/harness/e2e.md: dispatcher row -> the capabilities dispatch
  contract suites. docs/reborn/contracts/host-api.md: three ironclaw_dispatcher
  mentions -> capabilities dispatch module. standard-operations.md: renamed
  crate + arch-test package name.
- scripts: mutation-audit.sh usage header, check-hermetic-env.sh env_helpers
  pointer, check-generic-without-concrete.sh mirror pointer,
  telegram_smoke/README regression step (target deleted with v1 in nearai#6375).
- .env.example: dead SKILLS_REGEX_ACTIVATION_ENABLED entry -> config-file doc.
- docs/qa/telegram-coverage-map.md: nine not-automated reasons re-worded to the
  crate-level integration tier.

Records (dated notes, no rewrites): ADR 0003/0004 path notes (evidence pinned
to their measured SHA), FEATURE_PARITY state-migration paragraph marked
historical with a git-show recovery pointer, engine-v2 parity record's
"coexist on main" claim corrected with a historical note, subagent-spawn
legacy scope re-tensed.

Pre-family path reproduction count: 73 -> 70 files; every remaining file is a
dated record (docs/plans, docs/superpowers, ADRs, audits, CHANGELOG history,
historical-marked train docs) or a deliberate past-tense mention.

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

* docs(ws12): tick row 7 — the fresh-agent placement probe passed on the final tree

All three placements correct with high confidence, each naming the trait, the
tests, and the tempting wrong place it rejected. The probe doubled as a docs
audit and independently hit four defects, three of which the stacked guidance
PR fixes — it succeeded despite them.

WS12 is now 7/7. The restructure is complete.

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

* docs(target-arch): the product→loop_host recount was wrong on the day it was written

Eight importing files across four seams, not seven across three — the fourth
being a skill-activation-observer seam (projection.rs, projection/live_progress.rs)
this bullet never named, which §6.4.7's own same-day note already implied.
Surfaced by the plan-conformance audit.

The recount history is 3→5→6→7→8, wrong at four of five attempts. That retires
the prose count as a method: the sever slice should land an inventory ratchet
before or with the move, not another number.

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

* review(7263): CodeRabbit round-1 triage — 4 code fixes (2 sabotage-proven, 2 red-first) + 6 doc-truth corrections

Code, each verified red-first or by sabotage matrix:
- sealed-mint ratchet: per-name sighting floor for TEST_SEAM_MINT_FNS
  (closed path #12). Proven: renaming test_verified_for_tenant away plus one
  extra legitimate sibling mention passed the old aggregate floor (silent
  disarm) and fails the new per-name floor naming the constructor; suite
  23/23 after revert. (CodeRabbit's claimed baseline ">2 mentions today" is
  wrong — each name has exactly one kept sighting — but the doc/enforcement
  mismatch and at-threshold fragility were real.)
- trace credit: non-finite novelty_score/duplicate_score are treated as
  absent before clamping (clamp preserves NaN, which poisoned online_score
  and credit_points_estimate); NaN cases added to the nearai#7144 regression test,
  red first.
- trace submission: a 2xx whose body stream dies mid-read now maps through
  request_failed (network telemetry kind, true I/O cause) instead of
  collapsing to an empty body that the nearai#7144 strict parse misreported as
  response_invalid/Submission; truncated-body regression test, red first.
- Postgres contract suites (event store + assistant ledger): isolated-DB
  names now carry a creation epoch and the once-per-binary sweep is
  age-gated (1h), closing the cross-process window where a sibling's fresh
  zero-backend database (between CREATE DATABASE and first connection) was
  sweepable; legacy pid-scheme leftovers still collect immediately. Proven
  on live Postgres 16: planted stale name swept, planted fresh name
  survives, 13/13 x2 and 20/20 x2 with zero leftovers.

Docs (target-architecture truth pass):
- PROPOSAL section 9: the WS6 rename sweep (nearai#7152) had rewritten the source
  column of the 12 renamed rows to their post-rename names, turning their
  rename dispositions into no-ops (rows 13/14/28/30/49/51/59/61/64/66/67/70);
  pre-restructure names restored with a dated footnote.
- PROPOSAL:69: removed the superseded 3->5->6->7 recount sentence (the
  corrected 3->5->6->7->8 passage subsumes it).
- PROPOSAL row 34: ToolPermissionOverrideStorePort deletion marked landed
  (2026-08-05 WS8, matching section 6.5.3; zero workspace hits).
- CHECKLIST:631: dated note recording that the WS12 F3 gsuite join landed in
  this batch (scenario_uninstalled_tool_call_denied_until_active.rs asserts
  the seeded google token on the gmail.googleapis.com wire; suite run green).
- CHECKLIST:632: dated note spending F4 (the audit's 19 was correct at its
  SHA; the ratchet file now holds 23 tests, re-counted at lines 552/597).
- ws12-gauntlet-report P6 heading: first of TWO real failures (one class),
  matching P8 and the report's own summary.
- ws12-mapping-audit rows 49/137: dated D-S closure notes (await-edge store
  half = journal projection already; resolver retained loop-tier; no shed
  owed) so the backlog register no longer lists it as in-flight.

Not fixed, with evidence: the span-helper macros gate suggestion
(info_span!(target = ...) is a hard compile error, E0425 — no silent trap),
the webui tracing-subscriber workspace-dep suggestion (no
[workspace.dependencies] entry exists; suggestion would not build; 8
siblings use the identical direct shape), and the mapping-audit
regeneration (the audit is accurate at its pinned SHA; the in-batch F1 fix
is recorded in its dated coordinator note).

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

* review(7263): CodeRabbit round-2 — rejection-body read keeps its cause; 200 {} is not a submission acknowledgement; lanes.md family dep rule matches measured Cargo.tomls

- submission.rs non-2xx path: a failed rejection-body read no longer collapses
  to an empty detail via .unwrap_or_default() (banned by
  .claude/rules/error-handling.md); the read error folds into the
  http_rejection detail so the received status keeps driving the 401/403
  auth-retry and the Credential/HttpRejection telemetry split.
  Regression: submit_preserves_rejection_body_read_failure_cause_with_status.

- TraceSubmissionReceipt.status: serde default removed — it fabricated
  status "submitted" from a proxy's 200 {} (the nearai#7144 synthesis, resurfacing
  through the wire type's defaults), after which the flush caller recorded
  Submitted and deleted the only retryable queued copy. The acknowledgement is
  the server naming what happened to the submission — every workspace fixture
  sends status and callers persist it unconditionally as server_status — so a
  status-less 2xx body now fails the strict receipt parse as response_invalid.
  Regression: submit_rejects_success_response_without_explicit_server_status
  (covers 200 {} and a status-less non-empty object).

- docs(lanes.md): the family Dependency-direction rule no longer claims every
  lane takes the extension-surface vocabulary crate — measured across
  crates/lanes/*/Cargo.toml: mcp + sandbox hold ironclaw_extension_contracts
  under [dependencies], wasm only under [dev-dependencies]; dated ✎
  cross-references the ironclaw_wasm entry's 2026-08-05 correction.

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

* review(7263): CodeRabbit round-3 — shared Postgres test provisioner (the "new dep edge" premise measured false), entrypoint self-test armed (sabotage-proven), six doc self-contradictions reconciled

Code:
- ironclaw_filesystem gains a `postgres_isolation` test-support module — the
  single home of the per-test isolated-database scaffolding (once-per-binary
  age-gated stale sweep, epoch-in-name convention, DROP WITH (FORCE) cleanup),
  parameterised by suite/env-var/prefix/unreachable-policy. Zero new
  production edges: event_store already normal-deps filesystem, filesystem
  already owns tokio-postgres, and the dev-dep+feature pattern is the one 17
  crates already use. The event-store and product-workflow-ledger suites
  migrate onto it; both Postgres legs proven live against postgres:16 (12
  tests, zero leftover databases). The fabric original keeps its older
  variant with the differences documented at its IsolatedDatabase.
- ironclaw_event_store drops the duplicate tokio-postgres dev-dep (the normal
  dep already reaches tests).
- test-reborn-docker-entrypoint.sh: the missing-argv check now exits the
  command-substitution subshell instead of incrementing a counter the parent
  never sees — red-proven (a migrate-but-never-exec entrypoint passed with 7
  FAIL lines printed), green after the fix both sabotaged and restored.
- trace_commons submission test additionally pins !auth_rejection() for the
  503 rejection (the structural assert the API affords; the prescribed
  payload asserts are refuted — status is private and source is None by
  design, with the message derived from the structured status in the same
  constructor).

Docs (each reconciled to one canonical statement, measured):
- kernel.md: lease ownership decided from code — authorization stores,
  matches, and expires leases (CapabilityLeaseStore + port + expiry all live
  there); approvals constructs and issues into that store. The round-1
  re-homing of the spliced sentence into approvals was wrong and is corrected
  in the dated repair note.
- app.md: "nothing depends on app" scoped to the three app-layer crates;
  ironclaw_config's consumers restated by dependency kind (normal:
  composition, cli, operator, extension_host; dev-only: extension_manager,
  root integration-tests package).
- lanes.md: the mediated-services sentence now states the family law as
  layer-ladder + injected authority; the no-secrets/network/filesystem-dep
  claim is scoped to ironclaw_wasm, matching the file's own corrections.
- CHECKLIST 429/430: the one open traces clause is named (ScopedFilesystem
  adoption); the stale "other two" count corrected against the F3a strike.
- PROPOSAL:69 + CHECKLIST:72: the project-create route repointed —
  first_party_extension_ports dissolved into loop_host::skill_activation
  (WS8, §9 row 55) — still unattempted.
- PROPOSAL §9 rows 57/62 synced to §6.8.4 (telegram: dependency-set equality
  with Slack's four contract-tier crates) and §6.9.4 (webui -> assistant is a
  charter-permanent edge, §12.11 D-B).
- PLAN top summary records Wave 6's design question as resolved (D-S,
  2026-08-05).
- deploy-reborn-cli-docker.md: the two migration paragraphs unified on the
  entrypoint's actual behavior — only enabled = false beside
  signing_secret_env/bot_token_env is migrated; every other retired-key shape
  fails startup with the migration pointer.
- composition-budget.toml: the stale "2398 bp, a true ratchet" header
  replaced with the WS0-floor truth the baselines test asserts.

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

* docs: move the guidance convention into this PR so its citations resolve

families/lanes.md and families/events.md cite docs/reborn/guidance-conventions.md
when superseding their 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement, but the file was only on the stacked guidance branch — a forward
reference that dangles if this PR merges alone. The convention is the rule those
notes invoke, so it belongs with them.

Caught by the CodeRabbit round-3 pass.

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

* fix(ci): give the hoisted postgres provisioner its safety rationales

The round-3 hoist moved test provisioning into a production src/ path, so
check_no_panics flagged its four panic/expect sites and reddened Code Style via
fast-checks. The gate is right to flag them: it deliberately does NOT exempt
#[cfg(feature = "test-support")] modules, because a cargo feature is not a
privilege boundary in this workspace (PROPOSAL 12.1a proved exactly that) —
so a test-support module still compiles into a build where any sibling enables
the feature.

Suppressed with the gate's documented inline rationale, which must trail the
statement rather than precede it. The panics themselves stay: a configured but
unusable Postgres must fail the suite loudly rather than skip it, which is the
inert-guard rule the isolation fix exists to serve.

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

* fix(ci): classify the three planner-unknown paths this PR touches

The Reborn PR test planner fails closed on any unclassified path and
raises on the FIRST failure in sorted order, so CI only ever showed
.github/pull_request_template.md. Classifying that unmasked two more
paths in this PR's own diff: scripts/mutation-audit.sh and
scripts/telegram_smoke/README.md. All three are classified; the
fail-closed arm is untouched:

* .github/pull_request_template.md -> IGNORED_PREFIXES, beside its
  exact sibling .github/ISSUE_TEMPLATE/ (both GitHub UI templates;
  classify-test-scope.sh already pairs them in its docs-only arm).
* scripts/mutation-audit.sh -> PR_STATIC_CONTROL_PATHS, beside its
  self-test scripts/test-mutation-audit.sh; both run only in
  nightly-deep-ci.yml's mutation-frontier job.
* scripts/telegram_smoke/ -> QA_HARNESS_PREFIXES; a live, by-hand
  release smoke harness referenced by no workflow, same class as
  scripts/reborn_qa_matrix/.

Each entry is pinned red-first in test_reborn_pr_test_plan.py (entry
commented out, new assertion fails with the exact production error,
entry restored, green): a new PR-template test with paired
accept-AND-select-nothing assertions plus unknown-.github/-sibling
refusal probes, and the two existing class tests extended. Planner
self-test: 65 tests OK. The planner CLI over this PR's full 209-path
diff now exits 0.

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
…e 4 rows (nearai#7152)

* refactor(contracts): move extension runtime descriptors to a neutral contract (WS3)

Deletes the two `-> ironclaw_extensions` layer-matrix exceptions
(`ironclaw_mcp`, `ironclaw_scripts`) by giving the runtimes-layer lanes a
contracts home for the descriptors they read, instead of the registry crate
they may not depend on. Exceptions 13 -> 11; baseline lowered in the same
change.

Moved to `ironclaw_extension_contracts`:
- `runtime::{ExtensionRuntime, ExtensionAssetPath, ExtensionAssetPathError}`
- `hosted_mcp::{HostedMcpDiscoveredTool, HostedMcpDiscoveredToolAnnotations}`

`ExtensionPackage`/`ExtensionManifest` deliberately stay in
`ironclaw_extensions`: they carry the whole parsed manifest tree and a
`PackageRootBinding` typed on `ironclaw_filesystem::VirtualPath`, which the
§11.2.3 contracts-purity allowlist (`{ironclaw_host_api}` only) forbids the
contracts crate from naming. Measured instead: both lanes read exactly three
things off the package — `id`, `capabilities`, `manifest.runtime` — so the
lane request structs now take those three and the caller (which owns the
package) projects them.

Also repointed `ResourceReceipt` to its real owner: `ironclaw_resources`
only re-exports `ironclaw_host_api::resource::ResourceReceipt`, so the lanes'
import was a §11.2.4 two-import-paths hop, not a dependency.

No `pub use` shims (§11.3): every consumer is repointed in this change, and
`resolve_under` becomes the free function `ironclaw_extensions::resolve_asset_under`
because the orphan rule forbids an inherent impl on the moved type.

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

* refactor(sandbox): merge the sandbox lane into one crate (WS3)

Creates `ironclaw_sandbox` (runtimes) from the three halves of "run an
already-authorized command away from the host", and deletes the two crates
PROPOSAL §6.6.4 marks for merge:

- `ironclaw_process_sandbox` (plan contract)      -> `src/plan.rs`, `src/validation.rs`
- `ironclaw_host_runtime::sandbox_process`        -> `src/sandbox_process/**`
- `ironclaw_scripts` (script lane + Docker path)  -> `src/script.rs`

The kernel sheds the Docker/CA cone: `bollard`, `rcgen`, `x509-parser` and
`time` are gone from `ironclaw_host_runtime`'s manifest, and `bollard`/`rcgen`
are now declared by exactly one crate in the workspace.

Two migration details PROPOSAL §6.6.4 and CHECKLIST WS10 call load-bearing:
- `PROCESS_SANDBOX_CAPABILITY_ID` -> `ironclaw_host_api::capability`, so
  `ironclaw_loop_host` drops its lane dependency (production dep gone; a
  dev-dep remains for the tests that build plans).
- `SandboxCommandTransport` -> `ironclaw_host_api::process`, with the shapes
  it names (`CommandExecutionRequest`/`Output`, `RuntimeProcessError`,
  `SavedCommandOutput`, `SavedCommandOutputSanitization`). Without this the
  runtimes-layer lane could not implement what the kernel consumes.

Enumerating gates were repointed, never relaxed: the specificity carve-outs and
the struct/test-support ratchet entries moved with their files (both baselines
unchanged at 129 and their prior values), the panic-gate baseline row moved,
`reborn-crate-test-buckets.sh` registers the new crate, and the three
`reborn-e2e-rust.sh` script selectors follow the tests (plus `docker_security`,
which had no selector before).

One gate would have gone silently vacuous and was fixed rather than moved: the
script-lane surface scan in `reborn_dependency_boundaries.rs` read a hardcoded
`src/lib.rs`, which after the merge no longer holds the lane. It now scans the
whole crate source tree with a fatal-read walk and a non-vacuity assertion.

One deletion, recorded: `RebornScopedSandboxCommandTransport::into_process_port`
returned a kernel type a runtimes crate may not name. It had zero callers
workspace-wide; the kernel wraps the transport, which is the direction the port
inversion requires.

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

* docs(target-architecture): record the WS3 corrections with their evidence

Three dated amendments, each quoting the text it replaces:

1. CHECKLIST WS3 sandbox row + PROPOSAL §6.6.4 — "all pieces currently
   unwired/test-only" is REFUTED. Three production paths cross the merged
   crate (spawn-path plan validation, the process_executor routing check, and
   the saved-command-output scope digest). The accurate claim is narrower:
   no production *execution backend*. Behavior preservation is therefore
   argued at the diff (11 of 26 moved files byte-identical, 9 more differing
   by one import line, +63/-36 overall), not inferred from deadness.

2. CHECKLIST WS3 mcp row + PROPOSAL §6.6.3 — the prior wave's "structurally
   blocked" finding is half right, and the wrong half is load-bearing: only
   `ExtensionPackage` is un-absorbable, and no lane ever needed it (both read
   `id`, `capabilities`, `manifest.runtime` and nothing else). The registry
   half of the flip is done; the `resources` half is refuted as phrased —
   the estimate/usage vocabulary the row asks about is already in
   `host_api::resource` and already imported from there, while the real
   blocker is the `ResourceGovernor` authority port and `ResourceError`'s
   denial cone.

3. Recorded as a structural finding, not a note: the sandbox row and the mcp
   row are ONE problem. `ironclaw_scripts` imports the identical DTO set, so
   the merge alone deletes zero exceptions and only the mcp carve-out lets
   either lane shed the registry edge.

Also reconciled: PROPOSAL §6.1.2's as-built inventory gains the two modules
WS3 landed (and states why `ExtensionPackage` stayed); §2's package count
66 -> 65; the §9 disposition rows for `ironclaw_scripts`/`ironclaw_process_sandbox`/
`ironclaw_mcp`; the §11.2.2 ratchet rows (13 -> 11); the WS3 verify row; the
stale WS1.3 sentence asserting the blocker as settled fact; and
`reborn_restructure_baselines.rs`'s doc table, which still read 15.

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

* chore(sandbox): drop imports the merge left unused

`process_port.rs` no longer names `MountView` or `thiserror::Error` (both went
to `host_api::process` with the types that used them), and `sandbox_process.rs`
no longer needs `sync::Arc` after `into_process_port` was deleted. Found by
per-crate `clippy --all-targets --all-features -D warnings`.

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

* fix(ci): let the Reborn PR planner plan guidance edits and crate deletions

Three fail-closed gaps in `reborn_pr_test_plan.py`, all hit by this PR and all
live on `main` today — any PR with the same change shape is unplannable.

1. `.claude/**` was unclassified, so the planner refused outright. It is agent
   guidance in exactly the sense `docs/**` is human guidance: no Rust test
   reads either as data (the only in-tree references are prose citations in
   test doc comments). Added to `IGNORED_PREFIXES`. Without this, "guidance
   travels with the change" — the restructure's own discipline — cannot be
   satisfied in a single PR.

2. `crates/AGENTS.md`, `crates/README.md`, `crates/Architecture.md` raised
   "unmapped crate path": they sit under `crates/` but belong to no package.
   Now classified as crate-tree prose, matched by "Markdown no package
   directory owns" so a genuinely unmapped crate path is unaffected.

3. An unmapped crate path used to raise. `git diff` reports a deleted crate's
   old paths and CI feeds the planner that diff, so **every crate deletion or
   rename was unplannable** — including the six deletions PROPOSAL §2 plans.
   It now widens to the exhaustive plan. This is a semantic change and it is
   the safe direction: the full plan is a superset of any narrowing, so an
   unattributable path can never cause under-selection, whereas refusing to
   plan blocks the PR instead of protecting it. Malformed input is still
   rejected by the unclassified-path branch.

Each lands with fixtures per WS10's rule, positive and negative: guidance
paths select nothing while non-guidance paths still fail closed; crate-tree
prose selects nothing while crate *code* under the same unmapped directory
widens to `full` (so the Markdown carve-out cannot swallow code). The
pre-existing `test_unmapped_crate_path_fails_fast` is renamed and rewritten to
pin the new contract rather than deleted.

Verified against this PR's real 130-path diff: the planner returns `mode:
full`, and the workflow's own exhaustiveness guard passes on that output.

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

* fix(arch): give the retained resource exceptions an owning issue, not a wave

Review (#7065) caught that both surviving `-> ironclaw_resources` exceptions
declared `removes_in = "WS3"` — the wave this PR *is*, which does not remove
them. That is precisely the defect §11.2.2 already records against
`conversations -> turns` ("`removes_in = "WS5"` and WS5 has partly shipped
without it falling"), and it would have been repeated here.

Both now point at issue #7067, which owns the design work that actually clears
them: replacing the `ResourceGovernor` dependency with a narrow
reserve/reconcile/release port. The issue carries the measurements — 3 of 10
methods used, zero implementors, and the `ResourceError` denial cone — plus the
two open questions (error shape, port home) that make it a design slice rather
than a move.

An owning issue is also what §11.2.2 asks for and what the ratchet still cannot
enforce (there is no `owning_issue` field yet), so this is the strongest form
currently expressible.

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

* test(contracts): pin the asset-path validator that moved into extension_contracts

`validate_asset_path` moved here with `ExtensionAssetPath`, the type it
constructs. In `ironclaw_extensions` it was only ever reached indirectly
through manifest parsing, so its six rejection branches had no direct test —
and a contracts crate that carries validation owes that validation one.

Two tests: every reject branch with its exact reason and `Display` output
(empty, NUL/control, URL, absolute, Windows drive and backslash, and the
empty/`.`/`..` segment cases) plus the manifest-relative shapes that must keep
being accepted; and `ExtensionRuntime::kind()` over all five variants, since
that projection is what every lane uses to reject a runtime it does not serve.

Also removes a changed-line coverage risk this PR would otherwise carry into
the merge queue: the gate does not run on ordinary PRs (#7036), so ~100
newly-added lines of validator would first be measured where a failure is
expensive to diagnose.

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

* test(coverage): re-capture the host_runtime floor and floor the new sandbox lane

`RATCHET FAIL: ironclaw_host_runtime` — observed 18854 covered vs a
`floor_covered_lines` of 20538. This is the shrinkage case the ratchet's own
"To fix" text describes, not a coverage regression: `sandbox_process/**` moved
to `ironclaw_sandbox`, so the crate's denominator fell 23277 -> 21267 (-2010
instrumented lines) and its covered lines fell with it.

The percentage floor is **raised, not lowered**: observed 88.65% against an old
floor of 88.23%, so the entry now reads 88.65. Only the absolute line count
moves down, and it must — those lines are no longer in this crate.

To keep that from being a net loss of protection, `ironclaw_sandbox` is floored
on arrival at its observed 87.09% (3185 / 3657). This is a net *increase* in
ratchet coverage: neither `ironclaw_scripts` nor `ironclaw_process_sandbox` was
ever floored, and the `sandbox_process` half was protected only as part of
host_runtime's line count, which this PR necessarily reduces. Floored crates
16 -> 17.

Verified by replaying the ratchet arithmetic against CI's observed numbers:
both crates pass on percentage and on covered lines. Numbers taken from the
failing run's own report (job 91740733521), which is the authority for this
gate.

The `Tests (Reborn)` roll-up failed solely on this sub-job
("coverage-report result 'failure' did not match planned=true"); no other lane
failed — 50 pass, 2 fail, both this root cause and its roll-up.

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

* docs(target-architecture): record the coverage ratchet as a move-sensitive gate

WS3 hit a gate no move row had named. `tests/integration/coverage-floor.toml`
is keyed on crate identity plus absolute covered-line counts, so it is
invisible to WS10's path-keyed gate audit and yet it fails on every crate move,
merge, rename, or family `git mv` that shifts instrumented lines between
crates — as it did here, while the percentage floor was *improving*.

Recorded on WS10 with the three rules WS7 will need: re-capture in the same PR,
raise the percentage floor rather than leaving it, and floor the destination
crate or the move silently drops that code out of the ratchet.

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

* fix(extension-manager): repoint ironhub onto the moved ExtensionAssetPath

A semantic conflict the merge could not see: #6780 landed
`ironhub/{package,catalog}.rs` importing `ExtensionAssetPath` from
`ironclaw_extensions`, while this branch moved that type to
`ironclaw_extension_contracts::runtime`. Different files, so git auto-merged
cleanly and the breakage surfaced only at `cargo check`.

Repointed both sites to the contracts crate (no shim, per §11.3). The manifest
already named `ironclaw_extension_contracts`, so this is imports only.

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

* test(coverage): exempt the WS3 move's no-region lines and record the gate

The changed-lines coverage gate went red on four files while changed-line
coverage was 95.35% against a 90% floor: the failure was its two fail-closed
STRUCTURAL assertions, not any percentage.

Every line below was derived by replaying scripts/ci/reborn_changed_coverage.py
against this PR's own merged lcov (run 30831658659) with the base lcov the gate
itself resolved (run 30828540055 @ b89fcd3575), until the replay reproduced the
CI verdict byte-identically. Line numbers come from the gate's own
`candidate_lines - mechanically_uninstrumentable_lines()`, not from the log.

- host_api/src/process.rs (31 lines): new placement-neutral process vocabulary
  with no function body anywhere in the file; rustc emits no LCOV record for it
  at all. Same shape already exempted for product_contracts/loop_contracts.
- extension_contracts/src/hosted_mcp.rs (12): field declarations of the two new
  tools/list descriptor structs. The file is plainly instrumented (191 DA, 164
  hit), so this is a no-region artifact, not an instrumentation gap.
- host_runtime/src/services/runtime_adapters.rs (13): continuation lines of
  three rewritten calls, all PROVEN EXECUTING by their region-start heads
  (lines 380/434/977 score 24/16/63 hits). The four genuinely-uncovered lines
  in the same rewrite are deliberately NOT exempted -- the gate already
  subtracts them as pre-existing debt inherited from base.
- composition capability_host_tests/approval_gates.rs (6): type positions in a
  test double whose body region scores 1 hit.

The last one is a finding, not just a waiver: that file is 100% test code
behind `#[cfg(test)] mod capability_host_tests;`, but the gate's
test_only_path() recognises /tests/, /test_support/, */tests.rs and *_tests.rs
and NOT a cfg(test) module DIRECTORY, so it measures it as production. It is
the only such directory in crates/ today.

Docs (target-architecture, same PR per the docs-truth rule):
- CHECKLIST WS10 gains the changed-lines gate beside the ratchet row, cross-
  referencing the WS2.1 note rather than restating it: percentages are not what
  fail a move; derive lines by byte-identical replay (--fetch-base-coverage
  silently degrades without --github-repo); and a stranded exemption path is an
  ABORT with no verdict, not a loud failure.
- CHECKLIST WS10 exception-ratchet row: the constant was cited at :4063 and
  sits at :4164 -- corrected by removing the line pin, since the file is edited
  every wave. Records that the baseline is a UNION across parallel WS3 lanes.
- families/contracts.md: records extension_contracts' new ownership of the
  runtime descriptor vocabulary -- the carve-out that let BOTH lanes drop the
  registry edge -- and the orphan-rule seam that keeps resolve_asset_under in
  the registry crate.
- families/lanes.md: two "Never" claims were reading as satisfied when they are
  not. ironclaw_mcp's "never depends on the resource-governor crate directly"
  is refuted (the compiled edge survives; #7067 tracks the narrow port), and
  ironclaw_sandbox's "no direct process spawning outside the transport seam" is
  aspirational -- script.rs:454 still builds Command::new("docker").

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

* docs(sandbox,mcp): correct the wiring inventory and record the projection cost

Two review findings verified against the tree; three refuted with evidence in
the PR threads.

Valid — the sandbox wiring inventory was self-contradictory. `CLAUDE.md` said
"Two production call paths ... and both are plan validation" directly above a
list of THREE bullets, and `lib.rs` omitted the third entirely. The third is
real and is not validation: `host_runtime/src/process_output.rs:482` derives the
scoped saved-output directory through `RebornSandboxScopeKey::from_scope`. That
inventory is what tells a future agent which paths are live, so an undercount
invites deleting a production path as dead code. Both surfaces now say three and
no longer claim they are all plan validation (the `loop_host` capability-id
comparison never was either).

Valid, and recorded rather than redesigned — the registry carve-out cost a
type-level invariant. Replacing `package: &ExtensionPackage` with independent
`extension` / `capabilities` / `runtime` borrows is what deleted the
`mcp -> extensions` and `scripts -> extensions` exceptions, but it also means
the type no longer guarantees the three came from one package.
`execute_extension_json` re-checks the descriptor half
(`descriptor.provider == extension`); the runtime half cannot be re-derived,
because nothing in an `&ExtensionRuntime` names its owning extension. No caller
can trip it today -- there is exactly one production caller
(`runtime_adapters`) and it projects all three from one package in one
expression -- so this is a latent structural weakening, not a live defect.
Restoring the compile-time binding needs a sealed projection minted by the
package owner; a check inside the lane cannot express it, and re-taking the
registry edge would undo the carve-out. Both request types now carry the caller
obligation in their field docs.

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

* refactor(extensions): move the skill-install executor to extension_support (WS3)

WS3's first-party-tools row, family 1 of 6: skill management / URL install.

`skill_url_install.rs` and its `bundle`/`github`/`zip_bundle` submodules,
plus the install-input normalizer, move out of
`ironclaw_host_runtime::first_party_tools` into
`ironclaw_extension_support::skills::{url_install, resolve_install_input}`,
where the skill executor half already lived. Move-only: no behavior change,
no test edited for content.

`ironclaw_host_runtime -> ironclaw_skills` is deleted from
LAYER_MATRIX_EXCEPTIONS — the edge is gone, not waived (exceptions 13 -> 12,
WS0_LAYER_MATRIX_EXCEPTION_BASELINE drops with it). `ironclaw_skills` and
`zip` survive as dev-dependencies for host_runtime's own tests; dev edges are
outside the matrix by construction.

Two doc ambiguities are resolved in the same diff, as dated PROPOSAL
amendments quoting the text they replace:

- §6.8.4's "the builtin first-party tool handlers absorbed from
  host_runtime/first_party_tools" contradicted §8.2's "kernel: ✗ (ports only)"
  row and the enforced BoundaryRule. Resolution: the seam splits executor from
  adapter — the executor moves behind a neutral request/error pair, the
  FirstPartyCapabilityHandler / CapabilityManifest / registry wiring stay
  host-side. Same shape the groupware and web-access tools already ship.
- §8.2's "ports only" cell now says what it means: contracts-layer ports the
  kernel also consumes, not permission to name a kernel trait.

Two cost corrections recorded for the remaining families:
`host_runtime -> extension_support` is not divisible family-by-family (mod.rs
holds it via `extension_support::coding`), and
`host_runtime -> ironclaw_extensions` is not reachable by this row at all.

PATH_TERM_COLLISIONS shrinks by two: the installer's github carve-outs now sit
inside a scan-exempt crate.

Test accounting (un-masking discipline), unfiltered `--list` over both crates:
1398 -> 1398, with exactly two tests renamed by module path and none lost.

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

* docs(sandbox): record that the Docker fail-closed switch is wired to nothing

Review asked why the migrated docker_security test can pass with no daemon.
The skip is pre-existing (the file differs from its pre-merge original by one
import line); WS3 only enrolled it in the required Rust e2e lane, where it was
not run at all before.

The real defect the question surfaced is worse and also pre-existing: this
crate's tests/support/docker_gate.rs states that IRONCLAW_REQUIRE_DOCKER_TESTS=1
makes a missing daemon a hard failure and that "CI sets this" -- and nothing
sets it. Repo-wide the name occurs only in docker_gate.rs and
attribution_tests.rs, here and on main. So every real-Docker test in the crate
skips-and-passes everywhere, which is exactly the gap the gate's own comment
says let sandbox security bugs ship unnoticed. docker_security.rs additionally
open-codes its own check rather than using the gate, so it would stay fail-open
even once something did set the variable.

Recorded rather than fixed: setting the variable is a CI-behavior change that
would hard-fail any lane without a daemon or the ironclaw-worker image, which
is not verifiable from inside a move PR whose evidence claim is behavior
preservation. Filed as the #6945 guardrail-claim-vs-reality class with the
two-part fix stated.

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

* docs(host_runtime): record the executor/adapter seam in crate guidance

The crate's CLAUDE.md said "first-party runtime tools belong under
`first_party_tools/`" without saying that only the host half does. WS3 moves
each tool's executor into `ironclaw_extension_support`, which may not name this
crate, so the rule now names both halves and points at the skill-install family
as the worked example.

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

* refactor(host_runtime): keep the install-input error path log-free

The moved executor returns `SkillManagementCapabilityError`, and routing it
through `skill_management_error` would have added a `debug!` line to a path
that had none before the move. A move-only change must not add one, so the
install-input arm maps the kind directly and the `dispatch` arm keeps the
record it already had.

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

* ci(coverage): re-capture the host_runtime floor for the WS3 executor move

The ratchet does not run on `pull_request` (`reborn_pr_test_plan.py:21`; issue
#7036), so this PR's green checks were not evidence on this axis. A full-plan
`workflow_dispatch` run on this exact head reported:

  RATCHET FAIL: ironclaw_host_runtime
    observed: 88.59% (20485 / 23124 lines)
    floor:    88.23% ... floor_covered_lines: 20538 (effective floor 20518)

The percentage went UP while `floor_covered_lines` went DOWN — shedding
well-covered code lowers the absolute numerator, which is a separate assertion
from the percentage one. Re-captured to the observed numbers (floor raised
88.23 -> 88.59, not merely held). Verified locally against that run's own merged
lcov artifact: ENFORCING mode, 17 PASS / 0 FAIL, exit 0.

  run: https://github.com/nearai/ironclaw/actions/runs/30858257594
  head: e07b3b0299b0add11117e9591da71d46d7a7c832

The destination crate is deliberately not floored, because it cannot be: every
crate under `crates/extensions/` is invisible to the coverage tooling —
`reborn_coverage_lcov.py:19`'s CRATE_RE still requires a crate directory
directly under `crates/`, which #7037's colocation broke. Filed as #7083 with
the measurement; the global floor is left alone rather than re-captured onto
that hole.

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

* refactor(wasm): move wit/ inside its owning crate (Wave 3)

CHECKLIST WS4 + WS10 `wit/` rows. `wit/{tool,channel}.wit` moves from the
repo root to `crates/ironclaw_wasm/wit/` — the crate that owns the ABI —
per PROPOSAL §6.6.1. Behavior-free: same bytes, same generated bindings.

Wave-3 coordinates: the docs write the destination as
`crates/lanes/ironclaw_wasm/wit/`, but `crates/lanes/` does not exist until
WS7. Because the files now sit *inside* the crate, the WS7 family move
carries them with no further path edit anywhere — which is the whole point
of putting them there.

Ten wit-bindgen `path:` args repointed (the host plus nine guests: six under
`crates/extensions/packages/*/wasm-src/`, three under `test-tools/*/wasm-src/`
— the CHECKLIST row said six). All nine guests verified building against the
moved WIT on wasm32-wasip2.

The four `include_str!` readers of the ABI text do NOT get repointed
literals. Doing that would turn the two `ironclaw_host_runtime` sites from
repo-root reach-ins into *cross-crate* ones — §11.2.7's strict class, the
one WS2 turns into hard failures — taking the scan from 19 to 21 while
ticking a box that says "§11.2.7 scan passes". Instead the ABI text gets one
owner, `ironclaw_wasm::TOOL_WIT` (`src/config.rs`, beside `WIT_TOOL_VERSION`),
and all four sites read the const over cargo edges that already exist.
Measured with the scan: 133 -> 129 escaping sites, cross-crate 19 -> 19,
zero `wit/` entries remaining.

Path-keyed gates repointed: `scripts/check-version-bumps.sh` (both ABI
paths), `.githooks/pre-commit`, and `platform-and-compat.yml`'s
`has_direct_wasm_abi_risk` filter — where the bare `wit/` alternative is
*deleted* rather than rewritten, because the filter's existing
`crates/([^/]+/)*ironclaw_wasm/` alternative already matches both the
Wave-3 and the WS7 location. `scripts/ci/ws12_workflow_contracts.py`
anchored on that deleted string, so its anchor moves to
`build-wasm-extensions` and its in-scope probe now pins both locations.

`Dockerfile` loses two `COPY wit/ wit/` lines in the planner and builder
stages: both already run `COPY crates/ crates/`, so the files arrive with
the crate and the old line would COPY a path that no longer exists.

Docs: the WS4 row's `crates/lanes/wit/` destination was the only doc site
placing the directory beside the crate rather than inside it; corrected
there and in README's tree, with dated amendments in CHECKLIST, PROPOSAL
§6.6.1 and PLAN Wave 3 recording what the move found.

Test accounting (unfiltered `--list`, name-by-name, quiescent tree):
ironclaw_wasm 51 -> 51, ironclaw_host_runtime 1246 -> 1246,
ironclaw_architecture 198 -> 198. Zero diff, no test edited for content.

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

* build(wasm): rebuild first-party artifacts for the moved wit/ path

Forced by the previous commit, not incidental to it.
`scripts/ci/check-wasm-artifact-freshness.py` keys each package's committed
`wasm/<name>.wasm` to a digest of the `wasm-src/` tree that produced it, so
editing a guest's `wit_bindgen::generate!` `path:` — which the `wit/` move
requires in all six shipped guests — invalidates the recorded digest and
fails the gate.

The gate's own contract forbids the shortcut: "Re-record only after
`./scripts/build-wasm-extensions.sh --first-party` and committing the rebuilt
artifact — the digest asserts a claim about the artifact, and updating it
without rebuilding launders a stale one." So the artifacts are genuinely
rebuilt (`--first-party`, exit 0, 6 OK / 2 host-native SKIP), not re-recorded
in place.

Byte sizes move by more than the source change accounts for because these
builds are not reproducible by design — the guests pin no toolchain and
resolve their own `Cargo.lock` at build time, which is the documented reason
the gate hashes sources rather than artifact bytes.

Verified: `check-wasm-artifact-freshness.py` OK (6 packages), and
`cargo test -p ironclaw_extension_support` green (102/46/4) — that crate
`include_bytes!`s these artifacts, so it exercises the rebuilt components.

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

* docs(target-arch): record the WS7 artifact-rebuild cost of guest path edits

The `wit/` move had to rebuild six shipped WASM binaries because
`check-wasm-artifact-freshness.py` digests each guest's whole `wasm-src/`
tree. WS7 hits the same wall from the other direction: the six package
guests reach the ABI across two trees, so moving either `ironclaw_wasm` or
`extensions/packages` rewrites all six `path:` literals and forces the same
rebuild. Recorded on CHECKLIST WS10's `wit/` row (point 6), on the
loud-path-pattern row that owns the WS7 repoint (also corrected six -> nine
guests there), and on PLAN's Wave 5 block with the cheap mitigation: move
the two crates in one PR and pay it once.

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

* ci(planner): classify the path classes that blocked the wit/ move

`Detect Reborn test scope` exits 1 on any pull request whose diff holds a
path `reborn_pr_test_plan.py` has no rule for, which made this PR
unmergeable: it must edit `Dockerfile` (the moved directory's
`COPY wit/ wit/` no longer resolves) and `scripts/check-version-bumps.sh`
(the ABI gate would otherwise grep dead paths and silently stop
enforcing). 18 of its 46 paths were unclassified.

Same class as the `.claude/` gap #7064 fixed, and classified the same
way — one rule per class, recorded beside the constant:

  * `Dockerfile` / `.dockerignore` — `platform-and-compat.yml` keys
    `has_docker_risk` off exactly this pair and owns the image build.
  * `.githooks/**` — Code Style triggers on the tree and lints its
    contents (`test-ci-comm-locale-pin.sh`); no Reborn lane runs a hook.
  * `scripts/{build-wasm-extensions,check-version-bumps}.sh` —
    `platform-and-compat.yml`'s `has_direct_wasm_abi_risk` classifier
    both scopes and runs them.
  * markdown owned by no crate (`crates/AGENTS.md`,
    `test-tools/README.md`) — prose, like `docs/` and `.claude/`. A
    crate-resident doc still selects its own crate's lane.

The first-party extension package assets are deliberately NOT ignored.
`crates/extensions/packages/*/wasm/*.wasm` is a shipped artifact that
`ironclaw_extension_support` embeds with `include_bytes!`, and
`test-tools/*/manifest.toml` is `include_str!`d by
`ironclaw_extension_host`. Calling either prose would convert today's
loud failure into a silent under-schedule of a change to production
output — the WS10 failure mode. `EMBEDDED_ASSET_OWNERS` routes each tree
to the crate that compiles it instead, so this PR now additionally
schedules `ironclaw_extension_{support,host,manager}`: the crates that
consume the six rebuilt WASM artifacts.

Also fixes #7085 in a file this PR already touches. The WIT version
extractors used the GNU-only BRE `\+`, so on BSD sed (macOS) they matched
nothing, and because the `WIT_TOOL_VERSION` cross-check is guarded on a
non-empty version the hook printed "All version checks passed" having
compared nothing. `[[:space:]][[:space:]]*` is identical under GNU sed,
so the enforced Linux CI lane is unchanged; verified on BSD sed that both
`wit/tool.wit` (0.3.0) and `wit/channel.wit` (0.3.1) now extract.

Regression tests: every classified class gets a case in
`test_reborn_pr_test_plan.py`, including the paired assertion that the
embedded assets *select a lane* rather than merely being accepted (the
inverse of the `.claude/` prose test), and a staleness pin that fails if
an asset tree or its owning crate moves. All ten new cases fail against
the planner on `main`. `test_unclassified_build_input_fails_fast` moves
off `Dockerfile` onto a still-undecided input so the fail-closed arm
stays exercised.

Refs #7087, #7085

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

* refactor(host-runtime): split obligations into its three chartered owners (WS3)

`crates/ironclaw_host_runtime/src/obligations.rs` was 3,122 lines fusing the
three owners PROPOSAL §6.5.9 charters separately, held apart only by an
`// arch-exempt: large_file` waiver. It is now one module per owner:

- `obligations::handler` — which obligations apply and what each does
  before/after dispatch, plus the audit/redaction/ceiling/mount validation.
- `obligations::staged_handoffs` — material staged for a later consumer:
  the runtime-secret and network-policy stores and the credential-account
  resolver port.
- `obligations::process_store` — post-start handoff discard and reservation
  reconciliation.
- `obligations::mod` — only `BuiltinObligationServices`, the assembly seam,
  and deliberately the one place naming all three at once.

Every module is under the 1,500-line gate, so the waiver is deleted rather
than carried forward: re-fusing the owners now trips `pre-commit-safety.sh`.
`mod obligations;` stays private and the crate's `pub use obligations::{…}`
names are unchanged, so no consumer outside the crate sees this.

Behavior-free. Cross-owner access is `pub(super)` (three methods), not
`pub(crate)`. The split revealed one narrowing in the other direction:
`secret_present` was `pub(crate)` with no caller outside its own file and is
now private.

Also from the same CHECKLIST row, the bounded half of "shrink
`services/builder.rs` toward composition-facing factories": three builder
methods whose only callers are inside the crate's `src` narrow to
`pub(crate)`. The rest of that clause is measured and deferred in the
CHECKLIST amendment — 17 methods need a `test-support` cargo feature, three
are callerless and belong to WS8, and the remaining 33 are a redesign of the
fluent surface rather than a shrink of it. `+production_wiring` is refuted
there: it is readiness diagnostics, not assembly.

Two loud path-keyed gates fired and were repointed, not relaxed:
`reborn_host_runtime_services_do_not_expose_lower_substrate_handles` now
scans the whole `obligations/` directory and asserts it read ≥ 4 files
(`collect_runtime_rs` returns a count; both its callers now assert non-zero),
and `reborn_struct_test_support_ratchet`'s frozen per-file count moves to
`staged_handoffs.rs` with its count unchanged at 1.

Test accounting (un-masking discipline): `cargo test -p ironclaw_host_runtime
--all-targets -- --list` is 1,246 before and 1,246 after, name-by-name
identical — zero added, removed or renamed. `LAYER_MATRIX_EXCEPTIONS` is 10
before and after; an intra-crate split cannot move the register.

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

* refactor(operator,contracts): route operator secrets through a product_contracts port (WS3)

`ironclaw_operator` is a products-tier crate and held `ironclaw_secrets`, the
substrate that owns CAS one-shot leases, AAD/crypto and the OS keychain master
key. PROPOSAL §8.2's product row says the products tier loses that edge, and
§12.1b requires the port replacement to land before the edge is removed. Both
happen here, in that order.

- Port: `ironclaw_product_contracts::operator_secrets::OperatorSecretValueStore`.
- Implementor: `ironclaw_reborn_composition::RuntimeOperatorSecretValueStore`,
  the same placement as `OperatorStatusService` — assembly is the only layer
  that may name both a products-tier port and a substrate. Registered in
  `INVERTED_PORTS` beside it.
- `ironclaw_secrets` is gone from the operator manifest under every dependency
  kind, and `"ironclaw_secrets"` is now in the crate's `boundary_rules()`
  forbidden list. That gate's comment previously said the entry was
  deliberately absent because "the row owns it"; the row now owns it.

The port is deliberately narrower than the substrate, so this is a tightening
rather than a relocation: it takes no `ResourceScope` (the implementor fixes
the operator scope, where the caller used to pass one), exposes no
lease/consume protocol, and carries only a `&'static str` classification
instead of the substrate's error `Display` — asserted, including that the
backend message and the handle name are both absent from what crosses.

Two tests travelled with the behavior rather than being pointed at a fake:
`read_is_repeatable_across_reloads` (repeatability is a property of the lease
protocol) and the #4673 production-store reproduction (its value is wiring the
store exactly as production does, which now means the real store *behind the
adapter*). Two `FaultInjecting`-over-real-store fixtures became per-operation
port fakes, with the substrate error mapping re-pinned at the adapter; a third
assertion got stronger — batched-vs-N+1 stored-key lookup is now observed at
the port rather than by counting filesystem ops.

Test accounting: operator 154 -> 153, product_contracts 142 -> 143,
composition 937 -> 942 with zero removed; name-by-name diffs on a quiescent
tree.

Two findings the row could not have anticipated, both recorded in the
CHECKLIST amendment:

- The `webui` half of the row was already closed and was never a production
  edge. `ironclaw_secrets` has been a dev-dependency of `ironclaw_webui` since
  the commit that added it (#6619), both src mentions are `#[cfg(test)]`, and
  webui's boundary rule already forbade it.
- `ironclaw_extension_manager` (layer `products`) still holds a normal
  `ironclaw_secrets` edge in `admin_configuration.rs`. §8.2 covers it; the row
  does not, because the crate landed with WS2.4 after the row was written, and
  the substrate sits in the service's type parameters so it is not a
  like-for-like swap. Filed as #7095.

`LAYER_MATRIX_EXCEPTIONS` is 10 before and after: `products -> substrates` is
matrix-legal, so this edge was always an §8.2 rule and never a layer exception.

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

* test(sandbox): put the Docker security check behind the fail-closed gate

Review asked why the required Rust e2e lane can report `docker_security` as
passing with no daemon. Half of that is #7081 (nothing sets
IRONCLAW_REQUIRE_DOCKER_TESTS=1, so the switch is inert) and is not fixable
from here -- arming it hard-fails any lane lacking a daemon or the worker
image, which needs a runner guaranteed to have both.

The other half is fixable here and is fixed: docker_security.rs open-coded its
own `docker version` / `image inspect` checks with three bare `return`s, so it
sat entirely outside docker_gate and would have stayed fail-open even once
something did set the variable. It now takes both preconditions from
docker_gate::{docker_available, docker_image_available} and skips with the
visible `SKIP:` line that gate's module doc requires.

Measured, same machine, image absent:

  before, IRONCLAW_REQUIRE_DOCKER_TESTS=1 -> "skipping ..." / 1 passed
  after,  IRONCLAW_REQUIRE_DOCKER_TESTS=1 -> panic at docker_gate.rs:74 / FAILED
  after,  variable unset                  -> "SKIP: ..." / 1 passed

The third line is the no-op proof: the variable is set nowhere in this tree or
on main, so no lane's behavior changes today. The daemon-down path already
reached the image check and skipped there, so the outcome is identical; only
the branch it takes differs.

Two stale comments in docker_gate.rs corrected with it (they claimed
docker_security used its own gate, and that docker_image_available had no
consumer), and the crate's Known debt entry now splits the done half from the
#7081 half instead of describing both as open.

cargo test -p ironclaw_sandbox: 193 passed, 0 failed
cargo clippy -p ironclaw_sandbox --tests --all-features -- -D warnings: exit 0

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

* docs(reborn): stop calling the unwired script lane an execution lane

Two review findings, both correct, both artifacts of this PR's own renames.

1. engine-v2-to-reborn-parity.md note 4 read "a native script/software
   execution lane (`ironclaw_sandbox`, `RuntimeKind::Script`) sandboxed via
   `ironclaw_sandbox`" -- self-referential after the merge collapsed
   ironclaw_scripts and ironclaw_process_sandbox into one crate, and it
   contradicts note 5 four paragraphs down ("no production execution backend
   is wired for it"). Re-stated as the typed runtime contract it is, citing
   the measurement: `with_script_runtime` has zero production callers
   (`rg` finds only the builder itself, docs, and 30 test call sites).

2. CHECKLIST WS10 ratchet note 2 said "raise the percentage floor ...; only
   the line count should fall". That generalises WS3's sandbox merge, where
   observed coverage happened to rise. It is wrong as guidance for WS7, and
   the counterexample is in this same file: the 2026-08-03 entry from #7064
   records ironclaw_runner falling 85.55% -> 82.53% because the shed removed
   the crate's better-covered half, holding the floor, and RATCHET FAILing in
   the merge queue. Note 2 now says re-capture from the merged artifact, and
   lower only with that entry's move-not-regression counterfactual (add the
   moved files back, confirm the union clears the old floor, plus a zero-tests-
   lost name set-diff).

cargo test -p ironclaw_architecture: 32 targets, 206 passed, 0 failed

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

* fix(ci): pin the WIT scope probes and the embedded-asset owner pairing

Three review findings on the `wit/` move, each verified before it was acted on.

1. `ws12_workflow_contracts.py` probed `crates/ironclaw_wasm/wit/host.wit` and
   its nested twin. No `host.wit` exists in this repository — `git ls-files
   '*.wit'` returns only `tool.wit` and `channel.wit` — so both probes sat
   under the `crates/([^/]+/)*ironclaw_wasm/` alternative and re-asserted the
   crate-name term while saying nothing about the canonical ABI contracts. In
   a validator whose stated design is "probe derived from reality rather than
   from a guessed layout", a fabricated filename is a defect on its own terms.
   Replaced with a `crate_globs` entry, `("ironclaw_wasm", "wit/*.wit")`, which
   discovers the contracts on disk, requires each in scope, and synthesises the
   nested WS7 form — so a third contract, or the directory leaving the crate,
   fails the pin instead of passing on a stale name. Verified non-vacuous:
   narrowing the workflow alternative to `.../ironclaw_wasm/src/` now reports
   `tool.wit`, `channel.wit` and the nested probe as out of scope.

2. The embedded-asset routing test substituted `alpha`/`beta` owners so it
   could reuse the synthetic workspace. That exercised the real prefix strings
   through the real routing, but left the prefix->owner *pairing* — the table's
   entire semantic content — asserted nowhere: swapping
   `ironclaw_extension_support` and `ironclaw_extension_host` passed. Fixed in
   two halves. The routing test now drives the real `EMBEDDED_ASSET_OWNERS`
   against a workspace carrying the real owners' names and real manifest paths
   (the synthetic one could not: `build_plan` rejects a changed package outside
   the canonical set), asserting the real owner is selected. And the not-stale
   test now derives the same pairing from the tree instead of restating the
   constant: it resolves every literal `include_str!`/`include_bytes!` in every
   workspace crate through `crate_tree`, keeps the targets no crate owns — the
   ones that actually reach the table — and asserts that every crate compiling
   one of them is the routed owner or a dependent of it.

   That surfaced a property worth pinning: `crates/extensions/packages/` is
   embedded by four crates, not one. `ironclaw_extension_host`,
   `ironclaw_extension_manager` and `ironclaw_reborn_composition` reach into it
   alongside `ironclaw_extension_support`, and routing to the support crate
   covers them only because each depends on it. If that edge goes, a shipped
   artifact change stops scheduling a crate that embeds it — the silent
   under-schedule the table exists to prevent.

   Regression coverage verified red by sabotage, all three wrong tables:
   owners swapped (7 failures), `packages/` -> `ironclaw_llm` ("embeds nothing
   from it"), and the hardest case, `packages/` -> `ironclaw_reborn_composition`
   — a real embedder that the other embedders do not depend on
   ("...does not depend on..., so routing there never schedules it").

3. CHECKLIST WS10 claimed each of the nine `wit_bindgen` guest edits forces a
   committed WASM artifact rebuild. Only six do:
   `scripts/ci/check-wasm-artifact-freshness.py` scans
   `crates/extensions/packages/*/wasm-src` alone, `wasm-src-digests.toml` holds
   exactly six entries, and `git ls-files '*.wasm'` returns exactly those six.
   The three `test-tools/*/wasm-src/` guests commit no artifact; the tenth site
   is the host's `bindings.rs`, not a guest. Corrected, and the `wit/` row now
   states the boundary rather than implying it.

Guest paths, `wit/` contents and the six rebuilt artifacts are untouched.

Verified: `test_reborn_pr_test_plan.py` 46/46, `test_ws12_workflow_contracts.py`
25/25, `ws12_workflow_contracts.py` green on the real tree,
`cargo test -p ironclaw_architecture` 206/206 across 32 binaries.

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

* docs(host-runtime): state the obligation visibility rule as it holds

Review catch (#7090): the guardrail sentence promised "cross-owner access is
`pub(super)`, never `pub(crate)`", which is stronger than the code. Verified:
`RuntimeSecretInjectionStore::{insert, take, clone_material,
discard_for_capability}`, `NetworkObligationPolicyStore::{insert, get, take,
discard_for_capability}` and both constructors are `pub(crate)` and must stay
so — `src/egress/{mod,host_port,credential}.rs` call them, and that is
host-runtime composition outside `obligations/`.

The rule is restated as the property that actually holds: a method whose only
callers are inside `obligations/` is `pub(super)` (the three that are), and
`pub(crate)` is what the stores expose to the egress pipeline they exist to
serve. A future agent reading the old sentence would have read the existing
`pub(crate)` methods as violations.

Guidance-only; no code change.

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

* fix(architecture): put the operator secrets boundary entry on the right rule

Review catch (#7096), and it is the serious kind: the `"ironclaw_secrets"`
entry landed in `ironclaw_extension_contracts`'s forbidden vector, not
`ironclaw_operator`'s. The suite still passed, because `extension_contracts`
has no such dependency and `ironclaw_operator` then had no entry at all — so
the guard this row exists to add was inert, and a green architecture suite was
evidence of nothing. Reintroducing the edge would have passed every check.

Moved to `ironclaw_operator`'s vector; `extension_contracts` restored to its
`origin/main` content byte-for-byte.

Negative-probed rather than assumed. With `ironclaw_secrets` temporarily
re-added to `crates/ironclaw_operator/Cargo.toml`:

    reborn_crate_dependency_boundaries_hold ... FAILED
    ironclaw_operator must not have a normal dependency on ironclaw_secrets

and with the manifest restored, 35/35 pass.

Two further review findings, both verified before being accepted:

- `ironclaw_extension_manager` **does** have a `boundary_rules()` entry
  (`:3543-3556`, added with WS2.4). The CHECKLIST residue note and PROPOSAL
  §8.2's 2026-08-02 amendment both said it had none; §8.2's sentence is stale
  and is marked superseded. The real gap is narrower and now stated: the rule
  exists and simply does not forbid `ironclaw_secrets` (#7095).
- `ironclaw_product_contracts`'s guide claimed "twenty-four shipped modules".
  Measured: `src/lib.rs` has 26 shipped (27 `pub mod` less the gated
  `test_support`), and the table was missing `ironhub` **before** this branch
  touched it. Count corrected to twenty-six and the missing `ironhub` row
  added, so the inventory matches `lib.rs`.

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

* docs(sandbox): state the Docker-gate claim as the search that checks it

Review caught a false inventory in the Known debt entry, and the previous
commit is what made it false: "the name appears only in docker_gate.rs and
attribution_tests.rs" stopped holding the moment docker_security.rs gained a
module doc naming the variable, and CLAUDE.md itself was already a third
counterexample.

The narrower claim is the one that was always meant and is the one that
matters, so it now carries its own reproduction: no workflow, script, env file
or manifest mentions the name at all -- `git grep` over *.yml/*.yaml/*.sh/
*.toml/*.py/*.json/.env* is empty here and on main -- and the sole code
reference is a read, std::env::var(...) at docker_gate.rs:23. Every other
occurrence is a doc comment or a panic message.

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

* refactor(triggers,conversations): scan trusted trigger prompts at the mint (WS6)

PROPOSAL §6.4.2 asked for the trusted-trigger prompt safety scan to move
"behind the triggers/kernel seam it guards". It was not a module: it was
three lines inside `ConversationTrustedTriggerSubmitter::submit_trusted_trigger_fire`
— one of the two implementations of `ironclaw_triggers::TrustedTriggerFireSubmitter`
— holding its own `Arc<dyn InjectionScanner>` from `Sanitizer::new()`.

That placement is a fail-open: a guard that lives inside one implementation
of a port is lost the moment a second implementation exists, and nothing in
the tree forced a new submitter to re-run it.

The seam is `TrustedTriggerFireSubmitter`, whose only input is the sealed
`TrustedTriggerSubmitRequest`, which `ironclaw_triggers` is the sole minter
of. So the scan moved to the mint: `TrustedTriggerSubmitRequest::new` is now
fallible and calls the new `ironclaw_triggers::prompt_safety` first, making
"this prompt passed the trusted-prompt scan" an invariant of the type rather
than a step some submitter performs. `new_for_test` delegates to `new`, so
the test-support seal bypasses visibility only, never the scan.

Behaviour at the fire level is unchanged — same rejection point, same
`TriggerError::InvalidMaterialization`, same permanent disposition — and
composition's pre-materialization scan is untouched, so defence in depth
survives with the second scan relocated and now covering every submitter.

`ironclaw_conversations` drops `ironclaw_safety` entirely (the scan was its
only use). Enforcement: triggers' boundary rule stops forbidding
`ironclaw_safety` (a same-layer, I/O-free `substrates` leaf — a peer edge,
not a reach upward), and a NEW `BoundaryRule` for `ironclaw_conversations`
forbids it, plus `ironclaw_threads` (§6.4.2's "Never: transcript content"),
a crate that was unruled until now.

Regression coverage at the caller tier, not on the helper:
`tick_rejects_injection_prompt_before_any_trusted_submitter_is_reached`
drives the real `TriggerPollerWorker::tick_once` with a materializer that
does NOT scan and a submitter configured to accept, and asserts the
submitter is never reached. A companion pins that a medium-severity-only
prompt still submits, so the mint cannot drift into a blanket filter.

Tests: conversations 97 -> 97 (name-identical), triggers 169 -> 173
(+2 worker, +2 prompt_safety unit), architecture 206 -> 206.
LAYER_MATRIX_EXCEPTIONS unchanged at 10.

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

* fix(coverage): re-anchor the exemptions the merge shifted

tests/integration/changed-coverage-exemptions.toml is exact-line-keyed and
auto-merges silently. #7096's additions to ironclaw_reborn_composition moved
four entries' subject lines by +2 without anything flagging it; a stranded
entry makes the changed-coverage validator abort with no verdict at all.

Re-anchored by content (difflib line map from the #7065 tree, which the file
was validated against, to the union) rather than by arithmetic:
  runtime.rs [4068..4073, 4082, 4083] -> [4070..4075, 4084, 4085]
  runtime.rs [3701] -> [3703] ; runtime.rs [3433] -> [3435]
  lib.rs     [616]  -> [618]
All 142 entries / 1124 line references re-verified against the merged tree:
0 drift, 0 out-of-bounds, 0 missing paths.

* refactor(layers): re-layer processes -> kernel and skills -> substrates (WS3/WS4)

Two CHECKLIST rows, both of which were a one-line manifest correction rather
than a code move: the family docs already placed both crates where the rows
want them and only `Cargo.toml`'s `layer =` disagreed.

processes -> kernel (WS3). families/kernel.md already lists ironclaw_processes
among the kernel crates. The re-layer makes processes -> resources a
kernel -> kernel edge, so its LAYER_MATRIX_EXCEPTION went STALE and the gate
said so itself:

  Stale IronClaw crate layer matrix exceptions:
  ironclaw_processes -> ironclaw_resources from 2026-07-09 should be removed
  in W7: runtime process management still depends on resource contracts
  currently classed with kernel behavior

That is the gate's verdict, not a judgement call - deleting the entry is the
only way to make it pass. Baseline 5 -> 4, recomputed as len(merged list).
Checked the direction both ways: all nine crates that take a normal dependency
on processes (capabilities, turns, host_runtime, extension_host, loop_host,
extension_manager, runner, reborn_composition, stress) are kernel or above, so
the move legalizes an edge without forbidding an existing one.

skills -> substrates (WS4 SS3.D). families/domains.md already lists
ironclaw_skills under 'Layer(s): substrates'. Its only two normal dependencies
are ironclaw_filesystem (substrates) and ironclaw_host_api (contracts), both
at or below substrates, and its six consumers are all loops or above. No
exception moves in either direction.

cargo test -p ironclaw_architecture: 206 passed, 0 failed.
cargo check --workspace --all-targets: clean.

* docs(target-arch): close the WS3/WS4 rows this work satisfies, with evidence

Every tick was verified against the merged tree, never against a PR title.

TICKED:
- sandbox lane merge: ironclaw_sandbox exists, ironclaw_scripts and
  ironclaw_process_sandbox absent, bollard/rcgen declared by exactly one
  manifest in the workspace.
- mcp drops the registry dep: ironclaw_extensions is [dev-dependencies] only,
  0 production ironclaw_extensions:: refs in src/.
- skills -> substrates: landed here.
- hooks libSQL/Postgres [decision]: ADR recorded - keep both, with the four
  rejected alternatives and the evidence they are already converged on one
  trait plus a shared conformance suite. #6945 read first as the row demands,
  and explicitly NOT discharged: this PR changes nothing in the dispatch path.
- WS3 verify row: the row conflated Wave 3 with Wave 5 work (9 of its 10
  exceptions carried removes_in = W7). Corrected with the replaced text
  quoted, the Wave-3 half satisfied edge by edge, and the Wave-5 remainder
  named with its owning field value. Ticked on the corrected condition.

LEFT OPEN OR PARTIAL, each with measurements rather than a hand-wave:
- first_party_tools: 1 of 6 families moved; 15 modules still in host_runtime.
  Ticking would be false.
- processes/capabilities row: re-layer DONE; the capabilities/host.rs split is
  deferred with every module boundary already computed (4,560 lines, the six
  workflow ranges, and the arch-exempt waiver that must be deleted with it).
- host_runtime binding/catalog-defaults: binding half REFUTED (moving it needs
  RuntimeLaneExecutor/RuntimeLaneRequest made pub, contradicting the same
  section's Keeps clause; zero external references to either). Catalog half
  cannot go to extension_host at all - host_runtime is itself a production
  consumer at memory_native_extension.rs:96,101, so the move is a
  kernel -> products edge and a Cargo cycle. Correct destination is downward.
- network test_rewrite: NOT executed. Recorded the security shape (production
  binaries compile the seam and honour the rewrite env var at runtime) and the
  full 6-step plan, because the env var is how the entire E2E suite redirects
  vendor traffic through the production binary and the change needs feature
  forwarding into CI lanes I cannot verify here.

cargo test -p ironclaw_architecture: 206 passed, 0 failed.

* refactor(traces): drop the boundary-laundering re-export modules (WS6)

PROPOSAL §6.4.14: "drop the boundary-laundering re-export modules
(`recording`, `paths`) — consumers import the owners".

`ironclaw_reborn_traces::{recording, paths}` were two `pub use <other
crate>::*` passthroughs whose own doc comments stated their purpose
plainly: "so reborn-cli does not need a direct `ironclaw_llm`
dependency, preserving the architectural boundary". They preserved
nothing — the edge existed either way; the wildcard only hid which crate
owned the type, so the dependency graph read as a lie.

All three call sites were in `ironclaw_reborn_cli`. Note the literal
reading of "consumers import the owners" is not available here: the CLI's
dependency allowlist (`reborn_cli_binary_crate_stays_separate_from_v1_root`)
deliberately excludes `ironclaw_llm`, so importing the owner would have
traded a laundered re-export for a breached, tested boundary. Satisfied
instead by giving the owning crate the operation, which is what the
laundering was standing in for:

- `onboarding::onboard_instance(invite, consents)` — resolves the
  contribution root itself. Path layout under the base dir is this
  crate's own knowledge; the CLI no longer needs base-dir vocabulary.
- `TraceClientHost::build_envelope_from_recorded_trace_json(json, opts)`
  — parses `ironclaw_llm::recording::TraceFile` inside the crate that
  already depends on `ironclaw_llm`. The CLI hands over raw JSON.
- the CLI's private `trace_contribution_dir()` now delegates to
  `contribution::trace_contribution_dir_for_scope(None)` instead of
  re-deriving `<base>/trace_contributions`. Verified byte-identical:
  `trace_contribution_dir_for_scope(None)` is
  `trace_contribution_dir_for_scope_at(&ironclaw_base_dir(), None)`,
  whose `None` arm returns `base.join("trace_contributions")`.

No dependency was added to any crate. Semantics unchanged.

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

* refactor(llm): make providers.json a crate asset with a boundary rule (WS6)

CHECKLIST WS6: "`llm` `providers.json` becomes a crate asset/composition
input + boundary rule added".

The provider catalog sat at the **repository root**. A root-level data
file has no owning crate, so no boundary rule could govern who edits it,
and every consumer compiled it in behind Cargo's back with an escaping
`include_str!` — the "repo-root asset reach-in" shape §11.2.7's scanner
inventories. `git mv`'d to `crates/ironclaw_llm/assets/providers.json`
and the 20 `include_str!("../../../providers.json")` sites in
`registry.rs` become in-crate `../assets/providers.json`.

⚠ Correcting the row's inherited premise: a prior lane recorded the
"load-bearing include site is in `ironclaw_reborn_cli`" and judged the
item "needs a new mechanism, not a new path". Measured on main: the
load-bearing site is `crates/ironclaw_llm/src/registry.rs:383`
(`builtin_provider_definitions`), inside the owning crate. No new
mechanism was needed — only the path.

**Path-keyed gates rewritten in the same commit** (WS10: these fail
*silently* under a move):
- `Dockerfile` — both `COPY providers.json providers.json` lines deleted;
  `COPY crates/ crates/` already covers the new location in both stages.
  Verified by `scripts/ci/check-include-str-paths.sh` (OK, 119 refs).
- `.github/workflows/reborn-e2e.yml` — the literal `providers.json` path
  filter and its regex alternative removed; the depth-independent
  `crates/**` entry already matches. `ws12_workflow_contracts.py` passes.
- `scripts/ci/classify-test-scope.sh` — kept at its **shared** (both
  lanes) classification under the new path rather than letting it fall
  through to crate scope, so CI breadth does not silently narrow; the
  now-redundant entry in the reborn-only branch is dropped.

**The one consumer that could not simply be repointed.** The CLI's
`default_llm_consts_match_the_real_providers_json_nearai_entry` embedded
the catalog from five directories up to check its mirrored `DEFAULT_LLM_*`
constants. Repointing it would have turned a repo-root reach-in into a
*cross-crate* reach-in — the category §11.2.7 turns into a hard failure —
and the CLI may not depend on `ironclaw_llm`. A cross-crate consistency
rule belongs in the cross-crate suite, so the assertions moved into
`ironclaw_architecture` and read both files from disk at runtime, needing
no compile-time coupling at all.

Test accounting: `ironclaw_reborn_cli` config-init tests 2 -> 1; the
removed one is reborn as `reborn_provider_catalog_is_owned_by_its_crate`
in `reborn_dependency_boundaries.rs`, strictly stronger (it also pins the
asset's location, the repo root's emptiness, and single-embedder
ownership). Net test count +0.

**The new rule is sabotage-tested** — five cases, each red with the right
message, each restored to green:
1. catalog copied back to the repo root -> "must not sit at the
   repository root"
2. a foreign crate `include_str!`s it -> names the offending file
3. catalog `default_model` drifts from the CLI mirror -> names the const,
   the field and both files
4. walker pointed at a non-existent dir -> "walked only 0 Rust files ...
   would pass no matter what the tree contained" (reachability)
5. mirrored const renamed -> "no longer declared as a plain const ...
   update the extraction rather than deleting the drift check"

Case 2 caught a real false positive in the first draft of the guard: a
file-level `include_str!` AND `providers.json` conjunction flagged
`cli/tests/smoke.rs`, which names the *runtime*
`$IRONCLAW_REBORN_HOME/providers.json` and separately embeds something
else. The matcher now inspects the macro argument, not the file.

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

* ci(coverage): recapture the two composed floors from a real measurement

The provisional values were arithmetic - the sum of the two slices' recorded
deltas - and the dispatch caught them, which is the whole reason the brief
demanded a measurement rather than a reconciliation.

Dispatch run 30907774036 at 4512e03e28f1df15b419d2e36f9f38f8f55d62fd:
26 success / 1 skipped / 2 failure, judged by per-job tally per #6978. The one
skip is the pull_request-gated mutation gate; the two failures are the coverage
report and the roll-up it drags down, i.e. this file doing its job.

ironclaw_host_runtime: predicted 89.05% (18801 / 21114), MEASURED 88.63%
(17562 / 19814). The composition was wrong by 1300 denominator lines because
both slices measured their delta under the pre-#7083 aggregator, which could
not see crates/extensions/** at all - lines leaving host_runtime for
extension_support vanished from the tree it could measure, so neither branch's
recorded delta describes the post-#7094 world.

ironclaw_extension_support: MEASURED 75.31% (7142 / 9484) against #7094's
82.64% (6826 / 8260), captured before #7080's executor lines arrived.
floor_percent FALLS 7.33pp and that is flagged in the file for an owner's eye
rather than written quietly. Evidence it is composition and not lost tests:
floor_covered_lines RISES 6826 -> 7142, so the crate is protected by more
absolute lines than before, and #7080's un-masking accounting was 1398 -> 1398
with zero test names lost. Same shape as #7094's own ironclaw_runner recapture.

ironclaw_sandbox passed unchanged at its arrival capture (87.09%, 3185 / 3657).
The [global] entry is untouched: both moves are crate-to-crate inside the set
the fixed aggregator sees.

* docs(skills): rewrite the stale v1 lib.rs charter note (WS6)

CHECKLIST WS6 domain-internal cleanups: "`skills` stale v1 lib.rs doc
rewritten".

The crate doc claimed "In v1, trust-based tool filtering happens via
`src/skills/attenuation.rs`. In v2, the Python orchestrator handles trust
labels and the policy engine controls tool access via capability leases."
Both halves are dead vocabulary: there is no `src/` monolith on this tree
and no Python orchestrator anywhere in Reborn.

Replaced with what is true and checkable — this crate owns the trust
*label* and none of its enforcement; the ceiling is applied at the
capability tier (`host_api` capability/invocation attenuation via
`first_party_extension_ports`' activation and execution paths) and the
decision belongs to `ironclaw_authorization`. Also points at the existing
`SkillTrust` `Ord` safety note, which the old text left unconnected.

Doc-only; no code change.

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

* fix(network): compile the test rewrite seam out of production builds (WS3)

Closes the WS3 network row. Also RETRACTS an overstatement I made in this
row's earlier annotation.

CORRECTION FIRST. The earlier note claimed production binaries compile the
seam and honour IRONCLAW_REBORN_TEST_HTTP_REWRITE…
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…2 100% gate (nearai#7263)

* docs(target-arch): resolve the await-edge design question by measurement (D-S) and re-walk the WS9 verify row

Appends §12.13 D-S under delegated authority at owner direction, flagged
for post-hoc review by Illia Polosukhin (nearai#6696's author): the await-edge
store is measured to be a pure projection over ProcessDependencyPort
(that half of the shed happened inside nearai#6696 itself), and the resolver
is a genuine loop-tier responsibility journal edges cannot express
(owner recovery, sanitized transcript result materialization, batch-gate
resume-once drain, BlockedDependentRunGate resume policy). §6.7.3 is
amended (scheduler DONE / store DONE / resolver KEEP) instead of the
shed being executed; the 2.9k figure is corrected to 1,459 production +
1,448 cfg(test) lines. The §12.10 bullet, §2 divergence flag, §9 row 49,
§13 validation row, CHECKLIST header/WS4 pointer, README and PLAN all
carry the dated resolution.

WS9 verify row ticked with evidence: one lifecycle authority (the
process journal; TurnRunState/TurnRunRecord are projections via
AgentTurnProcessRuntime, ProcessRecord is a capability-invocation view,
no bare RunRecord exists) and §7 T4 re-walked clause-by-clause against
merged code — matches, including the checkpoint-gated no-auto-retry
mechanism (BeforeModel precedes ModelStage; requeue only when
checkpoint-free under the 3-claim cap).

Docs-only; no code, no tests, no gates touched.

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

* docs(ws12): rows 1-2 — package-set tick (64==64/1/0, gate+selftest+independent rederivation) and the 74-row §9 mapping audit (45 L / 15 L-A / 14 OBD / 0 NOT-LANDED; 3 findings recorded)

Row 1: check-target-tree.py reports 64 workspace members == 64 documented
packages, 1 documented exclusion (tools/ironclaw_silk_decoder), 0 owned
exceptions (EXCEPTIONS table empty — §5 steady state); self-test 17/17;
cargo-metadata name set diffed empty against an independent §5 parse.

Row 2: docs/reborn/target-architecture/ws12-mapping-audit.md is the audit
record — per-row executed-evidence, delete-clauses read against WS8's
execution notes, all 14 open rows cite their owning CHECKLIST/PROPOSAL
row or issue. Findings (recorded, not fixed): F1 prompt_envelope
manifest-description fix has no owner row; F2 WS6:429's 'nearai#5618 residue
deleted' overstates vs the live adopt_migrated_identity + open WS8:523;
F3 stale-docs cluster where the tree is ahead of the prose (trace
re-export drop, TurnRunTransitionPort, processes->resources).

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

* fold(7154): squash-port fix/red-main-7119 onto family-world main — defect train nearai#7146/nearai#7115/nearai#7104/nearai#7103/nearai#7144 (+nearai#7119 CI lane), 34-hunk contribution.rs port into the split modules, planner entrypoint classification, D-R loopback exception on the widened HTTPS credential guard

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

* test(extractors): issue-number + assertion-rationale doc refinement (rescued 844964f from rescue/7154-parked-guard)

Ports only the doc/assertion refinement commit; the guard-parking commit
e8f5a31 on that branch is deliberately NOT taken — superseded by the
D-R loopback ruling.

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

* docs(target-arch): record D-R — the loopback credential-guard ruling, wiring choice, and regression pins (PROPOSAL §12.13, 2026-08-05)

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

* review(7154): CodeRabbit round-1 triage — fail-closed tracing-target scan traversal (+node_modules), bounded sidecar output draining (capped capture + discard drain), deadlock regression asserts successful redaction (no seq), XLSX/DOCX empty-classification via extract_document, raise_for_status annotations

Threads already addressed by the fold: latency.rs caller-contract wording
(merged doc scopes the requirement to latency-trace callers), BodyJsonPointer
coverage (the plaintext-refusal test drives all four injection shapes).
Deliberately not taken: un-xfailing the four Slack-catalog projections —
the xfail is a documented tripwire (unexpected-pass goes red) and clearing
them is the nearai#6520 projection-modeling follow-on its comment specs.

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

* fix(assistant): re-point the one field-form tracing target the nearai#7146 gate caught — main's relocated triggered_run_delivery_services carried the drift the PR fixed at its old channel_host address

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

* closure fixups: execute the mapping-audit findings — prompt_envelope manifest description (F1), dated ✎ corrections for the nearai#5618 overstatement (F2) and the stale-prose cluster (F3)

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

* docs(ws12): second-reviewer security spot-audit + extension-journey re-verification (rows 5-6)

Adversarial second-reviewer pass over PROPOSAL §12.1a/b/c and the batch's own
§12.13 D-R loopback carve-out, plus a re-run of the five extension journeys.
Attacks were executed rather than argued: two sabotage files and a 38-shape
hostile-URL probe were planted, run, and reverted.

Verdicts — mint consolidation HOLDS-WITH-RESIDUAL, secrets tightening
HOLDS-WITH-RESIDUAL, host/verifier colocation HOLDS, D-R HOLDS. No HOLE.

Four findings recorded rather than fixed (report-not-repair):
- F1 test_verified/_for_tenant are ungranted mint constructors gated only by
  the `test-support` feature, in no mint-name table, with nothing pinning the
  feature to [dev-dependencies]; the shipped binary is measured feature-free.
- F2 §12.1b's products-layer residue undercounts by one (ironclaw_assistant).
- F3 journey coverage hole: gsuite-with-credential-injection is proven in two
  halves that no committed test joins.
- F4 both recorded census evasions and both fail-open reads are CLOSED on this
  tree, so §11.2.5/§12.1a/CHECKLIST:552/:597 now understate the seal.

Rows 5-6 ticked; only lines 631-632 of CHECKLIST touched so the concurrent
rows 3-4 edit folds cleanly.

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

* ratchet(closure): lock the budget gate at the program's end state

Dispatch ceiling 1122 -> 814 (today's observed, nudge taken; WS0 record 827
stays within effective 829). Mass-share ceiling 2398 -> 658 bp (the WS0
baseline floor — the arch-test assert refuses lower, and observed 578 bp sits
inside the nudge window). Absolute LOC re-equalized at 40423: nearai#6831 added 4
governed LOC through the queue's tolerance window; ceiling, observed, and
COMPOSITION_ABSOLUTE_SRC_LOC move together here. Both tightenings
sabotage-verified red (dispatch 9-over at 790; abs 73-over at 40200).

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

* docs(ws12): gauntlet report — row 3 ticked (full gauntlet green, 0 REAL in scope), row 4 verified-but-open on two pre-existing Postgres-leg test-isolation defects

WS12 rows 3-4 verification on the assembled batch tip 0c6c0cf:

Row 3 (ticked): fmt, clippy default/all-features/--lib --bins, workspace
tests (495 targets, 15,203 passed, 0 failed; the smoke.rs:3132
CPU-saturation flake passed first try), arch suite 285/0, the
integration-feature lane 1,665/0, recorded-fixture QA (61 fixtures clean,
41/0), frontend (typecheck 1,588 files; vitest 1,088/0; build + bundle
budgets), e2e smoke = the CI browser lane under the hermetic wrapper
(50 + 21 + 5 passed), and all 41 scripts/ci self-tests (two mapfile/bash-3.2
casualties green under bash 5, the CI shape).

Row 4 (stays open, dated note added): both-backend parity proven with
legs demonstrably executed for the fabric (57 pg + 81 libsql), triggers
(ADR 0003, REQUIRE_POSTGRES), hooks (ADR 0004, all three backends),
composition, processes journal, extension-registry, host-runtime libSQL
restart, and the backend matrix; fabric-delegated domains enumerated.
Two REAL blockers (one class): the Postgres legs of the event-store and
assistant-ledger contract suites assert against shared-database state and
cannot pass as-written (each failing test passes alone on a virgin
database; files byte-identical to origin/main; no CI lane sets their env
vars). Full evidence: docs/reborn/target-architecture/ws12-gauntlet-report.md.

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

* fix(tests): per-test isolated Postgres databases for the two WS12 parity-blocking contract suites

The WS12 gauntlet (ws12-gauntlet-report.md §P6/§P8) measured the Postgres
legs of ironclaw_event_store's durable_event_store_contract and
ironclaw_assistant's durable_ledger_contract as test-isolation-defective:
absolute database-global asserts (event cursors; settled-entry prune
bookkeeping) run against the single external database named by their
IRONCLAW_*_POSTGRES_URL env vars. Every failing test passes alone on a
virgin database - store semantics correct, suites not self-isolating
(PROPOSAL §12.13 D-T).

Fix: each affected test provisions a private database on the configured
server - the fabric contract's IsolatedDatabase pattern
(db_root_filesystem_contract.rs) ported locally into each suite: CREATE
DATABASE per test, store/pool + migrations against it, courtesy
DROP ... WITH (FORCE), and a once-per-binary stale-name sweep. Every
assertion preserved byte-identical; libsql/jsonl twins untouched. In the
ledger suite only the two retention tests move - the other six Postgres
tests keep their proven fingerprint-suffix isolation.

Regression pins are the fixed tests themselves:
- postgres_replay_advances_next_cursor_past_trailing_filtered_records
- postgres_runtime_and_audit_logs_survive_rebuild_with_filtered_cursor_semantics
- postgres_settled_entry_limit_prunes_oldest_when_configured
- postgres_settled_prune_interval_defers_until_interval_when_configured
Green proven on a shared dirty database twice in a row (parallel default
threading) and serially on a virgin database; red-first reproduction
captured before the fix.

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

* docs(reborn): record §12.13 D-T (parity-suite isolation ruling) and close CHECKLIST WS12 row 4

D-T (after D-S): the WS12 gauntlet's two REAL findings were one defect
class — absolute database-global asserts against the single shared
env-var Postgres database — in two suites (event store cursor contract,
assistant settled-ledger retention). Ruling executed in commit 864d93e:
per-test isolated databases via the fabric contract's IsolatedDatabase
pattern, assertions preserved; alternatives (baseline-relative asserts,
serial-only, leave-open) recorded with why they lost; regression pin =
the four fixed tests themselves.

CHECKLIST WS12 backend-parity row ticks [x] with a dated addendum: red-first
reproduction, the three green isolation runs (dirty shared DB twice in
parallel; failing pairs serial on virgin), parity now green 10/10.

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

* docs(target-arch): three measured corrections surfaced by the guidance program

memory packages are substrates-layer, not products (families/extensions.md);
memory_native declares no extension_contracts dep (PROPOSAL §6.8.4); wasm's
extension_contracts edge is dev-only and the wasm 'never depends on' bullet is
lane-scoped, not family-wide (families/lanes.md).

Three further reported defects were checked and NOT corrected — they were
misreads: the sandbox 'never above the runtime tier' rule holds (substrates sit
below it), and PROPOSAL's safety consumer count already reads 17, matching the
tree.

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

* docs(target-arch): repair the corrupted kernel bullet and correct two family laws

kernel.md: ironclaw_authorization's 'Security & authority role' bullet has been
textually corrupted since nearai#6918 — an approvals sentence was spliced into it
mid-clause, orphaning its continuation line. Reconstructed, with the spliced
sentence restored to the approvals entry where it is true.

lanes.md: 'a lane never depends on a substrate' is false as a family-wide law
(ironclaw_sandbox holds network/safety/secrets normal deps, which its own entry
licenses); the accurate law is the layer ladder, and the narrow claim holds for
ironclaw_wasm alone.

lanes.md + events.md: the 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement is superseded by docs/reborn/guidance-conventions.md — two files
restating one rule is the drift the guidance program removes.

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

* test(arch): govern the ProtocolAuthEvidence test seam — WS12 audit F1

Two new gates in reborn_sealed_evidence_mint_ratchet (closed paths #12/#13),
per the audit's remedy spec:
(a) TEST_SEAM_MINT_FNS governs test_verified/test_verified_for_tenant — any
    production-text call site outside ironclaw_host_api is an offender
    (comments/strings stripped, #[cfg(test)] blocks stripped, tests.rs /
    *_tests.rs and cfg-test-only files excluded via the shared census);
(b) test-support may appear in no normal dependency table workspace-wide
    (dependencies / build-dependencies / target.* variants /
    workspace.dependencies), and no [features] key other than test-support
    may forward to it — the laundering shape that would evade (b) by one
    rename. [dev-dependencies] enablement stays legal (cargo-features.md
    bar 4, the sanctioned dev seam).

Measured zero offenders on this tree in both directions before pinning;
sabotage-proven red->green both ways (planted production call named with
file:line-text; [dependencies] enablement named with its table path).
Self-tests drive the same pipelines the gates run (zero-match principle);
the definition-location and partition tests now cover the new table.

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

* test(integration): join the gsuite credential-injection journey — WS12 audit F3

WS12 row 5 leg 3 was verified in two halves no committed test joined: gsuite
handler -> staged credential (crate tier) and staged obligation -> wire
(GitHub/Slack only). Scenario 5 already drives gmail.list_messages through
production dispatch on a Google-OAuth-configured group; it now also asserts
the JOIN: the seeded google account's token (itest-google-token) lands on
the recorded outbound gmail.googleapis.com request as
'authorization: Bearer ...', injected at the host egress chokepoint
(apply_credential_injection) per the gmail manifest's declared recipe —
store -> dispatch-time staging -> chokepoint -> wire, through the caller.

Sabotage-proven: disabling the Header injection arm reds exactly this
scenario with 'no network egress request matching url gmail.googleapis.com
has header authorization' while the request itself still reaches the wire
(headers seen: content-type only) — the injection reason, not a setup
error; restore -> green.

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

* docs(target-arch): correct five measured dependency claims in families/domains.md

conversations does not depend on safety (its BoundaryRule now forbids it);
triggers depends on libsql_runtime + safety and NOT filesystem, so its
'filesystem-routed persistence path alongside SQL' is one path, not two;
memory's live set is host_api alone (prompt_envelope is allowlisted, unused);
auth was short by extension_contracts + product_contracts.

Each verified against the manifest before editing.

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

* docs(target-arch): record the closed scan evasions (F4) and the secrets-consumer correction (F2)

The sealed-mint census weaknesses PROPOSAL §11.2.5/§12.1a and CHECKLIST recorded
as live and owed to WS10 are all closed on this tree, verified by re-attacking
the seam with both evasions at once; the docs understated the seal. Ratchet is
23 tests. One residual replaces them: the test_verified test-seam constructors,
now pinned by two gates.

§12.1b's 'only products-layer crate with the edge' is false by one —
ironclaw_assistant carries ironclaw_secrets as port-declaration vocabulary with
no expose_secret call. Not a value-reach bypass; joins nearai#7095's inventory.

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

* docs(target-arch): correct the app-family layer, config's consumer set, and the webui route count

ironclaw_config declares layer=substrates while living in crates/app/;
its consumers include operator, extension_manager and extension_host, not just
the assembly crate and the binary; webui is 93 contract-locked routes, not 92
(nearai#6780 landed after the last recount).

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

* docs(ws12): tick row 7 — the fresh-agent placement probe passed on the final tree

All three placements correct with high confidence, each naming the trait, the
tests, and the tempting wrong place it rejected. The probe doubled as a docs
audit and independently hit four defects, three of which the stacked guidance
PR fixes — it succeeded despite them.

WS12 is now 7/7. The restructure is complete.

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

* docs(target-arch): the product→loop_host recount was wrong on the day it was written

Eight importing files across four seams, not seven across three — the fourth
being a skill-activation-observer seam (projection.rs, projection/live_progress.rs)
this bullet never named, which §6.4.7's own same-day note already implied.
Surfaced by the plan-conformance audit.

The recount history is 3→5→6→7→8, wrong at four of five attempts. That retires
the prose count as a method: the sever slice should land an inventory ratchet
before or with the move, not another number.

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

* review(7263): CodeRabbit round-1 triage — 4 code fixes (2 sabotage-proven, 2 red-first) + 6 doc-truth corrections

Code, each verified red-first or by sabotage matrix:
- sealed-mint ratchet: per-name sighting floor for TEST_SEAM_MINT_FNS
  (closed path #12). Proven: renaming test_verified_for_tenant away plus one
  extra legitimate sibling mention passed the old aggregate floor (silent
  disarm) and fails the new per-name floor naming the constructor; suite
  23/23 after revert. (CodeRabbit's claimed baseline ">2 mentions today" is
  wrong — each name has exactly one kept sighting — but the doc/enforcement
  mismatch and at-threshold fragility were real.)
- trace credit: non-finite novelty_score/duplicate_score are treated as
  absent before clamping (clamp preserves NaN, which poisoned online_score
  and credit_points_estimate); NaN cases added to the nearai#7144 regression test,
  red first.
- trace submission: a 2xx whose body stream dies mid-read now maps through
  request_failed (network telemetry kind, true I/O cause) instead of
  collapsing to an empty body that the nearai#7144 strict parse misreported as
  response_invalid/Submission; truncated-body regression test, red first.
- Postgres contract suites (event store + assistant ledger): isolated-DB
  names now carry a creation epoch and the once-per-binary sweep is
  age-gated (1h), closing the cross-process window where a sibling's fresh
  zero-backend database (between CREATE DATABASE and first connection) was
  sweepable; legacy pid-scheme leftovers still collect immediately. Proven
  on live Postgres 16: planted stale name swept, planted fresh name
  survives, 13/13 x2 and 20/20 x2 with zero leftovers.

Docs (target-architecture truth pass):
- PROPOSAL section 9: the WS6 rename sweep (nearai#7152) had rewritten the source
  column of the 12 renamed rows to their post-rename names, turning their
  rename dispositions into no-ops (rows 13/14/28/30/49/51/59/61/64/66/67/70);
  pre-restructure names restored with a dated footnote.
- PROPOSAL:69: removed the superseded 3->5->6->7 recount sentence (the
  corrected 3->5->6->7->8 passage subsumes it).
- PROPOSAL row 34: ToolPermissionOverrideStorePort deletion marked landed
  (2026-08-05 WS8, matching section 6.5.3; zero workspace hits).
- CHECKLIST:631: dated note recording that the WS12 F3 gsuite join landed in
  this batch (scenario_uninstalled_tool_call_denied_until_active.rs asserts
  the seeded google token on the gmail.googleapis.com wire; suite run green).
- CHECKLIST:632: dated note spending F4 (the audit's 19 was correct at its
  SHA; the ratchet file now holds 23 tests, re-counted at lines 552/597).
- ws12-gauntlet-report P6 heading: first of TWO real failures (one class),
  matching P8 and the report's own summary.
- ws12-mapping-audit rows 49/137: dated D-S closure notes (await-edge store
  half = journal projection already; resolver retained loop-tier; no shed
  owed) so the backlog register no longer lists it as in-flight.

Not fixed, with evidence: the span-helper macros gate suggestion
(info_span!(target = ...) is a hard compile error, E0425 — no silent trap),
the webui tracing-subscriber workspace-dep suggestion (no
[workspace.dependencies] entry exists; suggestion would not build; 8
siblings use the identical direct shape), and the mapping-audit
regeneration (the audit is accurate at its pinned SHA; the in-batch F1 fix
is recorded in its dated coordinator note).

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

* review(7263): CodeRabbit round-2 — rejection-body read keeps its cause; 200 {} is not a submission acknowledgement; lanes.md family dep rule matches measured Cargo.tomls

- submission.rs non-2xx path: a failed rejection-body read no longer collapses
  to an empty detail via .unwrap_or_default() (banned by
  .claude/rules/error-handling.md); the read error folds into the
  http_rejection detail so the received status keeps driving the 401/403
  auth-retry and the Credential/HttpRejection telemetry split.
  Regression: submit_preserves_rejection_body_read_failure_cause_with_status.

- TraceSubmissionReceipt.status: serde default removed — it fabricated
  status "submitted" from a proxy's 200 {} (the nearai#7144 synthesis, resurfacing
  through the wire type's defaults), after which the flush caller recorded
  Submitted and deleted the only retryable queued copy. The acknowledgement is
  the server naming what happened to the submission — every workspace fixture
  sends status and callers persist it unconditionally as server_status — so a
  status-less 2xx body now fails the strict receipt parse as response_invalid.
  Regression: submit_rejects_success_response_without_explicit_server_status
  (covers 200 {} and a status-less non-empty object).

- docs(lanes.md): the family Dependency-direction rule no longer claims every
  lane takes the extension-surface vocabulary crate — measured across
  crates/lanes/*/Cargo.toml: mcp + sandbox hold ironclaw_extension_contracts
  under [dependencies], wasm only under [dev-dependencies]; dated ✎
  cross-references the ironclaw_wasm entry's 2026-08-05 correction.

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

* review(7263): CodeRabbit round-3 — shared Postgres test provisioner (the "new dep edge" premise measured false), entrypoint self-test armed (sabotage-proven), six doc self-contradictions reconciled

Code:
- ironclaw_filesystem gains a `postgres_isolation` test-support module — the
  single home of the per-test isolated-database scaffolding (once-per-binary
  age-gated stale sweep, epoch-in-name convention, DROP WITH (FORCE) cleanup),
  parameterised by suite/env-var/prefix/unreachable-policy. Zero new
  production edges: event_store already normal-deps filesystem, filesystem
  already owns tokio-postgres, and the dev-dep+feature pattern is the one 17
  crates already use. The event-store and product-workflow-ledger suites
  migrate onto it; both Postgres legs proven live against postgres:16 (12
  tests, zero leftover databases). The fabric original keeps its older
  variant with the differences documented at its IsolatedDatabase.
- ironclaw_event_store drops the duplicate tokio-postgres dev-dep (the normal
  dep already reaches tests).
- test-reborn-docker-entrypoint.sh: the missing-argv check now exits the
  command-substitution subshell instead of incrementing a counter the parent
  never sees — red-proven (a migrate-but-never-exec entrypoint passed with 7
  FAIL lines printed), green after the fix both sabotaged and restored.
- trace_commons submission test additionally pins !auth_rejection() for the
  503 rejection (the structural assert the API affords; the prescribed
  payload asserts are refuted — status is private and source is None by
  design, with the message derived from the structured status in the same
  constructor).

Docs (each reconciled to one canonical statement, measured):
- kernel.md: lease ownership decided from code — authorization stores,
  matches, and expires leases (CapabilityLeaseStore + port + expiry all live
  there); approvals constructs and issues into that store. The round-1
  re-homing of the spliced sentence into approvals was wrong and is corrected
  in the dated repair note.
- app.md: "nothing depends on app" scoped to the three app-layer crates;
  ironclaw_config's consumers restated by dependency kind (normal:
  composition, cli, operator, extension_host; dev-only: extension_manager,
  root integration-tests package).
- lanes.md: the mediated-services sentence now states the family law as
  layer-ladder + injected authority; the no-secrets/network/filesystem-dep
  claim is scoped to ironclaw_wasm, matching the file's own corrections.
- CHECKLIST 429/430: the one open traces clause is named (ScopedFilesystem
  adoption); the stale "other two" count corrected against the F3a strike.
- PROPOSAL:69 + CHECKLIST:72: the project-create route repointed —
  first_party_extension_ports dissolved into loop_host::skill_activation
  (WS8, §9 row 55) — still unattempted.
- PROPOSAL §9 rows 57/62 synced to §6.8.4 (telegram: dependency-set equality
  with Slack's four contract-tier crates) and §6.9.4 (webui -> assistant is a
  charter-permanent edge, §12.11 D-B).
- PLAN top summary records Wave 6's design question as resolved (D-S,
  2026-08-05).
- deploy-reborn-cli-docker.md: the two migration paragraphs unified on the
  entrypoint's actual behavior — only enabled = false beside
  signing_secret_env/bot_token_env is migrated; every other retired-key shape
  fails startup with the migration pointer.
- composition-budget.toml: the stale "2398 bp, a true ratchet" header
  replaced with the WS0-floor truth the baselines test asserts.

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

* docs: move the guidance convention into this PR so its citations resolve

families/lanes.md and families/events.md cite docs/reborn/guidance-conventions.md
when superseding their 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement, but the file was only on the stacked guidance branch — a forward
reference that dangles if this PR merges alone. The convention is the rule those
notes invoke, so it belongs with them.

Caught by the CodeRabbit round-3 pass.

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

* fix(ci): give the hoisted postgres provisioner its safety rationales

The round-3 hoist moved test provisioning into a production src/ path, so
check_no_panics flagged its four panic/expect sites and reddened Code Style via
fast-checks. The gate is right to flag them: it deliberately does NOT exempt
#[cfg(feature = "test-support")] modules, because a cargo feature is not a
privilege boundary in this workspace (PROPOSAL 12.1a proved exactly that) —
so a test-support module still compiles into a build where any sibling enables
the feature.

Suppressed with the gate's documented inline rationale, which must trail the
statement rather than precede it. The panics themselves stay: a configured but
unusable Postgres must fail the suite loudly rather than skip it, which is the
inert-guard rule the isolation fix exists to serve.

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
…ry crate, and a repo-wide stale sweep (nearai#7264)

* docs(target-arch): resolve the await-edge design question by measurement (D-S) and re-walk the WS9 verify row

Appends §12.13 D-S under delegated authority at owner direction, flagged
for post-hoc review by Illia Polosukhin (nearai#6696's author): the await-edge
store is measured to be a pure projection over ProcessDependencyPort
(that half of the shed happened inside nearai#6696 itself), and the resolver
is a genuine loop-tier responsibility journal edges cannot express
(owner recovery, sanitized transcript result materialization, batch-gate
resume-once drain, BlockedDependentRunGate resume policy). §6.7.3 is
amended (scheduler DONE / store DONE / resolver KEEP) instead of the
shed being executed; the 2.9k figure is corrected to 1,459 production +
1,448 cfg(test) lines. The §12.10 bullet, §2 divergence flag, §9 row 49,
§13 validation row, CHECKLIST header/WS4 pointer, README and PLAN all
carry the dated resolution.

WS9 verify row ticked with evidence: one lifecycle authority (the
process journal; TurnRunState/TurnRunRecord are projections via
AgentTurnProcessRuntime, ProcessRecord is a capability-invocation view,
no bare RunRecord exists) and §7 T4 re-walked clause-by-clause against
merged code — matches, including the checkpoint-gated no-auto-retry
mechanism (BeforeModel precedes ModelStage; requeue only when
checkpoint-free under the 3-claim cap).

Docs-only; no code, no tests, no gates touched.

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

* docs(ws12): rows 1-2 — package-set tick (64==64/1/0, gate+selftest+independent rederivation) and the 74-row §9 mapping audit (45 L / 15 L-A / 14 OBD / 0 NOT-LANDED; 3 findings recorded)

Row 1: check-target-tree.py reports 64 workspace members == 64 documented
packages, 1 documented exclusion (tools/ironclaw_silk_decoder), 0 owned
exceptions (EXCEPTIONS table empty — §5 steady state); self-test 17/17;
cargo-metadata name set diffed empty against an independent §5 parse.

Row 2: docs/reborn/target-architecture/ws12-mapping-audit.md is the audit
record — per-row executed-evidence, delete-clauses read against WS8's
execution notes, all 14 open rows cite their owning CHECKLIST/PROPOSAL
row or issue. Findings (recorded, not fixed): F1 prompt_envelope
manifest-description fix has no owner row; F2 WS6:429's 'nearai#5618 residue
deleted' overstates vs the live adopt_migrated_identity + open WS8:523;
F3 stale-docs cluster where the tree is ahead of the prose (trace
re-export drop, TurnRunTransitionPort, processes->resources).

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

* fold(7154): squash-port fix/red-main-7119 onto family-world main — defect train nearai#7146/nearai#7115/nearai#7104/nearai#7103/nearai#7144 (+nearai#7119 CI lane), 34-hunk contribution.rs port into the split modules, planner entrypoint classification, D-R loopback exception on the widened HTTPS credential guard

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

* test(extractors): issue-number + assertion-rationale doc refinement (rescued 844964f from rescue/7154-parked-guard)

Ports only the doc/assertion refinement commit; the guard-parking commit
e8f5a31 on that branch is deliberately NOT taken — superseded by the
D-R loopback ruling.

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

* docs(target-arch): record D-R — the loopback credential-guard ruling, wiring choice, and regression pins (PROPOSAL §12.13, 2026-08-05)

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

* review(7154): CodeRabbit round-1 triage — fail-closed tracing-target scan traversal (+node_modules), bounded sidecar output draining (capped capture + discard drain), deadlock regression asserts successful redaction (no seq), XLSX/DOCX empty-classification via extract_document, raise_for_status annotations

Threads already addressed by the fold: latency.rs caller-contract wording
(merged doc scopes the requirement to latency-trace callers), BodyJsonPointer
coverage (the plaintext-refusal test drives all four injection shapes).
Deliberately not taken: un-xfailing the four Slack-catalog projections —
the xfail is a documented tripwire (unexpected-pass goes red) and clearing
them is the nearai#6520 projection-modeling follow-on its comment specs.

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

* fix(assistant): re-point the one field-form tracing target the nearai#7146 gate caught — main's relocated triggered_run_delivery_services carried the drift the PR fixed at its old channel_host address

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

* closure fixups: execute the mapping-audit findings — prompt_envelope manifest description (F1), dated ✎ corrections for the nearai#5618 overstatement (F2) and the stale-prose cluster (F3)

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

* docs(ws12): second-reviewer security spot-audit + extension-journey re-verification (rows 5-6)

Adversarial second-reviewer pass over PROPOSAL §12.1a/b/c and the batch's own
§12.13 D-R loopback carve-out, plus a re-run of the five extension journeys.
Attacks were executed rather than argued: two sabotage files and a 38-shape
hostile-URL probe were planted, run, and reverted.

Verdicts — mint consolidation HOLDS-WITH-RESIDUAL, secrets tightening
HOLDS-WITH-RESIDUAL, host/verifier colocation HOLDS, D-R HOLDS. No HOLE.

Four findings recorded rather than fixed (report-not-repair):
- F1 test_verified/_for_tenant are ungranted mint constructors gated only by
  the `test-support` feature, in no mint-name table, with nothing pinning the
  feature to [dev-dependencies]; the shipped binary is measured feature-free.
- F2 §12.1b's products-layer residue undercounts by one (ironclaw_assistant).
- F3 journey coverage hole: gsuite-with-credential-injection is proven in two
  halves that no committed test joins.
- F4 both recorded census evasions and both fail-open reads are CLOSED on this
  tree, so §11.2.5/§12.1a/CHECKLIST:552/:597 now understate the seal.

Rows 5-6 ticked; only lines 631-632 of CHECKLIST touched so the concurrent
rows 3-4 edit folds cleanly.

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

* ratchet(closure): lock the budget gate at the program's end state

Dispatch ceiling 1122 -> 814 (today's observed, nudge taken; WS0 record 827
stays within effective 829). Mass-share ceiling 2398 -> 658 bp (the WS0
baseline floor — the arch-test assert refuses lower, and observed 578 bp sits
inside the nudge window). Absolute LOC re-equalized at 40423: nearai#6831 added 4
governed LOC through the queue's tolerance window; ceiling, observed, and
COMPOSITION_ABSOLUTE_SRC_LOC move together here. Both tightenings
sabotage-verified red (dispatch 9-over at 790; abs 73-over at 40200).

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

* docs(ws12): gauntlet report — row 3 ticked (full gauntlet green, 0 REAL in scope), row 4 verified-but-open on two pre-existing Postgres-leg test-isolation defects

WS12 rows 3-4 verification on the assembled batch tip 0c6c0cf:

Row 3 (ticked): fmt, clippy default/all-features/--lib --bins, workspace
tests (495 targets, 15,203 passed, 0 failed; the smoke.rs:3132
CPU-saturation flake passed first try), arch suite 285/0, the
integration-feature lane 1,665/0, recorded-fixture QA (61 fixtures clean,
41/0), frontend (typecheck 1,588 files; vitest 1,088/0; build + bundle
budgets), e2e smoke = the CI browser lane under the hermetic wrapper
(50 + 21 + 5 passed), and all 41 scripts/ci self-tests (two mapfile/bash-3.2
casualties green under bash 5, the CI shape).

Row 4 (stays open, dated note added): both-backend parity proven with
legs demonstrably executed for the fabric (57 pg + 81 libsql), triggers
(ADR 0003, REQUIRE_POSTGRES), hooks (ADR 0004, all three backends),
composition, processes journal, extension-registry, host-runtime libSQL
restart, and the backend matrix; fabric-delegated domains enumerated.
Two REAL blockers (one class): the Postgres legs of the event-store and
assistant-ledger contract suites assert against shared-database state and
cannot pass as-written (each failing test passes alone on a virgin
database; files byte-identical to origin/main; no CI lane sets their env
vars). Full evidence: docs/reborn/target-architecture/ws12-gauntlet-report.md.

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

* docs(guidance): set the crate/family guidance convention

The base commit for the family-guidance program: one canonical home per fact,
measured-not-aspirational claims, boundaries stated as exclusions, and the note
that guidance files can be gate-pinned. Every family/crate document written on
top of this branch follows this shape.

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

* fix(tests): per-test isolated Postgres databases for the two WS12 parity-blocking contract suites

The WS12 gauntlet (ws12-gauntlet-report.md §P6/§P8) measured the Postgres
legs of ironclaw_event_store's durable_event_store_contract and
ironclaw_assistant's durable_ledger_contract as test-isolation-defective:
absolute database-global asserts (event cursors; settled-entry prune
bookkeeping) run against the single external database named by their
IRONCLAW_*_POSTGRES_URL env vars. Every failing test passes alone on a
virgin database - store semantics correct, suites not self-isolating
(PROPOSAL §12.13 D-T).

Fix: each affected test provisions a private database on the configured
server - the fabric contract's IsolatedDatabase pattern
(db_root_filesystem_contract.rs) ported locally into each suite: CREATE
DATABASE per test, store/pool + migrations against it, courtesy
DROP ... WITH (FORCE), and a once-per-binary stale-name sweep. Every
assertion preserved byte-identical; libsql/jsonl twins untouched. In the
ledger suite only the two retention tests move - the other six Postgres
tests keep their proven fingerprint-suffix isolation.

Regression pins are the fixed tests themselves:
- postgres_replay_advances_next_cursor_past_trailing_filtered_records
- postgres_runtime_and_audit_logs_survive_rebuild_with_filtered_cursor_semantics
- postgres_settled_entry_limit_prunes_oldest_when_configured
- postgres_settled_prune_interval_defers_until_interval_when_configured
Green proven on a shared dirty database twice in a row (parallel default
threading) and serially on a virgin database; red-first reproduction
captured before the fix.

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

* docs(reborn): record §12.13 D-T (parity-suite isolation ruling) and close CHECKLIST WS12 row 4

D-T (after D-S): the WS12 gauntlet's two REAL findings were one defect
class — absolute database-global asserts against the single shared
env-var Postgres database — in two suites (event store cursor contract,
assistant settled-ledger retention). Ruling executed in commit 864d93e:
per-test isolated databases via the fabric contract's IsolatedDatabase
pattern, assertions preserved; alternatives (baseline-relative asserts,
serial-only, leave-open) recorded with why they lost; regression pin =
the four fixed tests themselves.

CHECKLIST WS12 backend-parity row ticks [x] with a dated addendum: red-first
reproduction, the three green isolation runs (dirty shared DB twice in
parallel; failing pairs serial on virgin), parity now green 10/10.

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

* docs(extensions): family guidance layer — AGENTS.md rewrite to the guidance-conventions shape, READMEs for all 4 family crates and 14 packages, duplicate-guidance consolidation

The family AGENTS.md now teaches the unified extension model (extension =
the only product object; channel/tool/auth are manifest surfaces; runtime
is loading, never taxonomy; ExtensionId vs VendorId; retired vocabulary
pinned by reborn_retired_taxonomy.rs), carries the self-containment and
package-to-crate rules from families/extensions.md, the four-responsibility
lookup, the measured package catalog, the exclusion list, and the armed
gates by test name.

Every crate and package gains a README.md (ironclaw_extension_host had no
guidance of any kind). ironclaw_extension_registry and memory-native each
had both an AGENTS.md and a CLAUDE.md saying overlapping things: AGENTS.md
is now canonical, CLAUDE.md a pointer, and memory-native's stale v1
references (src/workspace, src/db/libsql) are dropped in the merge. The
slack/telegram agent maps get package framing and a contracts-tier pointer
in place of the stale ironclaw_assistant one. Every path literal verified
to resolve on disk; all figures (tool counts, dep sets, consumers, layer
declarations) measured from the tree at 8d13454.

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

* docs(guidance): substrates + lanes family guidance per guidance-conventions.md

Family AGENTS.md rewritten to the spec shape for crates/substrates/ and
crates/lanes/: boundary, crate table, exclusion lists (mechanism-not-authority
for substrates; kernel-decides-lane-executes for lanes), armed gates by test
name, and measured deviations stated as deviations (sandbox's three substrate
deps, script.rs direct spawn). The lanes wit/-is-load-bearing note is kept.

A README.md for every crate in both families (10 new), measured against
cargo metadata 2026-08-05: public surface, workspace edges, consumer counts,
and enforced invariants each citing their gate. ironclaw_libsql_runtime and
ironclaw_wasm_limiter previously had no guidance of any kind; their READMEs
carry the sole-pool-home rule (ADDITIONAL_DRIVER_ALLOWLISTS: deadpool =
{filesystem, libsql_runtime}) and the outbound-only limiter gate
(wasm_sandbox_core_module_stays_domain_free_v1_parity_kernel; no BoundaryRule
names the limiter).

Duplicate guidance consolidated per rule 1: for the six crates holding both
AGENTS.md and CLAUDE.md (filesystem, network, secrets, mcp, sandbox, wasm),
CLAUDE.md stays canonical (module spec for filesystem; gate-pinned wording for
mcp and wasm) and AGENTS.md becomes a short pointer. No gate-pinned file was
edited. Stale reference removed: safety AGENTS.md pointed at
src/NETWORK_SECURITY.md, which exists nowhere in the tree.

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

* docs(crates-map): rewrite the three top-level maps family-first after the restructure

crates/AGENTS.md (264 -> 175 lines): routing map only — the ten families,
the read order (family AGENTS.md -> crate README.md -> working rules/module
spec -> docs/reborn/contracts/), the enforced seven-layer matrix with the
family/layer divergences, measured workspace facts (64 packages, 1 documented
exclusion, 0 owned exceptions per scripts/ci/check-target-tree.py), and a
verified command block. The 40-row per-crate map is gone: family AGENTS.md
files own crate routing per docs/reborn/guidance-conventions.md.

crates/README.md (141 -> 119 lines): human map — mental model in family
vocabulary, the ten families with measured crate counts, the 14 extension
packages (4 crates + 10 data-only), and the two workspace members outside
crates/.

crates/Architecture.md (1019 -> 1059 lines): audited against the live tree;
every named symbol/path re-verified 2026-08-05. Corrected: retired
ProductAdapter vocabulary (zero residue in code), the stale pre-rename
dependency ladder that still cited the deleted gateway/TUI crates, run-state
store mentions, lane-table crate anchors (sandbox/extension_support),
declared-in vs minted-by owners in the core data model, and the subagent
deny-filter status note (re-verified). Marked the pre-restructure
'partial or evolving' list as unmeasured rather than asserting it.

Also documents that scripts/check-boundaries.sh fails on a clean tree
(check-5 grep false positives) and greps the deleted v1 src/ in 4 of 6
checks — boundary enforcement for crates/ is the architecture suite.

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

* docs(crates-map): package directories carry their own README.md (coordinator sync with extensions-family agent)

Every extensions package dir — the 10 data-only ones included — now ships a
README.md, so both maps extend the read order to package level. The sibling
branch also confirmed what this map already derived per-crate: packages/ is
not uniformly products-layer (memory-native and mem0 declare substrates).
The other two coordinator corrections targeted rows of the old per-crate
map, which this rewrite deleted wholesale; nothing here cites
memory-native's CLAUDE.md or claims ironclaw_extension_host lacks guidance.

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

* docs(guidance): contracts + events family guidance layer per docs/reborn/guidance-conventions.md

- crates/contracts/AGENTS.md and crates/events/AGENTS.md rewritten to the
  family shape: exclusion lists with destinations, armed gates by test name,
  layer-matrix rows, crossing guide, measured header counts.
- README.md added for all 10 crates (ironclaw_prompt_envelope previously had
  no guidance of any kind — the CHECKLIST WS11 gap).
- One canonical guidance file per crate, other file a pointer:
  A+C merges for ironclaw_host_api, ironclaw_event_log,
  ironclaw_event_projections, ironclaw_event_streams; CLAUDE-only content
  moved to AGENTS.md for ironclaw_loop_contracts,
  ironclaw_extension_contracts, ironclaw_product_contracts (none of these are
  root module-spec crates, so AGENTS.md is the working-rules home).
- Stale guidance fixed against the live tree:
  * loop_contracts dep list contradicted the enforced allowlist (manifest is
    host_api + extension_contracts; common/prompt_envelope are permitted,
    unused).
  * event_log still documented the deleted jsonl parse/replay helpers.
  * event_projections still claimed EventStreamManager,
    DurableMemoryAuditSink, MemoryAuditProjectionMetadata, and
    PendingGateProjection — all deleted per PROPOSAL 6.3.3.
  * product_contracts still carried the pre-D-E open vendor decision under
    the nonexistent module name llm_config, and a Deferred section
    contradicting its own operator_llm/operator_service rows.
  * extension_contracts module table was missing the WS3 runtime module
    while counting 18.
  * common's llm_costs note carried the ModelCostTable seam claim refuted by
    PROPOSAL 12.11 D-F; now cites the pricer-port ruling and the vendor
    census residue.
- Deleted crates/events/ironclaw_event_projections/PENDING_GATE_PROJECTION.md:
  every claim in it referenced deleted symbols or the removed v1 src/ tree,
  and its only inbound reference was the crate's own CLAUDE.md.

Verified: all consumer counts reproduce via the printed grep commands; 147
path literals across the 28 touched files resolve on disk; no architecture
test reads any of these files by name; conflict-marker scan clean.

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

* docs(target-arch): three measured corrections surfaced by the guidance program

memory packages are substrates-layer, not products (families/extensions.md);
memory_native declares no extension_contracts dep (PROPOSAL §6.8.4); wasm's
extension_contracts edge is dev-only and the wasm 'never depends on' bullet is
lane-scoped, not family-wide (families/lanes.md).

Three further reported defects were checked and NOT corrected — they were
misreads: the sandbox 'never above the runtime tier' rule holds (substrates sit
below it), and PROPOSAL's safety consumer count already reads 17, matching the
tree.

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

* docs(kernel): family guidance layer — perimeter AGENTS.md, nine crate READMEs, AGENTS/CLAUDE consolidation

Family-guidance program, kernel family (guidance-conventions.md shape):

- crates/kernel/AGENTS.md rewritten to the family shape: the nine-stage
  effect pipeline with stage ownership, the sealed-mint table (witness /
  trust ceiling / approval lease / verified-inbound evidence, each with its
  mint site and its seal mechanism), the per-stage fail-closed table with
  file:line or test citations, the sharp exclusion list, and the armed
  gates by test name (authorized-seal ratchet, sealed-evidence mint
  ratchet, BoundaryRules, same-layer edge inventory at 21 kernel edges,
  empty LAYER_MATRIX_EXCEPTIONS register, driver boundary, process storage
  scan, origin-gate matrix ratchet).
- A README.md for each of the nine crates, per the crate shape: measured
  workspace deps and consumer counts (cargo metadata), public surface with
  verified citations, enforced invariants naming their gates.
  ironclaw_processes states the single-lifecycle-authority direction of
  truth (journal = store; TurnRunState/ProcessRecord/await-edge =
  projections; PROPOSAL §12.13 D-S); ironclaw_host_runtime documents the
  D-R literal-loopback carve-out and names its two regression tests.
- Duplicate guidance reconciled in all nine crates: AGENTS.md is canonical
  (guardrails absorbed), CLAUDE.md reduced to a pointer; ironclaw_trust's
  CONTRACT.md untouched as the co-located cross-crate contract.
- Stale references fixed inside owned paths: the deleted capability-profile
  conformance module (evaluate_profile_conformance — zero hits
  workspace-wide) removed from ironclaw_capabilities guidance; trust's
  'staging branch' / 'PR3' phrasing updated; capabilities' 'later
  obligation slices' updated to the landed host_runtime obligations split;
  cross-crate path mentions fully qualified. Every path literal in all 29
  kernel .md files verified to resolve on disk; every named symbol swept
  against crate sources.

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

* docs(domains): family guidance layer — AGENTS.md boundary doc, 12 crate READMEs, duplicate-guidance consolidation, stale-path fixes

Family guidance for crates/domains/ per docs/reborn/guidance-conventions.md:

- crates/domains/AGENTS.md rewritten to the family shape: charter table with
  go-here-when routing, the exclusion list, every armed gate named by test
  (BoundaryRules + identity/memory allowlists, the 5-entry in-family edge
  inventory, the naming gates, trusted-trigger ownership, the memory-provider
  residue ledger, persistence-driver boundary, the two module-charter gates).
- A measured README.md for each of the 12 crates: charter, use-when /
  don't-use-when routing, public surface, measured normal deps + named
  consumers, enforced invariants with their gates, exact test commands.
  ironclaw_attachments and ironclaw_identity had no guidance of any kind;
  identity's README points at CONTRACT.md (the module spec), llm's at its
  CLAUDE.md module spec.
- Duplicate guidance consolidated to one canonical file + pointer per crate:
  threads/conversations/memory/outbound rules now live in AGENTS.md (CLAUDE.md
  is a pointer); auth/llm keep CLAUDE.md canonical because their
  tests/module_charter.rs gates read it (AGENTS.md is the pointer). One
  misstatement fixed in the conversations merge: transcript content belongs to
  ironclaw_threads' SessionThreadService, not InboundConversationService.
- Staleness fixed inside the family: identity CONTRACT.md two-edge allowlist
  claim reconciled with D-Q's three entries; trace_commons CLAUDE.md gains the
  capture module row and strikes its two discharged Known Gaps (recording/paths
  shims deleted, rename done); llm CLAUDE.md reasoning.rs caller corrected to
  crates/loop/ironclaw_loop_host; triggers lib.rs 'feature-gated' repo doc
  comments corrected; pre-family path literals in comments repointed
  (kernel/approvals+processes, loop/hooks, app/architecture_tests,
  domains/auth) and the deleted-v1-engine references in skills marked
  historical.

Verified: cargo test -p ironclaw_llm --no-fail-fast (922 passed, exit 0 —
CLAUDE.md is gate-pinned); cargo check --all-targets on all six crates with
source edits; every cited path literal resolves on disk; conflict-marker scan
clean.

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

* docs(target-arch): repair the corrupted kernel bullet and correct two family laws

kernel.md: ironclaw_authorization's 'Security & authority role' bullet has been
textually corrupted since nearai#6918 — an approvals sentence was spliced into it
mid-clause, orphaning its continuation line. Reconstructed, with the spliced
sentence restored to the approvals entry where it is true.

lanes.md: 'a lane never depends on a substrate' is false as a family-wide law
(ironclaw_sandbox holds network/safety/secrets normal deps, which its own entry
licenses); the accurate law is the layer ladder, and the narrow claim holds for
ironclaw_wasm alone.

lanes.md + events.md: the 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement is superseded by docs/reborn/guidance-conventions.md — two files
restating one rule is the drift the guidance program removes.

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

* test(arch): govern the ProtocolAuthEvidence test seam — WS12 audit F1

Two new gates in reborn_sealed_evidence_mint_ratchet (closed paths #12/#13),
per the audit's remedy spec:
(a) TEST_SEAM_MINT_FNS governs test_verified/test_verified_for_tenant — any
    production-text call site outside ironclaw_host_api is an offender
    (comments/strings stripped, #[cfg(test)] blocks stripped, tests.rs /
    *_tests.rs and cfg-test-only files excluded via the shared census);
(b) test-support may appear in no normal dependency table workspace-wide
    (dependencies / build-dependencies / target.* variants /
    workspace.dependencies), and no [features] key other than test-support
    may forward to it — the laundering shape that would evade (b) by one
    rename. [dev-dependencies] enablement stays legal (cargo-features.md
    bar 4, the sanctioned dev seam).

Measured zero offenders on this tree in both directions before pinning;
sabotage-proven red->green both ways (planted production call named with
file:line-text; [dependencies] enablement named with its table path).
Self-tests drive the same pipelines the gates run (zero-match principle);
the definition-location and partition tests now cover the new table.

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

* test(integration): join the gsuite credential-injection journey — WS12 audit F3

WS12 row 5 leg 3 was verified in two halves no committed test joined: gsuite
handler -> staged credential (crate tier) and staged obligation -> wire
(GitHub/Slack only). Scenario 5 already drives gmail.list_messages through
production dispatch on a Google-OAuth-configured group; it now also asserts
the JOIN: the seeded google account's token (itest-google-token) lands on
the recorded outbound gmail.googleapis.com request as
'authorization: Bearer ...', injected at the host egress chokepoint
(apply_credential_injection) per the gmail manifest's declared recipe —
store -> dispatch-time staging -> chokepoint -> wire, through the caller.

Sabotage-proven: disabling the Header injection arm reds exactly this
scenario with 'no network egress request matching url gmail.googleapis.com
has header authorization' while the request itself still reaches the wire
(headers seen: content-type only) — the injection reason, not a setup
error; restore -> green.

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

* docs(target-arch): correct five measured dependency claims in families/domains.md

conversations does not depend on safety (its BoundaryRule now forbids it);
triggers depends on libsql_runtime + safety and NOT filesystem, so its
'filesystem-routed persistence path alongside SQL' is one path, not two;
memory's live set is host_api alone (prompt_envelope is allowlisted, unused);
auth was short by extension_contracts + product_contracts.

Each verified against the manifest before editing.

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

* docs(guidance): family AGENTS.md + crate READMEs + guidance consolidation for loop/product/app

Family-guidance program, families 8-10 (the top of the stack), per
docs/reborn/guidance-conventions.md:

- Rewrite crates/{loop,product,app}/AGENTS.md from routing stubs to the
  spec's family shape: exclusion lists, armed gates by test name, layer
  rows, crossing guides. Loop carries the trust story + the declared
  Loop*Port decorator chain; product carries the frozen-surface rule,
  the transports-consume-contracts rule (with the D-B frozen-constant
  qualification), the evidence-mint prohibition, and the two vendor
  exceptions; app carries the wires-owners-never-becomes-one charter,
  the binary-names-packages rule, config's zero-dep guarantee, and the
  composition mass ratchet (loc 40423 / Arc<dyn> 814).
- Add a README.md to all 13 crates (12 new; webui's rewritten to the
  spec shape) with measured public surface, deps, and consumer counts.
- Consolidate duplicate AGENTS.md/CLAUDE.md per spec rule 1: AGENTS.md
  is canonical and CLAUDE.md a pointer for agent_loop, loop_host,
  turn_runner, hooks, host_ingress, openai_compat, operator, and
  architecture_tests; CLAUDE.md stays canonical (module spec /
  gate-pinned) for webui, composition, and assistant, with
  composition's AGENTS.md reduced to the pointer.
- Fix stale references in owned paths: hooks' dependency diagram and
  AgentLoopDriver home (ironclaw_loop_contracts, not ironclaw_turns),
  loop_host/agent_loop port-home claims, turn_runner's pre-nearai#6696
  scheduler description, webui's ProductSurface path
  (product_contracts, not host_api), route count (93, measured), and
  webui's allowed-dependency list (7 of 10 were listed), the D-S
  await-edge ruling reflected in turn_runner guidance, composition's
  llm_admin residue (nearai_login_serve left for operator).

Verified: cargo test -p ironclaw_architecture_tests --no-fail-fast
(39 binaries, 0 failures — covers the CLI AGENTS.md phrase pin and the
composition guidance-markdown scan), scripts/ci/check-target-tree.py,
path-literal resolution over all 37 changed files, conflict-marker scan.

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

* docs(target-arch): record the closed scan evasions (F4) and the secrets-consumer correction (F2)

The sealed-mint census weaknesses PROPOSAL §11.2.5/§12.1a and CHECKLIST recorded
as live and owed to WS10 are all closed on this tree, verified by re-attacking
the seam with both evasions at once; the docs understated the seal. Ratchet is
23 tests. One residual replaces them: the test_verified test-seam constructors,
now pinned by two gates.

§12.1b's 'only products-layer crate with the edge' is false by one —
ironclaw_assistant carries ironclaw_secrets as port-declaration vocabulary with
no expose_secret call. Not a value-reach bypass; joins nearai#7095's inventory.

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

* docs(target-arch): correct the app-family layer, config's consumer set, and the webui route count

ironclaw_config declares layer=substrates while living in crates/app/;
its consumers include operator, extension_manager and extension_host, not just
the assembly crate and the binary; webui is 93 contract-locked routes, not 92
(nearai#6780 landed after the last recount).

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

* docs(stale-sweep): fix agent guidance outside crates/ for the family restructure

Audit-and-fix pass over every stale document outside crates/ (PR 2 of the
family-guidance program). Live guidance verified against the tree; records
kept with dated notes instead of rewrites.

Guidance fixes (verified against HEAD before writing):
- .claude/commands/trace.md: MCP tool prefix codebase-memory -> codebase-memory-mcp
  (allowed-tools never matched the real server), ProductSurface home ->
  ironclaw_product_contracts, capabilities host.rs -> host/ module split,
  scripts lane -> script-sandbox; deleted the redundant v1-anchors section.
- .claude/commands/add-sse-event.md: deleted the banner-quarantined v1 scaffold
  steps (every path deleted with the monolith); now an honest redirect to the
  Reborn projection/SSE path. Frontmatter no longer advertises a working scaffold.
- .claude/commands/deslop-reborn.md: three dead crates/*/Cargo.toml globs (family
  layout added a level), ls crates/ -> family-aware listing, v1-only consumer
  logic retired, per-crate --features integration phrasing.
- .claude/rules/type-placement.md: crates/*/src globs matched nothing; recipes
  re-pointed and numbers re-measured 2026-08 (3,495 structs/enums, 385 traits,
  fan-in host_api 53 / common 20 / turns 12).
- .claude/rules/skills.md: paths trigger pointed at a nonexistent
  bundled_skills.rs (rule never fired); SKILLS_REGEX_ACTIVATION_ENABLED /
  SKILLS_MAX_TOKENS env vars are read by nothing -> documented the real
  config-file setting and DEFAULT_MAX_SKILL_CONTEXT_TOKENS.
- .claude/rules/testing.md, ironclaw-reborn-testing skill, CONTRIBUTING.md,
  .github/pull_request_template.md, testing-playbook, deslop: the workspace-root
  `integration` feature is empty with zero consumers - all "cargo test
  --features integration" guidance re-pointed to crate-level suites.
- .claude/skills/reborn-extension-surfaces: four pre-colocation assets/ paths,
  CapabilitySurfaceKind home, conformance-suite move to
  ironclaw_extension_contracts, ingestion test move to the registry crate,
  gate-banned migration exemplar replaced with the live behavioral pin, [mcp]
  instead-of claim softened (nearai-mcp pins a static [[tools]]).
- .claude/skills/ironclaw-reborn-orientation: turn_runner labels, prompt-crate
  list re-derived (turns/first_party_extension_ports out; host_api,
  loop_contracts, assistant in), consumer-grep glob fixed.
- .claude/skills/reborn-feature + docs/reborn/how-to-port-channel-to-reborn.md:
  ProductSurface/ProductView/descriptors/caller types live in
  ironclaw_product_contracts; recipes re-pointed.
- CLAUDE.md: dead root --features integration line replaced; project tree
  redrawn with the ten families; trait homes corrected; ProviderId -> VendorId;
  CapabilitySurfaceKind + ChannelAdapter homes; [channel.config] ->
  [channel.connection]/[admin_configuration]; v1 Job State Machine section
  deleted (no such machine in Reborn); prompt-crates recipe fixed; MCP server
  name; LLM backend list re-derived from LlmBackendKind.
- docs/extensions/building-a-tool.md: product-adapter crates row -> channel
  surface model; package registration -> PACKAGES collector in
  ironclaw_extension_support (available_extensions.rs is being dissolved);
  hosted-MCP policy home -> ironclaw_extension_host/src/mcp.rs; dead v1 bullets
  dropped.
- docs/internal/mutation-audit.md: runnable command blocks re-pointed (family
  paths; ironclaw_dispatcher example replaced - crate deleted in WS0).
- docs/reborn/harness/e2e.md: dispatcher row -> the capabilities dispatch
  contract suites. docs/reborn/contracts/host-api.md: three ironclaw_dispatcher
  mentions -> capabilities dispatch module. standard-operations.md: renamed
  crate + arch-test package name.
- scripts: mutation-audit.sh usage header, check-hermetic-env.sh env_helpers
  pointer, check-generic-without-concrete.sh mirror pointer,
  telegram_smoke/README regression step (target deleted with v1 in nearai#6375).
- .env.example: dead SKILLS_REGEX_ACTIVATION_ENABLED entry -> config-file doc.
- docs/qa/telegram-coverage-map.md: nine not-automated reasons re-worded to the
  crate-level integration tier.

Records (dated notes, no rewrites): ADR 0003/0004 path notes (evidence pinned
to their measured SHA), FEATURE_PARITY state-migration paragraph marked
historical with a git-show recovery pointer, engine-v2 parity record's
"coexist on main" claim corrected with a historical note, subagent-spawn
legacy scope re-tensed.

Pre-family path reproduction count: 73 -> 70 files; every remaining file is a
dated record (docs/plans, docs/superpowers, ADRs, audits, CHANGELOG history,
historical-marked train docs) or a deliberate past-tense mention.

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

* docs(ws12): tick row 7 — the fresh-agent placement probe passed on the final tree

All three placements correct with high confidence, each naming the trait, the
tests, and the tempting wrong place it rejected. The probe doubled as a docs
audit and independently hit four defects, three of which the stacked guidance
PR fixes — it succeeded despite them.

WS12 is now 7/7. The restructure is complete.

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

* docs(target-arch): the product→loop_host recount was wrong on the day it was written

Eight importing files across four seams, not seven across three — the fourth
being a skill-activation-observer seam (projection.rs, projection/live_progress.rs)
this bullet never named, which §6.4.7's own same-day note already implied.
Surfaced by the plan-conformance audit.

The recount history is 3→5→6→7→8, wrong at four of five attempts. That retires
the prose count as a method: the sever slice should land an inventory ratchet
before or with the move, not another number.

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

* review(7263): CodeRabbit round-1 triage — 4 code fixes (2 sabotage-proven, 2 red-first) + 6 doc-truth corrections

Code, each verified red-first or by sabotage matrix:
- sealed-mint ratchet: per-name sighting floor for TEST_SEAM_MINT_FNS
  (closed path #12). Proven: renaming test_verified_for_tenant away plus one
  extra legitimate sibling mention passed the old aggregate floor (silent
  disarm) and fails the new per-name floor naming the constructor; suite
  23/23 after revert. (CodeRabbit's claimed baseline ">2 mentions today" is
  wrong — each name has exactly one kept sighting — but the doc/enforcement
  mismatch and at-threshold fragility were real.)
- trace credit: non-finite novelty_score/duplicate_score are treated as
  absent before clamping (clamp preserves NaN, which poisoned online_score
  and credit_points_estimate); NaN cases added to the nearai#7144 regression test,
  red first.
- trace submission: a 2xx whose body stream dies mid-read now maps through
  request_failed (network telemetry kind, true I/O cause) instead of
  collapsing to an empty body that the nearai#7144 strict parse misreported as
  response_invalid/Submission; truncated-body regression test, red first.
- Postgres contract suites (event store + assistant ledger): isolated-DB
  names now carry a creation epoch and the once-per-binary sweep is
  age-gated (1h), closing the cross-process window where a sibling's fresh
  zero-backend database (between CREATE DATABASE and first connection) was
  sweepable; legacy pid-scheme leftovers still collect immediately. Proven
  on live Postgres 16: planted stale name swept, planted fresh name
  survives, 13/13 x2 and 20/20 x2 with zero leftovers.

Docs (target-architecture truth pass):
- PROPOSAL section 9: the WS6 rename sweep (nearai#7152) had rewritten the source
  column of the 12 renamed rows to their post-rename names, turning their
  rename dispositions into no-ops (rows 13/14/28/30/49/51/59/61/64/66/67/70);
  pre-restructure names restored with a dated footnote.
- PROPOSAL:69: removed the superseded 3->5->6->7 recount sentence (the
  corrected 3->5->6->7->8 passage subsumes it).
- PROPOSAL row 34: ToolPermissionOverrideStorePort deletion marked landed
  (2026-08-05 WS8, matching section 6.5.3; zero workspace hits).
- CHECKLIST:631: dated note recording that the WS12 F3 gsuite join landed in
  this batch (scenario_uninstalled_tool_call_denied_until_active.rs asserts
  the seeded google token on the gmail.googleapis.com wire; suite run green).
- CHECKLIST:632: dated note spending F4 (the audit's 19 was correct at its
  SHA; the ratchet file now holds 23 tests, re-counted at lines 552/597).
- ws12-gauntlet-report P6 heading: first of TWO real failures (one class),
  matching P8 and the report's own summary.
- ws12-mapping-audit rows 49/137: dated D-S closure notes (await-edge store
  half = journal projection already; resolver retained loop-tier; no shed
  owed) so the backlog register no longer lists it as in-flight.

Not fixed, with evidence: the span-helper macros gate suggestion
(info_span!(target = ...) is a hard compile error, E0425 — no silent trap),
the webui tracing-subscriber workspace-dep suggestion (no
[workspace.dependencies] entry exists; suggestion would not build; 8
siblings use the identical direct shape), and the mapping-audit
regeneration (the audit is accurate at its pinned SHA; the in-batch F1 fix
is recorded in its dated coordinator note).

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

* review(7263): CodeRabbit round-2 — rejection-body read keeps its cause; 200 {} is not a submission acknowledgement; lanes.md family dep rule matches measured Cargo.tomls

- submission.rs non-2xx path: a failed rejection-body read no longer collapses
  to an empty detail via .unwrap_or_default() (banned by
  .claude/rules/error-handling.md); the read error folds into the
  http_rejection detail so the received status keeps driving the 401/403
  auth-retry and the Credential/HttpRejection telemetry split.
  Regression: submit_preserves_rejection_body_read_failure_cause_with_status.

- TraceSubmissionReceipt.status: serde default removed — it fabricated
  status "submitted" from a proxy's 200 {} (the nearai#7144 synthesis, resurfacing
  through the wire type's defaults), after which the flush caller recorded
  Submitted and deleted the only retryable queued copy. The acknowledgement is
  the server naming what happened to the submission — every workspace fixture
  sends status and callers persist it unconditionally as server_status — so a
  status-less 2xx body now fails the strict receipt parse as response_invalid.
  Regression: submit_rejects_success_response_without_explicit_server_status
  (covers 200 {} and a status-less non-empty object).

- docs(lanes.md): the family Dependency-direction rule no longer claims every
  lane takes the extension-surface vocabulary crate — measured across
  crates/lanes/*/Cargo.toml: mcp + sandbox hold ironclaw_extension_contracts
  under [dependencies], wasm only under [dev-dependencies]; dated ✎
  cross-references the ironclaw_wasm entry's 2026-08-05 correction.

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

* review(7263): CodeRabbit round-3 — shared Postgres test provisioner (the "new dep edge" premise measured false), entrypoint self-test armed (sabotage-proven), six doc self-contradictions reconciled

Code:
- ironclaw_filesystem gains a `postgres_isolation` test-support module — the
  single home of the per-test isolated-database scaffolding (once-per-binary
  age-gated stale sweep, epoch-in-name convention, DROP WITH (FORCE) cleanup),
  parameterised by suite/env-var/prefix/unreachable-policy. Zero new
  production edges: event_store already normal-deps filesystem, filesystem
  already owns tokio-postgres, and the dev-dep+feature pattern is the one 17
  crates already use. The event-store and product-workflow-ledger suites
  migrate onto it; both Postgres legs proven live against postgres:16 (12
  tests, zero leftover databases). The fabric original keeps its older
  variant with the differences documented at its IsolatedDatabase.
- ironclaw_event_store drops the duplicate tokio-postgres dev-dep (the normal
  dep already reaches tests).
- test-reborn-docker-entrypoint.sh: the missing-argv check now exits the
  command-substitution subshell instead of incrementing a counter the parent
  never sees — red-proven (a migrate-but-never-exec entrypoint passed with 7
  FAIL lines printed), green after the fix both sabotaged and restored.
- trace_commons submission test additionally pins !auth_rejection() for the
  503 rejection (the structural assert the API affords; the prescribed
  payload asserts are refuted — status is private and source is None by
  design, with the message derived from the structured status in the same
  constructor).

Docs (each reconciled to one canonical statement, measured):
- kernel.md: lease ownership decided from code — authorization stores,
  matches, and expires leases (CapabilityLeaseStore + port + expiry all live
  there); approvals constructs and issues into that store. The round-1
  re-homing of the spliced sentence into approvals was wrong and is corrected
  in the dated repair note.
- app.md: "nothing depends on app" scoped to the three app-layer crates;
  ironclaw_config's consumers restated by dependency kind (normal:
  composition, cli, operator, extension_host; dev-only: extension_manager,
  root integration-tests package).
- lanes.md: the mediated-services sentence now states the family law as
  layer-ladder + injected authority; the no-secrets/network/filesystem-dep
  claim is scoped to ironclaw_wasm, matching the file's own corrections.
- CHECKLIST 429/430: the one open traces clause is named (ScopedFilesystem
  adoption); the stale "other two" count corrected against the F3a strike.
- PROPOSAL:69 + CHECKLIST:72: the project-create route repointed —
  first_party_extension_ports dissolved into loop_host::skill_activation
  (WS8, §9 row 55) — still unattempted.
- PROPOSAL §9 rows 57/62 synced to §6.8.4 (telegram: dependency-set equality
  with Slack's four contract-tier crates) and §6.9.4 (webui -> assistant is a
  charter-permanent edge, §12.11 D-B).
- PLAN top summary records Wave 6's design question as resolved (D-S,
  2026-08-05).
- deploy-reborn-cli-docker.md: the two migration paragraphs unified on the
  entrypoint's actual behavior — only enabled = false beside
  signing_secret_env/bot_token_env is migrated; every other retired-key shape
  fails startup with the migration pointer.
- composition-budget.toml: the stale "2398 bp, a true ratchet" header
  replaced with the WS0-floor truth the baselines test asserts.

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

* docs: move the guidance convention into this PR so its citations resolve

families/lanes.md and families/events.md cite docs/reborn/guidance-conventions.md
when superseding their 'every crate ships both an AGENTS.md and a CLAUDE.md'
requirement, but the file was only on the stacked guidance branch — a forward
reference that dangles if this PR merges alone. The convention is the rule those
notes invoke, so it belongs with them.

Caught by the CodeRabbit round-3 pass.

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

* fix(ci): give the hoisted postgres provisioner its safety rationales

The round-3 hoist moved test provisioning into a production src/ path, so
check_no_panics flagged its four panic/expect sites and reddened Code Style via
fast-checks. The gate is right to flag them: it deliberately does NOT exempt
#[cfg(feature = "test-support")] modules, because a cargo feature is not a
privilege boundary in this workspace (PROPOSAL 12.1a proved exactly that) —
so a test-support module still compiles into a build where any sibling enables
the feature.

Suppressed with the gate's documented inline rationale, which must trail the
statement rather than precede it. The panics themselves stay: a configured but
unusable Postgres must fail the suite loudly rather than skip it, which is the
inert-guard rule the isolation fix exists to serve.

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

* fix(ci): classify the three planner-unknown paths this PR touches

The Reborn PR test planner fails closed on any unclassified path and
raises on the FIRST failure in sorted order, so CI only ever showed
.github/pull_request_template.md. Classifying that unmasked two more
paths in this PR's own diff: scripts/mutation-audit.sh and
scripts/telegram_smoke/README.md. All three are classified; the
fail-closed arm is untouched:

* .github/pull_request_template.md -> IGNORED_PREFIXES, beside its
  exact sibling .github/ISSUE_TEMPLATE/ (both GitHub UI templates;
  classify-test-scope.sh already pairs them in its docs-only arm).
* scripts/mutation-audit.sh -> PR_STATIC_CONTROL_PATHS, beside its
  self-test scripts/test-mutation-audit.sh; both run only in
  nightly-deep-ci.yml's mutation-frontier job.
* scripts/telegram_smoke/ -> QA_HARNESS_PREFIXES; a live, by-hand
  release smoke harness referenced by no workflow, same class as
  scripts/reborn_qa_matrix/.

Each entry is pinned red-first in test_reborn_pr_test_plan.py (entry
commented out, new assertion fails with the exact production error,
entry restored, green): a new PR-template test with paired
accept-AND-select-nothing assertions plus unknown-.github/-sibling
refusal probes, and the two existing class tests extended. Planner
self-test: 65 tests OK. The planner CLI over this PR's full 209-path
diff now exits 0.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-7152 — d7c38fbb Deployed Aug 5, 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: medium Business logic, config, or moderate-risk modules scope: ci CI/CD workflows scope: dependencies Dependency updates scope: docs Documentation scope: sandbox Docker sandbox size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants