docs(target-architecture): reconcile the decision record with post-Wave-2 main - #7032
Conversation
…ve-2 main Audits docs/reborn/target-architecture/ (plus crates/AGENTS.md and the crate guides Wave 2 touched) against merged main at 3be5f05, after #6996, #6998, #7002 and #7018. Docs-only: 13 .md files, no code, no tests. House style throughout — dated amendments, prior text quoted verbatim wherever a clause is corrected, nothing rewritten silently and no decision record deleted. The two structural findings the wave produced and nobody had written down: same-layer edges are invisible to the layer matrix by construction, so the exception count could never have moved in Wave 2 and each removal needed its own purpose-built shrink-only gate (PROPOSAL §8.1, §8.2, §11.1); and the changed-line coverage policy — 90% lines, branch coverage ungated since #7013 — was recorded in no document at all, alongside a stranded-exemption failure mode the new pre-existing-uncovered exclusion creates (CHECKLIST WS10). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
🚅 Deployed to the ironclaw-pr-7032 environment in ironclaw-ci-preview
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR updates crate guidance and Reborn target-architecture documentation. It records Wave 2 ownership, dependency, milestone, inventory, exception, coverage, and enforcement findings. ChangesArchitecture documentation audit
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔎 Review · PR #7032
Execution result is invalid The structured result could not be verified. Automatic · PR opened · attempt 1 of 3 · failed after 1m 45s Failure details
|
There was a problem hiding this comment.
Actionable comments posted: 12
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/AGENTS.md`:
- Line 127: Update the stale module reference in
crates/ironclaw_product/CLAUDE.md to use operator_llm::{LlmConfigService,
ActiveModelReader, ...} for the service implementation. Preserve llm_config only
where it refers to the public ProductSurface view or historical text.
In `@crates/ironclaw_conversations/AGENTS.md`:
- Around line 18-34: Update the conversation binding documentation to state that
the live external identity is (space_id, conversation_id, topic_id). Clarify
that thread_id refers to canonical ironclaw_host_api::ids::ThreadId, with the
sole compatibility exception being the stored_refs field spelling used for
rollback-safe storage.
In `@docs/reborn/target-architecture/CHECKLIST.md`:
- Around line 260-261: Update the recorded defects in the checklist amendment to
include follow-up issue IDs for the stranded-exemption failure mode, the WS2
entry-count mismatch, and the stale workflow comment. Replace the existing
review reference to `#7018` with the corresponding issue reference, and ensure
each unresolved defect has an explicit issue link before marking the audit
complete.
- Line 17: The WS7 checklist entry overclaims that every gate uses
crate-inventory discovery and positive/negative fixtures while later
acknowledging roughly 20 named-path gates still need repointing. Narrow that
guarantee to only the gates fixed by `#6946` and `#6996`, or remove the claim, while
preserving the accurate details about the remaining WS10 work.
In `@docs/reborn/target-architecture/families/domains.md`:
- Around line 63-64: Update the family-level exhaustive dependency summaries to
include ironclaw_attachments → ironclaw_threads and ironclaw_product_contracts
wherever the chartered same-family edges and contracts allowlist are listed.
Revise the stated edge count from three to four and ensure the attachments and
contracts crate-level “Depends on” records match these summaries.
- Around line 103-110: Update the “Identity and binding value types” ownership
entry in the target-architecture documentation to remove external actor and
conversation reference types from ironclaw_conversations ownership. Retain only
this crate’s ownership of the durable stored_refs on-disk grammar, while
identifying ironclaw_extension_contracts::external as the sole home for those
reference types.
In `@docs/reborn/target-architecture/families/product.md`:
- Line 75: Update the secret-access statement on line 54 to reflect that
ironclaw_operator currently has a direct ironclaw_secrets dependency, or
explicitly label the port-only access guarantee as target state. Keep the
dependency inventory and its existing distinction between current and intended
architecture consistent.
- Around line 42-44: Update the live dependency guidance in this document to use
the current crate identifiers ironclaw_process_sandbox and
ironclaw_reborn_openai_compat, including the amended architecture text and any
other non-historical references. Search the Markdown guidance for stale
ironclaw_sandbox and ironclaw_openai_compat matches, and explicitly mark any
retained historical or target-state names.
In `@docs/reborn/target-architecture/PROPOSAL.md`:
- Around line 797-801: Correct the quantitative claim in the Wave 2 truth-audit
amendment so the same-layer category counts and stated total agree: either
change “72” to the sum of the listed categories, or add the missing category
with its verified count. Update the surrounding claim consistently without
altering the edge examples or conclusions.
- Line 640: Clarify the dependency statement in the 6.9.4 ironclaw_webui entry
by marking the ironclaw_product → product_contracts change as a target or
conditional outcome, not an applied change. Keep the later statement that the
dependency does not flip in this row and that placement remains an open
§6.1.3-vs-§6.9.4 decision consistent throughout the entry.
- Line 1098: Run mint dev and mint broken-links from the docs directory, then
record both completed checks in the documentation change; if mint is
unavailable, explicitly note the missing dependency instead.
In `@docs/reborn/target-architecture/README.md`:
- Line 11: Rewrite the paragraph beginning “Three program-level facts” to
distinguish the recorded causes for each surviving product edge: concrete
assembly in channel_host.rs for extension_host, DTO/capability and port residue
for extension_manager, and frozen product constants for webui and openai_compat.
Preserve the ironclaw_operator removal and Wave 2 exception-count statements,
and state the corresponding owner decisions without attributing all four edges
to route handlers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: aeae7778-b1d6-47f1-8d8b-92730ac7adcd
📒 Files selected for processing (13)
crates/AGENTS.mdcrates/ironclaw_conversations/AGENTS.mdcrates/ironclaw_operator/AGENTS.mdcrates/ironclaw_operator/CLAUDE.mdcrates/ironclaw_reborn_openai_compat/CLAUDE.mdcrates/ironclaw_webui/CLAUDE.mddocs/reborn/target-architecture/CHECKLIST.mddocs/reborn/target-architecture/PLAN.mddocs/reborn/target-architecture/PROPOSAL.mddocs/reborn/target-architecture/README.mddocs/reborn/target-architecture/families/domains.mddocs/reborn/target-architecture/families/extensions.mddocs/reborn/target-architecture/families/product.md
| | `ironclaw_loop_contracts` | `ironclaw_loop_contracts/CLAUDE.md`, `docs/reborn/target-architecture/families/contracts.md` | The loop-tier contract: the eleven `Loop*Port` traits + `AgentLoopDriverHost`, `AgentLoopDriver`, run-profile vocabulary, prompt/model/skill/instruction/milestone contract types, the `LoopExit` claim DTOs, the redacted checkpoint payload. | Any implementation of a port declared here, the turn coordinator/state store/exit applier, a dependency on `ironclaw_turns` (the direction inverts), any framework or driver crate. | | ||
| | `ironclaw_threads` | `ironclaw_threads/AGENTS.md`, `ironclaw_threads/CLAUDE.md` | Canonical session thread/transcript service contracts, identifiers, tool-result references, db/in-memory stores. | Product delivery policy or model/provider behavior. | | ||
| | `ironclaw_conversations` | `ironclaw_conversations/AGENTS.md`, `ironclaw_conversations/CLAUDE.md` | Conversation binding, session thread contracts, inbound/state store, libSQL/Postgres conversation persistence. | Capability runtime internals or UI transport. | | ||
| | `ironclaw_conversations` | `ironclaw_conversations/AGENTS.md`, `ironclaw_conversations/CLAUDE.md` | Conversation binding, the **inbound conversation** service contracts, inbound/state store, libSQL/Postgres conversation persistence, and the **durable ref grammar** (`stored_refs`) that keeps the released on-disk field spelling so a rename survives a rollback. ✎ *Corrected 2026-08-02: this read "session thread contracts", which is exactly the naming trap WS5 removed — `SessionThreadService` is `InboundConversationService` and `ThreadMessageRecord` is `ConversationMessageRecord`; five names collided with `ironclaw_threads` and all five were renamed.* | Capability runtime internals or UI transport. **The external actor/conversation ref types** — WS5 unified those onto `ironclaw_extension_contracts::external`; this crate owns the durable *grammar* for them, never a second declaration (`reborn_conversations_threads_attachments.rs` fails on one). | |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden \
--glob '.claude/**' \
--glob '**/AGENTS.md' \
--glob '**/CLAUDE.md' \
--glob 'crates/AGENTS.md' \
--glob 'docs/reborn/contracts/**' \
--glob '**/*.md' \
-P '(?<![A-Za-z0-9_])(SessionThreadService|ThreadMessageRecord|llm_config|SseManager|AppEvent|OperatorPublicRouteMount|OperatorProtectedRouteMount)(?![A-Za-z0-9_])'Repository: nearai/ironclaw
Length of output: 153
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
names = re.compile(
r"(?<![A-Za-z0-9_])"
r"(SessionThreadService|ThreadMessageRecord|llm_config|SseManager|AppEvent|"
r"OperatorPublicRouteMount|OperatorProtectedRouteMount)"
r"(?![A-Za-z0-9_])"
)
roots = [Path(".claude"), Path("docs/reborn/contracts")]
files = set()
for root in roots:
if root.exists():
files.update(p for p in root.rglob("*") if p.is_file())
files.update(Path(".").rglob("AGENTS.md"))
files.update(Path(".").rglob("CLAUDE.md"))
files.update(p for p in Path(".").rglob("*.md") if p.is_file())
matches = []
for path in sorted(files):
text = path.read_text(encoding="utf-8", errors="replace")
found = sorted(set(names.findall(text)))
if found:
matches.append((str(path), found))
print(f"scanned_files={len(files)}")
print(f"stale_reference_files={len(matches)}")
for path, found in matches:
print(f"{path}: {', '.join(found)}")
PYRepository: nearai/ironclaw
Length of output: 2410
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
pattern = re.compile(
r"(?<![A-Za-z0-9_])"
r"(SessionThreadService|ThreadMessageRecord|llm_config|SseManager|AppEvent|"
r"OperatorPublicRouteMount|OperatorProtectedRouteMount)"
r"(?![A-Za-z0-9_])"
)
files = set()
for root in (Path(".claude"), Path("docs/reborn/contracts")):
if root.exists():
files.update(p for p in root.rglob("*") if p.is_file())
files.update(Path(".").rglob("AGENTS.md"))
files.update(Path(".").rglob("CLAUDE.md"))
files.update(p for p in Path(".").rglob("*.md") if p.is_file())
for path in sorted(files):
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
for number, line in enumerate(lines, 1):
if pattern.search(line):
before = lines[number - 2].strip() if number > 1 else ""
after = lines[number].strip() if number < len(lines) else ""
print(f"{path}:{number}")
print(f" {before}")
print(f" {line.strip()}")
print(f" {after}")
PYRepository: nearai/ironclaw
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' 'Current Rust declarations and references:'
rg -n --glob '*.rs' \
'trait (SessionThreadService|InboundConversationService)|struct (ThreadMessageRecord|ConversationMessageRecord)|\b(SessionThreadService|InboundConversationService|ThreadMessageRecord|ConversationMessageRecord)\b' crates | head -120
printf '%s\n' 'Relevant guidance and contract references:'
rg -n \
'SessionThreadService|InboundConversationService|ThreadMessageRecord|ConversationMessageRecord|SseManager|EventStreamManager|AppEvent|OperatorPublicRouteMount|OperatorProtectedRouteMount|PublicRouteMount|ProtectedRouteMount|llm_config|operator_llm' \
.claude/commands/trace.md \
.claude/skills/ironclaw-reborn-orientation/SKILL.md \
AGENTS.md \
crates/AGENTS.md \
crates/Architecture.md \
crates/ironclaw_conversations/CLAUDE.md \
crates/ironclaw_product/AGENTS.md \
crates/ironclaw_product/CLAUDE.md \
crates/ironclaw_reborn_openai_compat/CLAUDE.md \
docs/reborn/contracts/openai-compatible-api.md \
docs/reborn/contracts/storage-placement.md \
docs/reborn/contracts/settings-config.md \
2>/dev/nullRepository: nearai/ironclaw
Length of output: 21858
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for spec in \
'crates/ironclaw_product/CLAUDE.md:48:86' \
'crates/ironclaw_product/CLAUDE.md:168:194' \
'crates/ironclaw_operator/CLAUDE.md:28:72' \
'docs/reborn/contracts/openai-compatible-api.md:104:120' \
'docs/reborn/contracts/openai-compatible-api.md:246:260' \
'docs/reborn/contracts/storage-placement.md:66:108' \
'docs/reborn/contracts/settings-config.md:128:142'
do
file=${spec%%:*}
rest=${spec#*:}
start=${rest%%:*}
end=${rest##*:}
printf '\n--- %s:%s-%s ---\n' "$file" "$start" "$end"
sed -n "${start},${end}p" "$file"
done
printf '\n--- conversation service declarations ---\n'
rg -n --glob '*.rs' \
'pub trait (InboundConversationService|SessionThreadService)|pub struct (ConversationMessageRecord|ThreadMessageRecord)' \
crates/ironclaw_conversations crates/ironclaw_threadsRepository: nearai/ironclaw
Length of output: 11408
Replace the stale llm_config module reference. In crates/ironclaw_product/CLAUDE.md, use operator_llm::{LlmConfigService, ActiveModelReader, ...}. Keep llm_config when it denotes the public ProductSurface view or historical text.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/AGENTS.md` at line 127, Update the stale module reference in
crates/ironclaw_product/CLAUDE.md to use operator_llm::{LlmConfigService,
ActiveModelReader, ...} for the service implementation. Preserve llm_config only
where it refers to the public ProductSurface view or historical text.
Source: Path instructions
| - Adapter-safe conversation binding and inbound-turn service contracts. | ||
| - External actor/conversation refs, source/reply binding refs, participant checks, message acceptance refs, and idempotency semantics. | ||
| - Source/reply binding refs, participant checks, message acceptance refs, and | ||
| idempotency semantics — plus the **durable grammar** for external refs | ||
| (`stored_refs`): write the released spelling | ||
| `{space_id, conversation_id, thread_id, message_id}`, read either, so the | ||
| WS5 rename is invisible to storage in both directions and a rollback stays | ||
| safe. | ||
| > Corrected 2026-08-02 (Wave 2 docs-truth audit): this read "External | ||
| > actor/conversation refs, source/reply binding refs, …". The ref **types** | ||
| > are no longer owned here — WS5 unified them onto | ||
| > `ironclaw_extension_contracts::external` after finding the two copies were | ||
| > field-divergent *and* compared differently (this crate's derived `PartialEq` | ||
| > included the per-event message id; the canonical type excludes the | ||
| > reply-target hint by hand), so two refs for one route could be equal or | ||
| > unequal depending on which copy the caller held. Declaring them again here | ||
| > fails `reborn_conversations_threads_attachments.rs`. What this crate owns is | ||
| > the record grammar, above. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
State the live external identity separately from the storage spelling.
The guide documents the legacy stored_refs.thread_id field but does not state the live external binding identity. Add that identity as (space_id, conversation_id, topic_id). Reserve thread_id for canonical ironclaw_host_api::ids::ThreadId, except for the rollback-compatible stored_refs field spelling. This prevents callers from using the storage key as the runtime route identity.
Based on learnings: the stable external conversation binding identity is (space_id, conversation_id, topic_id), while thread_id has the documented compatibility-only exception.
Suggested clarification
- Source/reply binding refs, participant checks, message acceptance refs, and
idempotency semantics — plus the durable grammar for external refs
(`stored_refs`): write the released spelling
`{space_id, conversation_id, thread_id, message_id}`, read either, so the
WS5 rename is invisible to storage in both directions and a rollback stays
safe.
+ - Live external conversation identity is `(space_id, conversation_id,
+ topic_id)`. Use `thread_id` only for canonical `ThreadId` values and the
+ rollback-compatible `stored_refs` field spelling.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - Adapter-safe conversation binding and inbound-turn service contracts. | |
| - External actor/conversation refs, source/reply binding refs, participant checks, message acceptance refs, and idempotency semantics. | |
| - Source/reply binding refs, participant checks, message acceptance refs, and | |
| idempotency semantics — plus the **durable grammar** for external refs | |
| (`stored_refs`): write the released spelling | |
| `{space_id, conversation_id, thread_id, message_id}`, read either, so the | |
| WS5 rename is invisible to storage in both directions and a rollback stays | |
| safe. | |
| > Corrected 2026-08-02 (Wave 2 docs-truth audit): this read "External | |
| > actor/conversation refs, source/reply binding refs, …". The ref **types** | |
| > are no longer owned here — WS5 unified them onto | |
| > `ironclaw_extension_contracts::external` after finding the two copies were | |
| > field-divergent *and* compared differently (this crate's derived `PartialEq` | |
| > included the per-event message id; the canonical type excludes the | |
| > reply-target hint by hand), so two refs for one route could be equal or | |
| > unequal depending on which copy the caller held. Declaring them again here | |
| > fails `reborn_conversations_threads_attachments.rs`. What this crate owns is | |
| > the record grammar, above. | |
| - Adapter-safe conversation binding and inbound-turn service contracts. | |
| - Source/reply binding refs, participant checks, message acceptance refs, and | |
| idempotency semantics — plus the **durable grammar** for external refs | |
| (`stored_refs`): write the released spelling | |
| `{space_id, conversation_id, thread_id, message_id}`, read either, so the | |
| WS5 rename is invisible to storage in both directions and a rollback stays | |
| safe. | |
| - Live external conversation identity is `(space_id, conversation_id, | |
| topic_id)`. Use `thread_id` only for canonical `ThreadId` values and the | |
| rollback-compatible `stored_refs` field spelling. | |
| > Corrected 2026-08-02 (Wave 2 docs-truth audit): this read "External | |
| > actor/conversation refs, source/reply binding refs, …". The ref **types** | |
| > are no longer owned here — WS5 unified them onto | |
| > `ironclaw_extension_contracts::external` after finding the two copies were | |
| > field-divergent *and* compared differently (this crate's derived `PartialEq` | |
| > included the per-event message id; the canonical type excludes the | |
| > reply-target hint by hand), so two refs for one route could be equal or | |
| > unequal depending on which copy the caller held. Declaring them again here | |
| > fails `reborn_conversations_threads_attachments.rs`. What this crate owns is | |
| > the record grammar, above. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/ironclaw_conversations/AGENTS.md` around lines 18 - 34, Update the
conversation binding documentation to state that the live external identity is
(space_id, conversation_id, topic_id). Clarify that thread_id refers to
canonical ironclaw_host_api::ids::ThreadId, with the sole compatibility
exception being the stored_refs field spelling used for rollback-safe storage.
Source: Learnings
| - [x] Record baselines for the ratchets that must not regress during the restructure: composition mass, production-struct dead-code, integration coverage floor, `LAYER_MATRIX_EXCEPTIONS` count (=20), extension-specificity allowlist size. **Landed with #6936**, every number measured from `origin/main` @ `ae0989c37` rather than copied from these docs: `LAYER_MATRIX_EXCEPTIONS` **20** (the recount matched the documented 20), extension-specificity allowlist **130** pairs, production-struct dead-code **82 frozen paths / 283 members**, composition mass **43,936 / 667,978 production LOC = 6.58% (658 bp)** with **827** governed `Arc<dyn>` sites, integration-coverage floor **85.54%** (a counting mechanism already existed — `tests/integration/coverage-floor.toml` + `scripts/ci/reborn-coverage-ratchet.sh` — so none was invented). The three list-shaped baselines sit beside the lists they measure (`reborn_dependency_boundaries.rs`, `reborn_extension_specificity.rs`, `reborn_struct_test_support_ratchet.rs`), each now shrink-only; the two enforced by shell gates are recorded in `reborn_restructure_baselines.rs`, which also pins that both gates stay armed. | ||
| - [x] Confirm the team decision on Strategy B (family dirs + focused crates). Rename scope is fully decided (PROPOSAL §12.10; naming rule §5.1). **[decision]** — **Confirmed 2026-07-31 (owner): north star shared with the team, epic #3773 cut from this checklist, and Waves 0–1 executing it — decision recorded retroactively; it was made in practice at program start.** | ||
| - [x] ⚠ Blocking prerequisite for WS7: the WS10 path-keyed-gate rewrites land before the first family `git mv` (they fail silently under nested dirs). **Landed in two PRs — #6946 (the five gates the WS10 row names) and #6996, which closed #6963: the six further silent gates, the two loud-but-flat-keyed repoints, and the two members later comments added.** Every gate now discovers through the crate inventory (`scripts/ci/lib/crate_tree.py` — the outermost owner of each `crates/**/Cargo.toml`), asserts it measured something, and carries positive + negative fixtures; behavior on the flat tree is proven unchanged per gate (trigger sets over all 4203 tracked files for the three workflow filters, byte-identical stdout and `--json` for the script gates, an identical 1249-file scanned set for the registration boundary). *Fixed by #6996:* `code_style.yml`'s `has_reborn_cli` regex, `platform-and-compat.yml`'s `has_direct_wasm_abi_risk` regex and `ironclaw-stress.yml`'s `paths:` filter — all three now keyed to crate **name at any depth** and pinned by `scripts/ci/ws12_workflow_contracts.py` against the real inventory, so a renamed, moved or deleted crate fails loudly in Code Style instead of quietly unhooking a lane — plus `scripts/ci/regression-test-check.py`'s `HIGH_RISK_PATTERNS`, `scripts/build-wasm-extensions.sh`'s assets glob, `scripts/ci/reborn_changed_coverage.py`'s pathspec and `PRODUCTION_PATH`, `scripts/ci/critical_mutation_gate.py`, `scripts/ci/check-composition-budget.sh`, and `crates/ironclaw_architecture/tests/reborn_registration_pipeline_boundary.rs`. **Two stale terms removed, each matching nothing today** — `ironclaw_wasm_product_adapters` (crate deleted) and `ironclaw_run_state` (deleted with #6696) — so the staleness the previous version of this row recorded is now historical. **Two corrections to #6963's inventory, both established empirically rather than by reading:** `build-wasm-extensions.sh` did *not* build nothing and exit 0 — `build_manifest_set` already carried an empty-set guard, so it needed discovery only (its bash-3.2 `unbound variable` death before reaching that guard was the real sub-defect); and `check-composition-budget.sh` is not purely loud — the all-crates move is loud, but the *partial* move, which is the realistic WS7 batch shape, was **silently green** at `0.00% (0 bp)` with a "lower the ceiling" nudge. A fourth pin on the dist-build regex turned up only because it failed: `crates/ironclaw_reborn_cli/tests/smoke.rs` greps that workflow line for the flat literal, and it is the reason a one-sided edit could not land silently. *Residue, none of it blocking:* #6996 also fixed the shared `ratchet_support::workspace_root()` fixed-depth idiom that 23 of the 24 `ironclaw_architecture` gates shared, the two that went silently green under it (`reborn_authorized_seal_ratchet` — worst under a *partial* move, 1309 → 45 files scanned with no error — and `reborn_retired_taxonomy`, 1492 → 0), and two vacuous `assert!(!path.exists())` checks; the other 20 gates' *named-path* keying still needs repointing at the `git mv` and is tracked on the WS10 loud-inventory row below. #6947 (classifier arm-inventory rot) and #6999 (the server-lifecycle rule's WebChat v2 gap, found by this sweep) stay open and neither blocks WS7. | ||
| - [x] ⚠ Blocking prerequisite for WS7: the WS10 path-keyed-gate rewrites land before the first family `git mv` (they fail silently under nested dirs). **Landed in two PRs — #6946 (the five gates the WS10 row names) and #6996, which closed #6963: the six further silent gates, the two loud-but-flat-keyed repoints, and the two members later comments added.** Every gate now discovers through the crate inventory (`scripts/ci/lib/crate_tree.py` — the outermost owner of each `crates/**/Cargo.toml`), asserts it measured something, and carries positive + negative fixtures; behavior on the flat tree is proven unchanged per gate (trigger sets over all 4203 tracked files for the three workflow filters, byte-identical stdout and `--json` for the script gates, an identical 1249-file scanned set for the registration boundary). *Fixed by #6996:* `code_style.yml`'s `has_reborn_cli` regex, `platform-and-compat.yml`'s `has_direct_wasm_abi_risk` regex and `ironclaw-stress.yml`'s `paths:` filter — all three now keyed to crate **name at any depth** and pinned by `scripts/ci/ws12_workflow_contracts.py` against the real inventory, so a renamed, moved or deleted crate fails loudly in Code Style instead of quietly unhooking a lane — plus `scripts/ci/regression-test-check.py`'s `HIGH_RISK_PATTERNS`, `scripts/build-wasm-extensions.sh`'s assets glob, `scripts/ci/reborn_changed_coverage.py`'s pathspec and `PRODUCTION_PATH` ✎ *(the constant is `CRATE_PRODUCTION_SOURCE` on `main` — #6996 renamed it when it stopped being a whole-path regex and became a crate-relative one applied to inventory-discovered crates; the substance of this clause is unchanged)*, `scripts/ci/critical_mutation_gate.py`, `scripts/ci/check-composition-budget.sh`, and `crates/ironclaw_architecture/tests/reborn_registration_pipeline_boundary.rs`. **Two stale terms removed, each matching nothing today** — `ironclaw_wasm_product_adapters` (crate deleted) and `ironclaw_run_state` (deleted with #6696) — so the staleness the previous version of this row recorded is now historical. **Two corrections to #6963's inventory, both established empirically rather than by reading:** `build-wasm-extensions.sh` did *not* build nothing and exit 0 — `build_manifest_set` already carried an empty-set guard, so it needed discovery only (its bash-3.2 `unbound variable` death before reaching that guard was the real sub-defect); and `check-composition-budget.sh` is not purely loud — the all-crates move is loud, but the *partial* move, which is the realistic WS7 batch shape, was **silently green** at `0.00% (0 bp)` with a "lower the ceiling" nudge. A fourth pin on the dist-build regex turned up only because it failed: `crates/ironclaw_reborn_cli/tests/smoke.rs` greps that workflow line for the flat literal, and it is the reason a one-sided edit could not land silently. *Residue, none of it blocking:* #6996 also fixed the shared `ratchet_support::workspace_root()` fixed-depth idiom that 23 of the 24 `ironclaw_architecture` gates shared, the two that went silently green under it (`reborn_authorized_seal_ratchet` — worst under a *partial* move, 1309 → 45 files scanned with no error — and `reborn_retired_taxonomy`, 1492 → 0), and two vacuous `assert!(!path.exists())` checks; the other 20 gates' *named-path* keying still needs repointing at the `git mv` and is tracked on the WS10 loud-inventory row below. #6947 (classifier arm-inventory rot) and #6999 (the server-lifecycle rule's WebChat v2 gap, found by this sweep) stay open and neither blocks WS7. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Narrow the completed-gate claim.
Line 17 says every gate now uses crate-inventory discovery and positive and negative fixtures. Later text still lists about 20 named-path gates that require repointing. Scope this sentence to the gates fixed by #6946 and #6996, or remove the completion claim.
As per coding guidelines, documentation promising guardrail guarantees must match the enforcing code and tests.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/CHECKLIST.md` at line 17, The WS7 checklist
entry overclaims that every gate uses crate-inventory discovery and
positive/negative fixtures while later acknowledging roughly 20 named-path gates
still need repointing. Narrow that guarantee to only the gates fixed by `#6946`
and `#6996`, or remove the claim, while preserving the accurate details about the
remaining WS10 work.
Source: Coding guidelines
| - **⚠ A stranded-exemption failure mode this wave created, recorded because nothing detects it.** `load_manifest` is aggressively fail-closed about *staleness* — a path that is not a live production `.rs` aborts the whole gate before any coverage is read, a line past EOF fails, a non-`nearai/ironclaw` issue URL fails, and an **expired `review_after` reds the gate**. It has no notion of an exemption that is simply never *used*. The pre-existing-uncovered exclusion (WS2's coverage note above) now removes lines automatically that the WS2 consolidation had already hand-exempted as *"PRE-EXISTING AND BEHAVIOURALLY UNTOUCHED … its pre-image scored zero in the BASE commit's merged tracefile"* — the two mechanisms overlap by construction, so some of the 79 entries can be permanently inert while still reading as live carve-outs. **Wanted (WS10-shaped, one assertion):** report exemptions that matched no excluded line in a run where base coverage *was* applied, so an obsolete carve-out expires by evidence instead of by the `review_after` calendar. Sibling defect in the same file, found the same way: the WS2 consolidation block's header says *"Thirteen lines across six files"* while its six entries list **fifteen** (1+2+5+2+1+4) — flagged in review on #7018, not applied before merge. Both are code-file fixes, out of scope for a docs-only PR. | ||
| - **Live figures for the floor ratchet**, since this row's numbers are quoted downstream: `[global]` `enforce = true`, `floor_percent = 85.11`, `tolerance_percent = 0.5` (effective **84.61%**), captured 2026-07-30. WS0's recorded **85.54%** (the row at the top of this file) is the WS0 capture and is now history, not the live floor. One stale in-repo comment worth fixing alongside: `.github/workflows/reborn-tests.yml:782-784` still says *"While `[global].enforce = false` … this always exits 0 (dry-run soak period)"* — the ratchet has been enforcing since the recapture. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Link follow-up issues for the recorded defects.
This amendment records the stranded-exemption defect and stale workflow comment without follow-up issue references. It also points to a review on #7018 for the count mismatch, but that is not a follow-up issue. Add issue IDs for each unresolved defect before marking the audit complete.
As per coding guidelines, discovered problems need follow-up issues.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/CHECKLIST.md` around lines 260 - 261, Update
the recorded defects in the checklist amendment to include follow-up issue IDs
for the stranded-exemption failure mode, the WS2 entry-count mismatch, and the
stale workflow comment. Replace the existing review reference to `#7018` with the
corresponding issue reference, and ensure each unresolved defect has an explicit
issue link before marking the audit complete.
Source: Coding guidelines
| > ✎ **Corrected 2026-08-02 (Wave 2 truth audit) — there is a fourth edge, and Wave 2 created it.** Prior text, quoted: *"No other crate in the family depends on a sibling — the family's internal graph is a shallow forest, not a mesh."* `ironclaw_attachments` now depends on `ironclaw_threads` (`attachments/src/ports.rs:23`, `src/project_scoped.rs:24`, for `ThreadScope`), acquired with the WS5 attachments widening. It is a legal downward-within-layer edge and the forest is still a forest — four edges, no cycles — but the sentence claimed an exhaustive list and is now short by one. The `attachments` entry's own **Depends on** line below carries the same omission plus a second one, and is corrected there. | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Update every exhaustive dependency summary.
Line 63 records ironclaw_attachments → ironclaw_threads, but Line 57 still says there are only three chartered same-family edges and Line 61 still lists only three. Line 200 also adds ironclaw_product_contracts, which is absent from the contracts allowlist in Line 57. Update the family-level summaries so they match the crate-level dependency records.
Suggested documentation fix
- contracts/ — host_api, common, prompt_envelope · plus the three chartered same-family edges only: conversations→triggers, attachments→extractors, trace_commons→llm
+ contracts/ — host_api, common, prompt_envelope, product_contracts · plus the four chartered same-family edges only: conversations→triggers, attachments→extractors, attachments→threads, trace_commons→llmAlso applies to: 200-202
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/families/domains.md` around lines 63 - 64,
Update the family-level exhaustive dependency summaries to include
ironclaw_attachments → ironclaw_threads and ironclaw_product_contracts wherever
the chartered same-family edges and contracts allowlist are listed. Revise the
stated edge count from three to four and ensure the attachments and contracts
crate-level “Depends on” records match these summaries.
| - **Never contains:** conversation or channel behavior of any kind; extension lifecycle; route-handling logic beyond mounting a carrier supplied by the ingress vocabulary. | ||
| - **Public surface:** implementations of the operator-service ports — LLM configuration, active-model reading, log service, service-lifecycle service, and status service — each defined in `ironclaw_product_contracts`. | ||
| - **Depends on:** `ironclaw_product_contracts` for the ports it implements, `ironclaw_llm` for provider mechanics, and `ironclaw_host_ingress` for its route carriers; boot-time values it needs arrive as construction input from whoever assembles the deployment, never as a direct dependency on the boot-configuration crate. | ||
| - **Depends on:** `ironclaw_product_contracts` for the ports it implements, `ironclaw_llm` for provider mechanics, and `ironclaw_host_ingress` for its route carriers; boot-time values it needs arrive as construction input from whoever assembles the deployment, never as a direct dependency on the boot-configuration crate. ✎ **Corrected 2026-08-02 (Wave 2 truth audit): the last clause is the family's target and is false today.** `ironclaw_operator` names `ironclaw_reborn_config` in its manifest and uses it in `operator_service_lifecycle.rs` at five sites, and its `BoundaryRule` — new with WS5 — does **not** forbid the edge. Also absent from this list and present in the manifest: `ironclaw_secrets` (a *known* debt, called out in the crate's own `AGENTS.md` and in the gate's comment, and the "product/operator lose direct `secrets`" tightening PROPOSAL §6.2.2 owns), plus `ironclaw_safety`, `ironclaw_common`, `ironclaw_filesystem`, `ironclaw_host_api`. **What WS5 did close is the headline edge:** `ironclaw_product` is gone from the manifest with a residue of zero, proven through `cargo metadata` rather than a path literal. The rest of this line is still ahead of the code. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Correct the secret-access guarantee.
Line 54 says ironclaw_operator reaches secret storage only through a port. Line 75 records a current direct ironclaw_secrets dependency. Mark Line 54 as target state or rewrite it to the current state. Otherwise the documentation claims a security boundary that the current manifest does not enforce.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/families/product.md` at line 75, Update the
secret-access statement on line 54 to reflect that ironclaw_operator currently
has a direct ironclaw_secrets dependency, or explicitly label the port-only
access guarantee as target state. Keep the dependency inventory and its existing
distinction between current and intended architecture consistent.
| ✎ **Landed 2026-08-01 (WS5 operator row), with two corrections to this entry's wording.** (1) *"Its Axum route fragments move behind `host_ingress` carriers wired by composition (it stops owning routers)"* — the carriers already existed and operator had **duplicated** them: `OperatorPublicRouteMount`/`OperatorProtectedRouteMount` were field-identical copies of `ironclaw_host_ingress::{PublicRouteMount, ProtectedRouteMount}`, and the duplicate forced a composition-side shim whose whole body converted one into the other. The clause was satisfied by *deleting* both the local carriers and the shim, not by moving a route; the protected copy had no consumer at all. Operator still owns the one route it has (the public NEAR AI login callback) and hands it back as a host-owned mount — which is what "stops owning routers" should say: it never mounts, it never nests, it hands back a carrier. (2) *"Gets: guidance files + a boundary rule (today it has neither)"* — done, and the absence turned out to be causal rather than cosmetic. `ironclaw_operator` and `ironclaw_product` are both `products`-layer, so `products → products` is legal by the matrix and **invisible to every existing gate**; with no `BoundaryRule` and no crate guidance, nothing in the workspace could have reported the edge. It now has `AGENTS.md`, `CLAUDE.md`, a `BoundaryRule`, and a purpose-built gate (`reborn_operator_port_inversion.rs`) that proves the manifest edge gone through `cargo metadata` rather than a literal path, so WS10's move of this crate into `product/` fails loudly instead of silently scanning nothing. | ||
| - **6.9.3 `ironclaw_openai_compat`** — retain, rename (drop `reborn_`). The OpenAI-shaped ingress adapter: route descriptors, wire DTOs, sanitized error envelope, ref/idempotency store, workflows over `BoundProductSurface`. Change: depends on `product_contracts` (+`extension_contracts` where channel DTOs are shared) instead of `ironclaw_product`; stale feature-gating guidance corrected. ✎ *2026-08-01: both edges landed with the WS5 transport inversion — 23 → 3 product symbols, the three survivors being the same frozen command constants that keep webui's edge alive. The `extension_contracts` edge carries exactly one type, `ProductTriggerReason`.* Open modeling question (adapter-as-extension?) stays §12.10 — not forced. Why a crate: a protocol surface with its own wire-stability contract and the tightest honored guardrails in the audit. | ||
| - **6.9.4 `ironclaw_webui`** — retain. Route surface + descriptor table (✎ **92** routes, contract-locked — re-counted 2026-07-31 at `2e6522580`: `rg -c 'pub const WEBUI_V2_ROUTE_' crates/ironclaw_webui/src/webui_v2/descriptors.rs` → 92, was 91; #6930 added `WEBUI_V2_ROUTE_REGISTER_HOSTED_MCP_EXTENSION` with its frozen-table row and updated the crate's own `CLAUDE.md` route table in the same PR — the contract lock working as intended), gateway middleware order, serve loop, host authentication (Env/Session/OIDC/composite + `/auth/*` login), product-auth HTTP routes, embedded SPA. Changes: `ironclaw_product` dep → `product_contracts` (the one non-DTO import, the bearer-evidence mint, moves to `host_api`'s sealed evidence home, deleting the `host-auth-mint` feature plumbing) ✎ **Corrected 2026-08-01 (WS5 transport inversion): "the one non-DTO import" is wrong by 91.** Beyond the mint (which left with WS1.5), webui names **91 concrete command/view/capability constants** — the frozen inventory §6.1.3 keeps in product — plus 11 wire DTOs whose fields name `ironclaw_attachments`/`threads`/`auth`/`common`/`loop_contracts`. Measured at `f4819bb50`: 228 product symbols before the inversion, 102 after. **The dep therefore does not flip in this row**; whether the inventory follows the descriptor types into contracts is the open §6.1.3-vs-§6.9.4 decision recorded on the CHECKLIST row; gains the pairing routes from `extension_host`; its second OAuth stack (host login) stays by charter (documented, distinct concern) — §12.10 records the consolidation question. Why a crate: the transport/presentation artifact (axum + SPA cone) with a comprehensive boundary rule. | ||
| - **6.9.4 `ironclaw_webui`** — retain. Route surface + descriptor table (✎ **92** routes, contract-locked — re-counted 2026-07-31 at `2e6522580`: `rg -c 'pub const WEBUI_V2_ROUTE_' crates/ironclaw_webui/src/webui_v2/descriptors.rs` → 92, was 91; #6930 added `WEBUI_V2_ROUTE_REGISTER_HOSTED_MCP_EXTENSION` with its frozen-table row and updated the crate's own `CLAUDE.md` route table in the same PR — the contract lock working as intended), gateway middleware order, serve loop, host authentication (Env/Session/OIDC/composite + `/auth/*` login), product-auth HTTP routes, embedded SPA. Changes: `ironclaw_product` dep → `product_contracts` (the one non-DTO import, the bearer-evidence mint, moves to `host_api`'s sealed evidence home, deleting the `host-auth-mint` feature plumbing) ✎ **Corrected 2026-08-01 (WS5 transport inversion): "the one non-DTO import" is wrong by 91.** Beyond the mint (which left with WS1.5), webui names **91 concrete command/view/capability constants** — the frozen inventory §6.1.3 keeps in product — plus 11 wire DTOs whose fields name `ironclaw_attachments`/`threads`/`auth`/`common`/`loop_contracts`. Measured at `f4819bb50`: 228 product symbols before the inversion, 102 after. ✎ *Corrected 2026-08-02 (Wave 2 truth audit): **9** DTOs and **100** symbols at merged `main`. The row predicted its own invalidation and nobody applied it — the WS5 `attachments widened` slice in the very same PR moved `ProductAttachmentCapabilities`/`product_attachment_capabilities` into `ironclaw_attachments` (they are `AttachmentCapabilities`/`attachment_capabilities()` now), which the transport gate's own comment records: "102 when the WS5 transport inversion landed; **100** after the WS5 `attachments widened` row". The pin is `WEBUI_PRODUCT_SYMBOL_BASELINE: usize = 100` (`reborn_transport_product_boundary.rs:212`) over a 100-entry exact-match list. **91 constants is unchanged and exact**, as is "92 routes". The stale pair propagated to three other places — CHECKLIST WS5's `webui` row, its "eleven survivors" sub-finding, and `crates/ironclaw_webui/CLAUDE.md` — all corrected in this audit.* **The dep therefore does not flip in this row**; whether the inventory follows the descriptor types into contracts is the open §6.1.3-vs-§6.9.4 decision recorded on the CHECKLIST row; gains the pairing routes from `extension_host`; its second OAuth stack (host login) stays by charter (documented, distinct concern) — §12.10 records the consolidation question. Why a crate: the transport/presentation artifact (axum + SPA cone) with a comprehensive boundary rule. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Disambiguate the WebUI dependency status.
The entry first states that ironclaw_webui changes from ironclaw_product to product_contracts. The amendment later says the dependency does not flip in this row and leaves placement open. Mark the first statement as target-only or conditional. Otherwise, readers can treat an unresolved decision as landed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/PROPOSAL.md` at line 640, Clarify the
dependency statement in the 6.9.4 ironclaw_webui entry by marking the
ironclaw_product → product_contracts change as a target or conditional outcome,
not an applied change. Keep the later statement that the dependency does not
flip in this row and that placement remains an open §6.1.3-vs-§6.9.4 decision
consistent throughout the entry.
| > ✎ **Amended 2026-08-02 (Wave 2 truth audit) — "each layer may use itself" is the whole of Wave 2's blind spot, and this rule states it as a permission without stating its cost.** `layer_allows_dependency` is **reflexive at every layer** (`reborn_dependency_boundaries.rs:4294`, and for products at `:4307-4310`), and the caller only consults the exception register *inside* the `if !layer_allows_dependency(...)` branch (`:196`). So a same-layer edge never reaches the violation branch and **no `LAYER_MATRIX_EXCEPTION` can ever exist for one**. On `main` that leaves **72 same-layer edges entirely outside the matrix** — 34 substrates→substrates, 18 kernel→kernel, **10 products→products**, 5 contracts→contracts, 4 loops→loops. | ||
| > | ||
| > **Why this matters more than it reads.** Every edge Wave 2 exists to kill is in that unpoliced plane: `extension_host → product`, `webui → product`, `openai_compat → product`, `operator → product`, `extension_manager → product`. All five crates declare `layer = "products"`. That is why Wave 2's milestones could never have moved the exception count, why each removal needed a **purpose-built** gate instead (§11.1's amendment lists them), and why `ironclaw_operator` carried an inverted dependency for months with, in that PR's words, *"nothing watching"* — no `BoundaryRule`, no guidance, and a matrix structurally incapable of reporting it. **The exception register is not a progress metric for any wave whose work is intra-layer.** Read the per-edge residue baselines in §11.1's scan list instead. | ||
| > | ||
| > Four `products → products` edges are policed by nothing at all today — `extension_host → host_ingress`, `webui → host_ingress`, `operator → host_ingress`, and `webui → openai_compat` (plus `telegram_extension → telegram_v2_adapter`, which §6.8.4 dissolves by merging the crates). The first four are sanctioned by §8.2's `product/` "siblings" cell and are genuinely fine; they are listed so the next audit does not rediscover them as findings. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fix the same-layer edge count.
The listed categories total 71 edges, not 72. Add the omitted category or correct the total. Keep the quantitative claim consistent with the evidence.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/PROPOSAL.md` around lines 797 - 801, Correct
the quantitative claim in the Wave 2 truth-audit amendment so the same-layer
category counts and stated total agree: either change “72” to the sum of the
listed categories, or add the missing category with its verified count. Update
the surrounding claim consistently without altering the edge examples or
conclusions.
| ## 13. Final validation checklist | ||
|
|
||
| - ☑ **Every current workspace crate accounted for:** §9 rows 1–3, 7–52, and 54–68 cover all 64 `crates/` packages; 69–70 the tools/root packages; 71–74 excluded packages; 4–6, 53 the new crates. (66 workspace + 4 excluded classes; ✎ **re-verified 2026-07-30 at `457088c8f`** — every `cargo metadata` package name appears exactly once, with `ironclaw_libsql_runtime` added at row 12 and `ironclaw_run_state` retired; `first_party_extension_ports`' implicit membership is still called out. ✎ **2026-08-01 (Wave 1 truth audit): the criterion still holds at `a50ad0638`, where the live figure is 67, not 66** — §9's rows 4–6 are the three Wave 1 contracts crates, entered at authoring time as **new** and now built, and the two WS0 deletions are `ironclaw_dispatcher` and `ironclaw_embeddings`. The 66/64 figures on this line and in §9's header are that table's 2026-07-30 measurement and are deliberately not re-derived here; §2.1 carries the current count and its arithmetic.) | ||
| - ☑ **Every current workspace crate accounted for:** §9 rows 1–3, 7–52, and 54–68 cover all 64 `crates/` packages; 69–70 the tools/root packages; 71–74 excluded packages; 4–6, 53 the new crates. (66 workspace + 4 excluded classes; ✎ **re-verified 2026-07-30 at `457088c8f`** — every `cargo metadata` package name appears exactly once, with `ironclaw_libsql_runtime` added at row 12 and `ironclaw_run_state` retired; `first_party_extension_ports`' implicit membership is still called out. ✎ **2026-08-02 (Wave 2 truth audit): the criterion still holds at `3be5f056e`, where the live figure is 68** — row 53's `ironclaw_extension_manager` is built, so four of §9's five "new" rows are now real crates and only `sandbox` is still a target. ✎ **2026-08-01 (Wave 1 truth audit): the criterion still holds at `a50ad0638`, where the live figure is 67, not 66** — §9's rows 4–6 are the three Wave 1 contracts crates, entered at authoring time as **new** and now built, and the two WS0 deletions are `ironclaw_dispatcher` and `ironclaw_embeddings`. The 66/64 figures on this line and in §9's header are that table's 2026-07-30 measurement and are deliberately not re-derived here; §2.1 carries the current count and its arithmetic.) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cd docs
mint dev
mint broken-linksRepository: nearai/ironclaw
Length of output: 195
Run mint dev and mint broken-links from docs/.
This Markdown change is under docs/**/*, so the repo check for documented enforcement requires both results before merge. Include both completed checks here or note any missing mint dependency.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/PROPOSAL.md` at line 1098, Run mint dev and
mint broken-links from the docs directory, then record both completed checks in
the documentation change; if mint is unavailable, explicitly note the missing
dependency instead.
Source: Coding guidelines
|
|
||
| > ✎ **Landed marker — Wave 2's port-inversion half is on `main` (2026-08-02, audited at `3be5f056e`).** Four PRs: **#6998** (WS2.1, `extension_host`'s product-facing ports inverted onto `product_contracts`), **#7002** (WS5, `webui` + `openai_compat`), **#7018** (the consolidated stack: WS2.2's `ProductSurfaceFailure` linchpin, WS2.4's `ironclaw_extension_manager` split, WS5's operator inversion and the conversations/threads naming trap + attachments widening), plus **#6996** (the WS0 path-keyed-gate closeout, #6963). **`ironclaw_extension_manager` now exists**, so the `NEW` tag on it in the map below — like the three contracts crates' — describes the target rather than a gap; four of the five planned new crates are built and only `sandbox` remains. Workspace packages: **68** (PROPOSAL §2.1); the steady-state target of 64 is unchanged. | ||
| > | ||
| > **Three program-level facts came out of the wave and are recorded rather than smoothed over.** (1) **`ironclaw_operator`'s product dependency is gone from the manifest — the first of §8.2's three named product edges to actually close**, with a residue of zero, which no earlier inversion achieved. (2) The other four edges (`extension_host`, `extension_manager`, `webui`, `openai_compat` → `product`) **survive by construction, not by shortfall**: a route handler must name product's frozen command/view/capability *constants* to call the surface, and §6.1.3 deliberately withholds those from contracts — so those flips need an owner decision, not more work (CHECKLIST WS5's `webui` row). (3) **Wave 2 moved the `LAYER_MATRIX_EXCEPTIONS` count by zero and could not have moved it at all** — every edge it removed is `products → products`, a class the layer matrix is structurally blind to, which is why each removal needed its own purpose-built shrink-only gate (PROPOSAL §8.1 reading rule 1 and §11.1, both amended). **Read exception count as a Wave 3 metric, not a Wave 2 one.** |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Separate the four surviving product-edge causes.
Line 11 attributes all four edges to route handlers naming frozen product constants. The audit records different blockers: concrete assembly in channel_host.rs for extension_host, DTO/capability and port residue for extension_manager, and frozen constants for WebUI/OpenAI compatibility. Rewrite this paragraph with the per-crate causes and owner decisions.
As per coding guidelines, documentation claims must match the recorded architecture evidence.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reborn/target-architecture/README.md` at line 11, Rewrite the paragraph
beginning “Three program-level facts” to distinguish the recorded causes for
each surviving product edge: concrete assembly in channel_host.rs for
extension_host, DTO/capability and port residue for extension_manager, and
frozen product constants for webui and openai_compat. Preserve the
ironclaw_operator removal and Wave 2 exception-count statements, and state the
corresponding owner decisions without attributing all four edges to route
handlers.
Source: Coding guidelines
Coverage ratchetReborn integration-tier coverageLine coverage (Reborn crates): 86.17% — 328043 / 380706 lines Per-crate breakdown (64 crates, lowest-covered first)
This table itself is informational and never gates the PR on its own — not the percentage, not the per-crate holes, not the 0-coverage callout. A separate coverage ratchet (dry-run until enforce=true; see tests/integration/coverage-floor.toml) can fail the build on specific configured floors. Exemptions (18 entry/entries excluded from the accounting above)
|
…ve-2 main (nearai#7032) Audits docs/reborn/target-architecture/ (plus crates/AGENTS.md and the crate guides Wave 2 touched) against merged main at 3be5f05, after nearai#6996, nearai#6998, nearai#7002 and nearai#7018. Docs-only: 13 .md files, no code, no tests. House style throughout — dated amendments, prior text quoted verbatim wherever a clause is corrected, nothing rewritten silently and no decision record deleted. The two structural findings the wave produced and nobody had written down: same-layer edges are invisible to the layer matrix by construction, so the exception count could never have moved in Wave 2 and each removal needed its own purpose-built shrink-only gate (PROPOSAL §8.1, §8.2, §11.1); and the changed-line coverage policy — 90% lines, branch coverage ungated since nearai#7013 — was recorded in no document at all, alongside a stranded-exemption failure mode the new pre-existing-uncovered exclusion creates (CHECKLIST WS10). Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Wave 2's port-inversion half is fully merged — #6998 (WS2.1), #7002 (WS5 transports), #7018 (the consolidated WS2.2 + WS2.4 + WS5 stack) and #6996 (the #6963 gate closeout). This audits
docs/reborn/target-architecture/against mergedmainat3be5f056eand closes every place the decision record no longer matches what shipped.Standing rule this serves:
docs/reborn/target-architecture/is the single source of truth. A correction that lives only in a PR body, a review thread, or a GitHub issue is not recorded.Docs-only. 13
.mdfiles, no code, no tests. House style throughout: dated✎amendments, prior text quoted verbatim wherever a clause is corrected, nothing rewritten silently.Per-gap disposition
layer_allows_dependencyis reflexive at every layer (:4294, products at:4307-4310) and the exception register is only consulted inside theif !layer_allows_dependency(...)branch (:196) — so noLAYER_MATRIX_EXCEPTIONcan ever exist for a same-layer edge. 72 same-layer edges sit outside the matrix, 10 of themproducts → products. Every edge Wave 2 exists to kill is in that plane.products → productsedges so the next audit does not rediscover them.webui/srcis uncovered, and it is the one crate that would fail (lib.rs:220/238); tracked in #6999. (b) Theextension_hostrow's "✗ product (the restored invariant)" — there is noboundary_rules()entry forironclaw_extension_hostat all; what holds it is a shrink-only residue ratchet, a ratchet toward the invariant. (c) No row exists forextension_manager, which landed in WS2.4.loopsis what finally lets the matrix enforce theextension_hostrow, a second independent reason §12.1c's ordering binds.product_contracts13,362 lines,loop_contracts13,950; the two crates §11 calls "thin").conversations → turnsreadsremoves_in = "WS5"and WS5 has partly landed without it falling. The ratchet forbids growth, not lateness, andexception_tracking_defectonly rejects placeholder text — nothing in CI notices a milestone passing. The ratchet also landed in WS0, not "adds".productrow.LAYER_MATRIX_EXCEPTIONS(:3557-3702)":49→:126,:3497→:4010,:129→:197).:4065-4157, pin at:4063, citations corrected, and why Wave 2 moved it by zero.test_support, and "the product-side ports are deferred by design, not landed" → landed in WS2.1/WS2.2/WS5. §6.1.3 contradicts itself: three inline amendments above say the ports landed; this trailing block is the newest-looking dated note, so it is the one a reader trusts.WS2_..._BASELINE = 4), and #7008 given a home (product_wire.rs1,923→2,058 lines,large_fileexemption owner).OWNED_PREFIXESstill names onlycrates/ironclaw_extension_host/src/hosted_mcp_… must move with the pipeline"OWNED_SCOPES, it names two scopes, and #6996 rewrote it to(crate, in-crate prefix)pairs resolved through the inventory — its own doc comment now says "these survive a family move". The WS7 risk this warns about is discharged. PLAN's item 2 is the mirror image (prefixes right, mechanism stale).src/hosted_mcp_half travels.webuirow + §6.9.4 +webui/CLAUDE.md— "228 → 102 symbols", "eleven survivors"attachments widenedslice in the same PR moved the two symbols the webui row named as belonging to it. The gate's own comment records the correction — "102 when the WS5 transport inversion landed; 100 after…" — and no doc picked it up.llm_configmainhad already moved the same DTOs into the pre-existingoperator_llm, deleted the duplicate and repointed 18 import sites. The dead name propagated to four places;product_contracts/CLAUDE.md— the file they all point at — had it right throughout.pull_request ∉ FULL_EVENTS). With #6978, a stacked slice gets its coverage verdict for the first time in the merge queue.line_percent = 90.0,branch_percent = 0.0(changed-coverage-exemptions.toml:9-11), set by #7013, replacing 100/100 from #6889 — branch coverage was gated at a nonzero number for about two days in this program's history.0.0means ungated, not "0% required" (branch_pct < 0.0is unsatisfiable). Also "15 packages" → 16; WS0's 85.54% is history (live global floor 85.11, effective 84.61).4bb24accb: 19/10,527), then review commits added a 20th module and ~1.8k lines of tests. §2.4's 56,940/93 is now the pre-split high-water mark.coverage-floor.toml's 9,979 source / 5,440 instrumented; #7018's 9,671-line carve) — all honest, none quotable.attachments → threads, an edge Wave 2 itself created. Attachments' Depends-on omits that andproduct_contracts(the live[decision]). Conversations still "owns" the external ref pair (unified ontoextension_contracts) and its deps omit the new edge. "Never depends on the turn coordinator crate directly" reads as an achieved invariant whileconversations/CLAUDE.mdmandates that edge.ProjectScopedAttachmentReadercarve-out and its #7010 tracker.ironclaw_reborn_config, 5 sites, and its newBoundaryRuledoes not forbid it); webui's Depends-on omitsironclaw_attachments, the edge WS5 deliberately created; "transports compile against contracts, notironclaw_assistant" is true of vocabulary and cannot be made true of the manifest without the open §6.1.3-vs-§6.9.4 decision.ironhub, the crate's second-largest module cluster (~3.1k lines / 8 files). PROPOSAL, CHECKLIST and the crate's ownCLAUDE.mdall record it.ironclaw_operatorhas no row at all — the crate Wave 2 gaveAGENTS.md,CLAUDE.md, aBoundaryRuleand a purpose-built gate, 9.6k LOC, invisible on the routing map. Conversations row says "session thread contracts", the exact naming trap WS5 removed. Theproduct_contractsrow pins implementors "not by this prose" but omitsironclaw_operatorand the gate that pins it.extension_managerstill taggedNEWwith no signal it exists; PLAN's "the inventory moves as a unit, like it arrived" was refuted by the split (5 of 9 moved); package count 67 → 68.Verified still accurate
Recorded because the audit is only worth what it checked.
members, WS2.4's amendment exact. Independently re-derived fromcargo metadata.git log -Gconfirms no Wave 2 PR touched it.ironclaw_operator's product dep is gone from the manifest with residue zero — absent fromCargo.toml, 0 occurrences insrc/, forbidden by itsBoundaryRule, proven throughcargo metadata. The first of §8.2's three named product edges to actually close.openai_compat23 → 3.rgsays 9; two are doc comments the scanner strips).host_api+extension_contracts, noironclaw_common) — the two-document reconciliation Wave 1 claimed is real.ProductOperationFailure's six variants; theAppEventrefutation; the whole §6.8.2 shed list still unshed (so CHECKLIST WS2's verify row is correctly unchecked);families/contracts.mdfully current — it is where PROPOSAL §6.1.3 is stale.webuirow states it ("once a DTO's home is the contracts crate, animpl From<ironclaw_turns::X> for ThatDtohas neither side inironclaw_productand cannot be written there at all"), with the three kernel conversions that became free functions and the two extension traits named; theattachmentsrow applies it toProjectScopedAttachmentReader; and Split the product_wire DTO family in ironclaw_product_contracts (large_file exemption owner) #7008 records the cost any furtherproduct_wirecarve pays. I added the §6.1.3 cross-reference so the contracts crate's own entry carries it, and thefamilies/domains.mdattachments carve-out now names it too — but the finding itself was correctly recorded when it was made.products → productsinvisibility was recorded, but only in a slice disposition (CHECKLIST WS2 row 1, disposition 4) — accurate where it sat, and absent from §8 and §11, which is where a reader planning a wave would look. That asymmetry is gap 1 above rather than a contradiction.docs/reborn/target-architecture/, so this PR cannot break one. (Four tests do read docs: threedocs/reborn/contracts/*.mdanddocs/plans/composition-pubuse.snapshot.)Briefed premises this audit refuted
channel_config_product_service.rs:61-69unreachability finding is discharged in code, not an unrecorded gap. Theif let Ok(true)error-swallow was replaced with a totalmatchthat propagates non-NotInstalledfailures, andfield_statusgained caller-level tests, both before refactor(contracts): consolidate the Wave 2 port-inversion stack (WS2.2, WS2.4, WS5) #7018 merged. Nothing to record.branch_percent = 0.0is not a lowered floor, it is no floor — and branch coverage was never gated before ci: enforce WS11 coverage and critical mutation gates #6889 at all, so ci: restore the original 90% changed-line coverage floor #7013 restored the long-standing state rather than relaxing a settled one.Findings a sibling agent is actively changing — deliberately not amended here
Audited against
mainonly. Each of these is real and each belongs to a branch in flight, so amending it here would collide:ws2/decisions— the five open[decision]rows:channel_host.rsownership (now the binding constraint on the re-layer, 26 of the surviving product symbols), hosted-MCP registration placement, theslack_userdestructive boot migration,LlmConfigService's vendor vocabulary in contracts, and §6.1.3-vs-§6.9.4's frozen inventory. My §8.2/§11 amendments state the mechanism behind them and stop short of the calls.ws2/package-colocation— physical moves and renames. I did not touch the family tree layouts infamilies/*.md, §5's target tree, or WS6's rename list.ws2/strays-followups— CHECKLIST WS2's strays row and its four sub-items.Expect a merge-down before this lands.
Out of scope for a docs-only PR — code-file defects found in passing
Reported, not fixed:
changed-coverage-exemptions.toml's WS2 header says "Thirteen lines across six files"; its entries list fifteen (1+2+5+2+1+4). Flagged in review on refactor(contracts): consolidate the Wave 2 port-inversion stack (WS2.2, WS2.4, WS5) #7018, not applied before merge.review_afterreds it) but an exemption the new pre-existing-uncovered exclusion has made redundant is silently inert. Wanted: report exemptions that matched nothing in a run where base coverage was applied..github/workflows/reborn-tests.yml:782-784still says "While[global].enforce = false… this always exits 0 (dry-run soak period)" — the ratchet has been enforcing since the recapture.FULL_PR_PATHSlistscoverage-floor.tomlbut notchanged-coverage-exemptions.toml, so a PR that only edits the exemption manifest does not run the gate that consumes it.reborn_restructure_baselines.rs:10-12says the exception count is "now 15" (13) and the specificity allowlist is 130 (129).classify-test-scope.shtreats any.mdundercrates/as core code. Measured on this PR's own diff:crates/AGENTS.md→docs_only=false,has_core_code=true, so a documentation-only change runs the full Reborn matrix (a.mdunderdocs/correctly yieldsdocs_only=true). This errs in the fail-safe direction — the exact opposite of the Path-keyed CI gates that survive #6946: six silent + two loud, all blocking the first family git mv #6963 class, where a gate scanned nothing and reported success — so it needs no fix on safety grounds. It is recorded because this program ships guidance edits constantly (PLAN operating principle 3 requires them to travel with the change) and every such PR pays a full-matrix run. If it is ever tightened, the tightening must not let a crate-guide edit unhook the lane that would catch a stale guide.Verification
git diff --name-only origin/main→ 13 files, all.md;grep -v '\.md$'is empty.git diff --word-diff=porcelainremoves 19 distinct tokens, every one either quoted back verbatim by the amendment that replaces it (eleven,session thread,llm_config,SseManager/AppEvent, "External actor/conversation refs, source/reply") or a punctuation shift from inserting an inline note.CARGO_INCREMENTAL=0 cargo test -p ironclaw_architecture→ 187 passed, 0 failed (run after the edits; confirmed no suite reads this doc tree).file:linecitation against mergedmainor a PR/issue pointer.🤖 Generated with Claude Code