Skip to content

Extract operator and projection ownership - #6615

Merged
ilblackdragon merged 13 commits into
mainfrom
agent/extract-operator-and-projections
Jul 24, 2026
Merged

ilblackdragon merged 13 commits into
mainfrom
agent/extract-operator-and-projections

Conversation

@ilblackdragon

@ilblackdragon ilblackdragon commented Jul 24, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Move operator/admin control-plane code into the new ironclaw_operator crate, including LLM admin, provider admin, operator logs, service lifecycle, NEAR AI login route logic, and NEAR AI MCP config language.
  • Move product runtime projection builders/streams out of composition into ironclaw_product::projection, with composition reduced to route/runtime adapters only.
  • Move shared operator LLM DTO language into ironclaw_host_api::operator_llm so CLI/product/composition do not mirror the same admin wire types.
  • Remove composition re-export shim files and route CLI/tests directly to ironclaw_operator / ironclaw_product owner crates.

Composition LOC reduction:

  • crates/ironclaw_reborn_composition overall: 123 added / 24,493 deleted = 24,370 net LOC removed.
  • Moved admin/projection/operator-observability plus deleted shim areas: 16 added / 24,262 deleted = 24,246 net LOC removed.
  • The composition mass gate now reports 94,597 LOC of 647,030 = 14.62%, well below the current 24.28% effective ceiling.
  • Remaining real composition code in this area is intentional: OpenAI-compatible serving still builds directly from RebornRuntime, and NEAR AI MCP bootstrap still adapts product-auth/extension-lifecycle composition ports. The composition re-export shim files for projections, operator logs/lifecycle, and LLM/provider admin have been deleted.

Change Type

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • CI/Infrastructure
  • Security
  • Dependencies

Linked Issue

None.

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all --benches --tests --examples --all-features -- -D warnings
  • cargo build
  • Relevant tests pass: listed below
  • cargo test --features integration if database-backed or integration behavior changed: Not applicable: no new database-backed/integration behavior.
  • Manual testing: Not applicable: crate ownership refactor only, no UI/manual workflow change.
  • If a coding agent was used and supports it, review-pr or pr-shepherd --fix was run before requesting review: Not applicable: no such local review tool is installed in this workspace.

Test Strategy

User behavior:

No intended behavior change. This is an ownership/boundary simplification: composition remains the assembly root, while operator/admin code and product projection code move to their owning crates.

Risk areas:

  • Model behavior
  • Browser
  • Side effect
  • Persistence
  • Security or permissions
  • External provider
  • Cross-component behavior

Tests added or updated:

  • Unit or contract: moved projection tests into ironclaw_product; moved NEAR AI login/MCP tests into ironclaw_operator; updated architecture ratchets for new crate/path ownership and the reduced composition public facade.
  • Reborn integration: Not applicable: no new runtime behavior path; composition lib tests cover runtime bootstrap seams affected by the ownership move.
  • Recorded fixture: Not applicable: no model request/response shape change.
  • Browser E2E: Not applicable: no frontend/browser behavior change.
  • Backend or runtime: cargo test -p ironclaw_reborn_composition --lib, provider-admin integration tests, cargo test -p ironclaw_product, cargo test -p ironclaw_product --lib, cargo test -p ironclaw_operator, cargo test -p ironclaw_architecture.
  • Live canary: Not applicable: no live provider behavior change.

What the tests prove:

The moved operator/admin services still compile and pass their original unit coverage in ironclaw_operator; product projection behavior still passes the projection stream tests from its new owning crate, including tenant/user-scoped outbound state isolation; composition runtime/bootstrap tests continue passing through direct owner-crate imports; architecture ratchets accept the new dependency, public facade, and specificity boundaries. The channel-connection projection regression also proves model-visible lifecycle search keeps the typed web_generated_code strategy while omitting manifest-authored presentation copy. Restored auth-challenge stream tests now live under ironclaw_product::projection, covering OAuth URL enrichment, pairing context projection, manual-token fallback, retired pairing fallback, OAuth-without-URL fallback, and provider lookup failure.

Commands run:

  • cargo check -p ironclaw_operator
  • cargo test -p ironclaw_operator
  • cargo clippy -p ironclaw_operator --all-targets --all-features -- -D warnings
  • cargo check -p ironclaw_product
  • cargo test -p ironclaw_product
  • cargo test -p ironclaw_product projection_outbound_store_mount_is_tenant_user_scoped --lib
  • cargo test -p ironclaw_product projection::tests::turn_stream_auth --lib
  • cargo test -p ironclaw_product --lib
  • cargo clippy -p ironclaw_product --all-targets --all-features -- -D warnings
  • cargo clippy -p ironclaw_product -p ironclaw_reborn_composition --all-targets --all-features -- -D warnings
  • cargo check -p ironclaw_reborn_composition
  • cargo test -p ironclaw_reborn_composition --lib
  • cargo check -p ironclaw
  • cargo test -p ironclaw_architecture
  • cargo fmt --all -- --check
  • git diff --check
  • cargo clippy --all --benches --tests --examples --all-features -- -D warnings
  • cargo clippy -p ironclaw_reborn_composition -p ironclaw --all-targets --all-features -- -D warnings
  • cargo test -p ironclaw_reborn_composition --test provider_admin --test provider_admin_probe --test provider_admin_product_command
  • cargo test -p ironclaw_reborn_integration_tests --test reborn_integration_channel_connection_projection extension_search_projects_descriptor_declared_web_generated_code_guidance -- --nocapture
  • cargo test --test reborn_integration_channel_connection_projection extension_search_projects_descriptor_declared_web_generated_code_guidance -- --nocapture (latest focused re-run after stripping manifest-authored copy from model-visible WebGeneratedCode lifecycle output)
  • cargo test -p ironclaw_reborn_composition extension_host::extension_lifecycle_capabilities::tests::model_visible_extension_search_omits_channel_connection_failure_copy --lib
  • cargo build
  • scripts/pre-commit-safety.sh

Security Impact

No policy weakening intended. Operator lifecycle/log/admin code moved crates but keeps the same authorization checks and product-command boundaries. NEAR AI login descriptor construction now returns typed errors instead of using production expect.

Reborn Trust-Boundary Checklist

  • Public policy/evidence/trust-bearing types: who can construct them? No new trust-bearing authority types; operator route mount payloads carry routers/descriptors only and are adapted by composition.
  • Untrusted content enters prompts only through an envelope/escaping primitive. No prompt construction changes.
  • Hashes declare purpose; trust/binding/authenticity uses SHA-256/BLAKE3 or separate authenticity check. No hash/authenticity changes.
  • New/changed status, exit, policy, runtime, or error variants: downstream match sites audited. Command/output: no new status/policy/runtime variants; architecture and clippy checks passed.
  • Security/durability serde(default) fields fail closed or have migration tests. No schema/default additions.
  • Queues/maps/buffers/counters have bounds and overflow-safe arithmetic. Existing moved implementations preserve their bounds.
  • Driver/operator-visible errors have stable class semantics (Transient, Permanent, Misconfigured, PolicyDenied or equivalent). Existing operator-visible error semantics preserved; NEAR AI route descriptor errors now propagate through RebornBuildError.
  • Sandbox/native/host names accurately describe trust boundary. New crate is named ironclaw_operator for host/operator control-plane ownership.

Database Impact

None. No migrations or schema changes.

Blast Radius

Composition assembly, CLI admin/model/onboard paths, operator logs/service lifecycle, LLM admin/provider setup, NEAR AI login/MCP bootstrap, product projection streams, and architecture boundary tests. Main risk is missing a path during crate ownership rewiring; mitigated by product/operator/composition tests, CLI check, architecture tests, provider-admin integration tests, and clippy.

Rollback Plan

Revert this PR. The change is a crate ownership refactor with no migration or persisted schema changes, so rollback restores the previous composition-owned implementation layout.

Review Follow-Through

Requested ironloop review through GitHub, but GitHub rejected ironloop / IronLoop as non-collaborator. Added a PR comment tagging @IronLoop with that blocker. Known follow-up: OpenAI-compatible serving and the NEAR AI MCP lifecycle bootstrap still contain real composition adapters because they depend directly on RebornRuntime, product-auth, and extension lifecycle ports.


Review track: B (feature/maintainer-requested refactor)

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

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

@ironloopai

ironloopai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

🔎 IronLoop Review Status

Head: 0abefc4be9bf3b9a0a964b21bf08c8f148e308dc
Result: One or more review results were superseded by a newer PR head.
Next: Run @ironloopai review on the latest PR head.
Updated: 2026-07-24T17:06:02.512Z

Current reviewers:

Reviewer State Verdict Findings Last update
ironloop/common-reviewer (reviewer) Superseded N/A N/A 2026-07-24T15:34:01.502Z
Reviewer summaries
Reviewer Detail
ironloop/common-reviewer (reviewer) Superseded by a newer PR head. New head: 332dc8c. Previous verdict: Needs validation.
Recent activity
Time Reviewer State Detail
2026-07-24T08:31:24.597Z ironloop/common-reviewer (reviewer) Superseded A newer PR head replaced this review (c94e9d1).
2026-07-24T08:31:39.309Z ironloop/common-reviewer (reviewer) Queued Accepted review request for head c94e9d1.
2026-07-24T08:31:39.309Z ironloop/common-reviewer (reviewer) Queued Waiting for this reviewer lane to become available.
2026-07-24T08:31:39.922Z ironloop/common-reviewer (reviewer) Started Reviewer worker started.
2026-07-24T08:31:42.297Z ironloop/common-reviewer (reviewer) Workspace ready Prepared isolated checkout (merge_ref) at 3437fdf.
2026-07-24T08:37:18.918Z ironloop/common-reviewer (reviewer) Result captured Needs validation; 0 blocking findings.
2026-07-24T08:37:18.918Z ironloop/common-reviewer (reviewer) Completed Review completed and terminal status was persisted.
2026-07-24T15:34:01.502Z ironloop/common-reviewer (reviewer) Superseded A newer PR head replaced this review (332dc8c).
Available commands
  • @ironloopai help
  • @ironloopai agents
  • @ironloopai review
  • @ironloopai review --agent <agent>
Run metadata

Admission: webhook accepted the request and IronLoop persisted reviewer state before this projection.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 06:28 Destroyed
@github-actions github-actions Bot added scope: dependencies Dependency updates size: XL 500+ changed lines risk: medium Business logic, config, or moderate-risk modules contributor: core 20+ merged PRs labels Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Introduced operator LLM host API models and provider administration (listing, status, selection, probing, onboarding menu metadata).
    • Added operator-scoped LLM API key storage with catalog-based resolution and a stored-key live reload fallback.
    • Added operator log viewing with filtering, pagination, redaction, and follow.
    • Added NEAR AI OAuth callback routing and NEAR AI MCP endpoint/bootstrap support.
    • Expanded WebUI projections: capability previews, live progress streaming, failure explanations, and resumable runtime streaming.
    • Added local operator service lifecycle management.
  • Bug Fixes
    • Improved active-model detection and strengthened cursor/resume validation with safer redaction.
  • Tests
    • Expanded architecture guardrails and added coverage for NEAR AI, projections/streaming, secrets, lifecycle, and logs.

Walkthrough

The pull request introduces ironclaw_operator, relocates operator and projection functionality into dedicated crates, updates composition and CLI integrations, and adds workspace and architecture guardrails for the new dependency boundaries.

Changes

Operator and product ownership

Layer / File(s) Summary
Operator APIs and administration
crates/ironclaw_operator/..., crates/ironclaw_host_api/src/operator_llm.rs
Adds public provider DTOs, catalog resolution, secret storage, provider administration, reload, MCP, login, logging, and service lifecycle implementations.
Product projection pipeline
crates/ironclaw_product/src/projection/...
Adds public projection services, capability previews, live progress, runtime replay, turn-event handling, cursor validation, and extensive tests.
Composition delegation
crates/ironclaw_reborn_composition/...
Replaces local operator/projection implementations with delegation and re-exports from ironclaw_operator and ironclaw_product.
CLI and architecture wiring
crates/ironclaw_reborn_cli/..., crates/ironclaw_architecture/tests/..., Cargo.toml
Routes CLI model, onboarding, runtime, and secret-store operations through operator APIs and updates workspace membership and boundary assertions.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Suggested reviewers: benkurrek

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is clearly related to the PR’s ownership refactor, even though it does not use the preferred Conventional Commits format.
Description check ✅ Passed The description covers the required sections and most fields, including summary, validation, test strategy, security, and rollback details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Member Author

@IronLoop requested for review.

GitHub would not accept ironloop/IronLoop as a formal requested reviewer through the review-request API because the account/team is not a collaborator on nearai/ironclaw, so I am tagging here for visibility.

@ironloopai ironloopai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ IronLoop Review: reviewer

Review at a glance

Verdict Blocking Notes Inline Head
❌ Changes requested 1 0 1 cfc70df72dc1

Head: cfc70df72dc1fd20b27563b81a127c08824e1062
Next: Fix the blocking findings, push the PR branch, then re-run this reviewer.

Run details

Status: Current
Needs human: no
Needs validation: no

Summary

The ownership move drops stream-level regression coverage for the auth-prompt projection path.

Findings

Blocking: 1 / Notes: 0

Blocking findings

1. ❌ [MEDIUM] Restore auth projection stream tests

Location: crates/ironclaw_product/src/projection/turn_events.rs:414
The refactor deletes the seven turn_stream_auth tests without adding product-side replacements. The remaining prompt contract test invokes the helper directly, so it does not exercise the ProductRuntimeProjectionStream path that reaches this BlockedAuth branch. Rehome equivalent stream-level coverage for auth challenge scoping, manual/pairing/OAuth fallbacks, and lookup failures; otherwise regressions in the moved projection wiring can pass the product suite.

Developer follow-up

After fixing this feedback:

  1. Push the fix to this PR branch.
  2. Re-run this reviewer with @ironloopai review --agent reviewer if you only changed this reviewer's findings.
  3. Re-run all reviewers with @ironloopai review when the fix may affect multiple areas.

let gate_ref_str = gate_ref.as_str().to_string();
match event.status {
TurnStatus::BlockedAuth => {
let view = auth_prompt_view_for_blocked_auth(BlockedAuthPromptRequest {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The previous turn_stream_auth suite covered this stream-level BlockedAuth projection path, but it was deleted rather than moved. Please restore equivalent coverage in ironclaw_product; direct helper tests do not cover the stream wiring, auth scoping, fallback prompt variants, or lookup-error behavior.

@railway-app

railway-app Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

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

Service Status Web Updated (UTC)
ironclaw 🕒 Building (View Logs) Web Jul 24, 2026 at 5:06 pm

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 06:42 Destroyed
@github-actions github-actions Bot added the scope: docs Documentation label Jul 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@crates/ironclaw_operator/src/llm_admin/nearai_login_serve.rs`:
- Around line 33-42: Update the comments in the constant initializers
NEARAI_CALLBACK_RATE_WINDOW_SECONDS and NEARAI_CALLBACK_RATE_MAX to remove the
SAFETY label, using invariant wording that reflects the non-zero literal
instead.

In `@crates/ironclaw_operator/src/llm_admin/provider_admin.rs`:
- Around line 539-545: Add an inline `// silent-ok: <reason>` annotation to the
intentional `Err(error)` fallback in the provider settings resolution flow
around `resolve_provider_config_from_env`, explicitly explaining why returning
`None` after debug logging is acceptable; otherwise propagate the error
consistently with `detect_env_llm` and `resolve_env_api_key`.
🪄 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: b71491fc-dd33-457e-ab80-319089fbb53f

📥 Commits

Reviewing files that changed from the base of the PR and between 672f003 and cfc70df.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/Cargo.lock
📒 Files selected for processing (66)
  • Cargo.toml
  • crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs
  • crates/ironclaw_architecture/tests/reborn_extension_specificity.rs
  • crates/ironclaw_host_api/src/lib.rs
  • crates/ironclaw_host_api/src/operator_llm.rs
  • crates/ironclaw_operator/Cargo.toml
  • crates/ironclaw_operator/src/lib.rs
  • crates/ironclaw_operator/src/llm_admin/active_model.rs
  • crates/ironclaw_operator/src/llm_admin/llm_catalog.rs
  • crates/ironclaw_operator/src/llm_admin/llm_config_service.rs
  • crates/ironclaw_operator/src/llm_admin/llm_key_store.rs
  • crates/ironclaw_operator/src/llm_admin/llm_reload.rs
  • crates/ironclaw_operator/src/llm_admin/mod.rs
  • crates/ironclaw_operator/src/llm_admin/nearai_login_serve.rs
  • crates/ironclaw_operator/src/llm_admin/nearai_mcp.rs
  • crates/ironclaw_operator/src/llm_admin/provider_admin.rs
  • crates/ironclaw_operator/src/llm_admin/provider_admin_product_command.rs
  • crates/ironclaw_operator/src/llm_admin/provider_repo.rs
  • crates/ironclaw_operator/src/llm_admin/resolved_llm.rs
  • crates/ironclaw_operator/src/operator_logs.rs
  • crates/ironclaw_operator/src/operator_service_lifecycle.rs
  • crates/ironclaw_operator/src/route_mounts.rs
  • crates/ironclaw_product/Cargo.toml
  • crates/ironclaw_product/src/lib.rs
  • crates/ironclaw_product/src/projection.rs
  • crates/ironclaw_product/src/projection/display_preview.rs
  • crates/ironclaw_product/src/projection/live_progress.rs
  • crates/ironclaw_product/src/projection/runtime_replay.rs
  • crates/ironclaw_product/src/projection/tests.rs
  • crates/ironclaw_product/src/projection/tests/cursor_validation.rs
  • crates/ironclaw_product/src/projection/tests/display_preview.rs
  • crates/ironclaw_product/src/projection/tests/display_preview_runtime.rs
  • crates/ironclaw_product/src/projection/tests/failure_explanation.rs
  • crates/ironclaw_product/src/projection/tests/live_progress_stream.rs
  • crates/ironclaw_product/src/projection/tests/runtime_stream.rs
  • crates/ironclaw_product/src/projection/tests/turn_stream.rs
  • crates/ironclaw_product/src/projection/turn_events.rs
  • crates/ironclaw_reborn_cli/Cargo.toml
  • crates/ironclaw_reborn_cli/src/commands/config/set.rs
  • crates/ironclaw_reborn_cli/src/commands/models.rs
  • crates/ironclaw_reborn_cli/src/commands/onboard/llm_credentials.rs
  • crates/ironclaw_reborn_cli/src/commands/serve.rs
  • crates/ironclaw_reborn_cli/src/runtime/mod.rs
  • crates/ironclaw_reborn_cli/tests/smoke.rs
  • crates/ironclaw_reborn_composition/Cargo.toml
  • crates/ironclaw_reborn_composition/src/llm_admin/active_model.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/llm_catalog.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/llm_config_service.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/llm_key_store.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/llm_reload.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/nearai_login_serve.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/nearai_mcp.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/provider_admin.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/provider_admin_product_command.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/provider_repo.rs
  • crates/ironclaw_reborn_composition/src/observability/operator_logs.rs
  • crates/ironclaw_reborn_composition/src/observability/operator_service_lifecycle.rs
  • crates/ironclaw_reborn_composition/src/projection.rs
  • crates/ironclaw_reborn_composition/src/projection/display_preview.rs
  • crates/ironclaw_reborn_composition/src/projection/live_progress.rs
  • crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs
  • crates/ironclaw_reborn_composition/src/projection/tests/turn_stream_auth.rs
  • crates/ironclaw_reborn_composition/src/projection/turn_events.rs
  • crates/ironclaw_reborn_composition/src/runtime.rs
  • crates/ironclaw_reborn_composition/src/runtime/tests/core.rs
  • crates/ironclaw_reborn_composition/src/runtime_input.rs

Comment thread crates/ironclaw_operator/src/llm_admin/nearai_login_serve.rs
Comment thread crates/ironclaw_operator/src/llm_admin/provider_admin.rs
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 06:53 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
crates/ironclaw_reborn_cli/src/commands/models.rs (1)

69-86: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Keep model commands behind the composition provider-admin facade.

These commands now instantiate ironclaw_operator::RebornProviderAdmin directly, creating a second CLI-specific path that can bypass composition-owned profile, policy, and compatibility checks. Route model UX through the Reborn composition provider-admin facade and retain architecture coverage preventing direct operator construction in CLI code.

As per path instructions, provider registry, authentication, and model UX must enter through the Reborn composition provider-admin facade.

Also applies to: 90-101, 105-111, 114-121

🤖 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_reborn_cli/src/commands/models.rs` around lines 69 - 86,
Update the model command methods, including execute and the additional
provider/model handlers, to obtain and invoke the Reborn composition
provider-admin facade instead of constructing
ironclaw_operator::RebornProviderAdmin directly. Preserve the existing listing,
detail, JSON, and verbosity behavior while routing all provider registry,
authentication, and model UX through the composition-owned facade, and retain or
add architecture coverage preventing direct operator construction in CLI code.

Source: Path instructions

crates/ironclaw_reborn_composition/src/llm_admin/nearai_mcp.rs (1)

11-20: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Remove the internal operator re-export shim.

pub(crate) use ironclaw_operator::... preserves the old composition module as a path-preservation layer. Import these symbols directly from ironclaw_operator at their consumers and keep this module limited to composition-owned adapters.

As per path instructions, extracted-crate types must be imported directly and re-exports are reserved for downstream API facades.

🤖 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_reborn_composition/src/llm_admin/nearai_mcp.rs` around lines
11 - 20, Remove the pub(crate) operator re-export block from the nearai_mcp
composition module, including NearAiMcpBootstrapOutcome, NearAiMcpEndpoint,
durable_product_auth_storage_enabled, and the nearai_mcp helper functions.
Update each consumer to import these symbols directly from ironclaw_operator,
leaving this module only with composition-owned adapters and its direct
NearAiMcpBootstrapConfig import.

Source: Path instructions

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

Inline comments:
In `@crates/ironclaw_reborn_cli/src/commands/models.rs`:
- Line 124: Update the CLI’s provider DTO references in print_provider_list and
the corresponding code at the indicated additional locations to import
RebornProviderList, RebornProviderStatus, and RebornProviderWriteOutcome from
ironclaw_host_api::operator_llm. Retain ironclaw_operator only for
administration implementation APIs, removing any reliance on its DTO re-exports.

In `@crates/ironclaw_reborn_composition/src/observability/mod.rs`:
- Line 8: Remove the OperatorServiceLifecycle re-export from observability and
update all callers to import it directly from ironclaw_operator. In
available_extensions.rs, import the complete NEAR AI MCP endpoint/helper set
from ironclaw_operator, or rename any intentional composition adapter to a
distinct contract name; do not preserve the old composition paths.

---

Outside diff comments:
In `@crates/ironclaw_reborn_cli/src/commands/models.rs`:
- Around line 69-86: Update the model command methods, including execute and the
additional provider/model handlers, to obtain and invoke the Reborn composition
provider-admin facade instead of constructing
ironclaw_operator::RebornProviderAdmin directly. Preserve the existing listing,
detail, JSON, and verbosity behavior while routing all provider registry,
authentication, and model UX through the composition-owned facade, and retain or
add architecture coverage preventing direct operator construction in CLI code.

In `@crates/ironclaw_reborn_composition/src/llm_admin/nearai_mcp.rs`:
- Around line 11-20: Remove the pub(crate) operator re-export block from the
nearai_mcp composition module, including NearAiMcpBootstrapOutcome,
NearAiMcpEndpoint, durable_product_auth_storage_enabled, and the nearai_mcp
helper functions. Update each consumer to import these symbols directly from
ironclaw_operator, leaving this module only with composition-owned adapters and
its direct NearAiMcpBootstrapConfig import.
🪄 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: ad49718a-3aa3-4dd9-8227-0d7d36d45af4

📥 Commits

Reviewing files that changed from the base of the PR and between cfc70df and 3bfabce.

📒 Files selected for processing (32)
  • crates/ironclaw_architecture/tests/reborn_extension_specificity.rs
  • crates/ironclaw_reborn_cli/src/commands/config/init.rs
  • crates/ironclaw_reborn_cli/src/commands/doctor.rs
  • crates/ironclaw_reborn_cli/src/commands/models.rs
  • crates/ironclaw_reborn_cli/src/commands/onboard/llm_credentials.rs
  • crates/ironclaw_reborn_cli/src/commands/onboard/prompts.rs
  • crates/ironclaw_reborn_cli/src/commands/service/launchd.rs
  • crates/ironclaw_reborn_cli/src/commands/service/mod.rs
  • crates/ironclaw_reborn_cli/src/runtime/mod.rs
  • crates/ironclaw_reborn_composition/src/deployment.rs
  • crates/ironclaw_reborn_composition/src/extension_host/available_extensions.rs
  • crates/ironclaw_reborn_composition/src/extension_host/run_delivery_ports.rs
  • crates/ironclaw_reborn_composition/src/extension_host/skill_learning.rs
  • crates/ironclaw_reborn_composition/src/factory.rs
  • crates/ironclaw_reborn_composition/src/factory/tests.rs
  • crates/ironclaw_reborn_composition/src/google_oauth_secret_store.rs
  • crates/ironclaw_reborn_composition/src/input.rs
  • crates/ironclaw_reborn_composition/src/lib.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/mod.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/nearai_login_serve.rs
  • crates/ironclaw_reborn_composition/src/llm_admin/nearai_mcp.rs
  • crates/ironclaw_reborn_composition/src/observability/mod.rs
  • crates/ironclaw_reborn_composition/src/root/product_live_adapters.rs
  • crates/ironclaw_reborn_composition/src/runtime.rs
  • crates/ironclaw_reborn_composition/src/runtime/local_dev.rs
  • crates/ironclaw_reborn_composition/src/runtime/tests/core.rs
  • crates/ironclaw_reborn_composition/src/test_support/projection.rs
  • crates/ironclaw_reborn_composition/src/webui/facade.rs
  • crates/ironclaw_reborn_composition/tests/provider_admin.rs
  • crates/ironclaw_reborn_composition/tests/provider_admin_probe.rs
  • crates/ironclaw_reborn_composition/tests/provider_admin_product_command.rs
  • docs/plans/composition-pubuse.snapshot
💤 Files with no reviewable changes (4)
  • crates/ironclaw_reborn_composition/src/llm_admin/mod.rs
  • crates/ironclaw_architecture/tests/reborn_extension_specificity.rs
  • docs/plans/composition-pubuse.snapshot
  • crates/ironclaw_reborn_composition/src/lib.rs

Comment thread crates/ironclaw_reborn_cli/src/commands/models.rs Outdated
Comment thread crates/ironclaw_reborn_composition/src/observability/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In
`@crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs`:
- Around line 308-318: Remove the WebGeneratedCode exception that preserves
extension.summary.channel_connection in the model-visible projection, keeping
the redaction contract enforced by the existing model-facing path. If product/UI
consumers need this data, preserve it through a separate product projection
rather than the redacted copy, and add an ExtensionSearch regression test
confirming WebGeneratedCode connection instructions, placeholders, labels, and
errors are absent from model-visible results.
🪄 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: a7009524-ed53-4a57-b2f4-195860f6aaa0

📥 Commits

Reviewing files that changed from the base of the PR and between 3bfabce and 5614dec.

📒 Files selected for processing (1)
  • crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs

@github-actions

github-actions Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Coverage ratchet

Ratchet mode: ENFORCING

RATCHET PASS: global
  observed: 86.23% (314395 / 364600 lines)
  floor:    86.27% (tolerance 0.5pp -> effective floor 85.77%)
  denominator: 364600 lines now vs 354049 at floor capture (+10551 lines, +2.98%) — not a material change

⚠️ 2 Reborn crate(s) have 0 int-tier coverage (target: 0) — ironclaw_prompt_envelope, ironclaw_scripts

Reborn integration-tier coverage

Line coverage (Reborn crates): 86.23% — 314395 / 364600 lines

Per-crate breakdown (61 crates, lowest-covered first)
Crate Line % Covered / Total
ironclaw_prompt_envelope 0% 0 / 88
ironclaw_scripts 0% 0 / 345
ironclaw_host_ingress 42.5% 17 / 40
ironclaw_event_projections 43.71% 684 / 1565
ironclaw_observability 61.54% 16 / 26
ironclaw_telegram_v2_adapter 62.35% 631 / 1012
ironclaw_authorization 62.98% 609 / 967
ironclaw_dispatcher 64.75% 79 / 122
ironclaw_memory 70.15% 919 / 1310
ironclaw_trust 73.21% 664 / 907
ironclaw_filesystem 73.7% 4587 / 6224
ironclaw_wasm_limiter 74.6% 47 / 63
ironclaw_extractors 74.72% 538 / 720
ironclaw_capabilities 75.36% 2593 / 3441
ironclaw_mcp 76.2% 775 / 1017
ironclaw_projects 76.48% 400 / 523
ironclaw_reborn_cli 77.97% 10549 / 13529
ironclaw_telegram_extension 78.59% 962 / 1224
ironclaw_llm 78.73% 20989 / 26659
ironclaw_wasm 79.72% 735 / 922
ironclaw_process_sandbox 80.46% 671 / 834
ironclaw_memory_native 80.97% 3114 / 3846
ironclaw_first_party_extensions 82.26% 6624 / 8053
ironclaw_events 82.47% 1604 / 1945
ironclaw_operator 83.2% 5625 / 6761
ironclaw_processes 83.3% 933 / 1120
ironclaw_host_api 83.53% 8945 / 10709
ironclaw_secrets 83.79% 2548 / 3041
ironclaw_reborn_identity 83.8% 450 / 537
ironclaw_reborn_config 85.24% 2102 / 2466
ironclaw_auth 85.31% 4026 / 4719
ironclaw_common 85.53% 2252 / 2633
ironclaw_run_state 85.77% 458 / 534
ironclaw_triggers 85.92% 2783 / 3239
ironclaw_network 85.97% 913 / 1062
ironclaw_reborn_event_store 86.51% 1251 / 1446
ironclaw_hooks 86.58% 9930 / 11469
ironclaw_product 86.82% 21685 / 24977
ironclaw_extensions 87.2% 3678 / 4218
ironclaw_threads 87.2% 4851 / 5563
ironclaw_skills 87.75% 4479 / 5104
ironclaw_turns 88.06% 14312 / 16253
ironclaw_reborn_traces 88.13% 11986 / 13600
ironclaw_reborn_composition 88.36% 49088 / 55557
ironclaw_slack_extension 88.47% 1934 / 2186
ironclaw_host_runtime 88.57% 18997 / 21448
ironclaw_extension_host 89.12% 3745 / 4202
ironclaw_reborn_openai_compat 89.32% 3780 / 4232
ironclaw_resources 90.84% 4474 / 4925
ironclaw_runner 90.87% 17192 / 18920
ironclaw_webui 91.02% 9035 / 9926
ironclaw_conversations 91.1% 3202 / 3515
ironclaw_approvals 91.12% 1682 / 1846
ironclaw_event_streams 91.24% 1063 / 1165
ironclaw_loop_host 91.9% 16503 / 17958
ironclaw_attachments 93.06% 630 / 677
ironclaw_outbound 94.43% 3987 / 4222
ironclaw_agent_loop 94.88% 9837 / 10368
ironclaw_safety 95.15% 3749 / 3940
ironclaw_first_party_extension_ports 95.62% 3672 / 3840
ironclaw_runtime_policy 96.55% 811 / 840

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 (3 entry/entries excluded from the accounting above)
Module / Crate Reason Issue
crate: ironclaw_embeddings v1-only: consumed only by root ironclaw (src/app.rs, src/tools/builtin/memory.rs, src/workspace/mod.rs, src/config/{mod,embeddings}.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_gateway v1-only: consumed only by root ironclaw (src/channels/web/platform/static_files.rs, src/channels/web/handlers/frontend.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_tui v1-only: consumed only by root ironclaw (src/main.rs, src/channels/tui.rs); no crates/* dependents. Crate's own doc comment confirms it bridges INTO v1, not Reborn. Covered by "Tests (Legacy)". #5657

@ilblackdragon

Copy link
Copy Markdown
Member Author

@ironloopai review --agent reviewer

@ironloopai ironloopai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ IronLoop Review: reviewer

Review at a glance

Verdict Blocking Notes Inline Head
❌ Changes requested 1 0 1 5614decdd77a

Head: 5614decdd77a510ab55696fa147922ce938ee919
Next: Fix the blocking findings, push the PR branch, then re-run this reviewer.

Run details

Status: Current
Needs human: no
Needs validation: no

Summary

The ownership refactor drops structural tenant/user isolation for the projection EventStreamManager’s outbound state store.

Findings

Blocking: 1 / Notes: 0

Blocking findings

1. ❌ [HIGH] Restore scope-derived outbound state mounts

Location: crates/ironclaw_product/src/projection.rs:256
This resolver ignores ResourceScope and maps every caller to /engine/outbound. Before the move, build_reborn_projection_services used composition's wrap_scoped, which rewrites /outbound under /tenants/<tenant>/users/<user>. The OutboundStateStore backing this shared EventStreamManager relies on that mount boundary for structural isolation, including list/query operations. Restore a tenant/user-derived mount view here (or inject the scoped filesystem from composition) so one user's projection/delivery state cannot share another user's subtree.

Developer follow-up

After fixing this feedback:

  1. Push the fix to this PR branch.
  2. Re-run this reviewer with @ironloopai review --agent reviewer if you only changed this reviewer's findings.
  3. Re-run all reviewers with @ironloopai review when the fix may affect multiple areas.

Arc::new(ScopedFilesystem::new(root, outbound_mount_view))
}

fn outbound_mount_view(_scope: &ResourceScope) -> Result<MountView, HostApiError> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This resolver ignores ResourceScope and maps every caller to /engine/outbound. The previous composition wrap_scoped rewrote this alias under /tenants/<tenant>/users/<user>, which OutboundStateStore relies on for structural isolation. Please restore scope-derived mounting (or inject it) rather than sharing this subtree.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 07:57 Destroyed
@ilblackdragon

Copy link
Copy Markdown
Member Author

@ironloopai review --agent reviewer

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@crates/ironclaw_product/src/projection/tests.rs`:
- Around line 79-98: Extend the isolation test around scope_a and scope_b to
validate each scope dimension independently: add a same-tenant/different-user
scope and a different-tenant/same-user scope, then assert both cannot read the
data written through scope_a. Keep the existing both-dimensions-different
assertion and use the same shared outbound path and resource setup.
🪄 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: 7f9fd6cd-92f9-44ff-9cac-c3a7d1fa3420

📥 Commits

Reviewing files that changed from the base of the PR and between 5614dec and d412db6.

📒 Files selected for processing (2)
  • crates/ironclaw_product/src/projection.rs
  • crates/ironclaw_product/src/projection/tests.rs

Comment thread crates/ironclaw_product/src/projection/tests.rs

@ironloopai ironloopai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ IronLoop Review: reviewer

Review at a glance

Verdict Blocking Notes Inline Head
❌ Changes requested 1 0 1 d412db62a107

Head: d412db62a10744a68d95a549e57f16c31ce5071b
Next: Fix the blocking findings, push the PR branch, then re-run this reviewer.

Run details

Status: Current
Needs human: no
Needs validation: no

Summary

The extraction is largely mechanical, but the generated-code exception exposes untrusted extension-manifest text in model-visible lifecycle output.

Findings

Blocking: 1 / Notes: 0

Blocking findings

1. ❌ [HIGH] Keep imported channel copy out of model-visible search output

Location: crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs:314
WebGeneratedCode now retains the entire manifest-derived channel_connection in the value serialized for model-visible extension_search. The catalog accepts imported InstalledLocal bundles, while these fields (including instructions, labels, and error text) are only validated as non-empty. A third-party bundle can therefore inject arbitrary directives into model context. Strip this connection data as before, or replace it with host-authored/enveloped guidance, and add a regression test using hostile imported-manifest text.

Developer follow-up

After fixing this feedback:

  1. Push the fix to this PR branch.
  2. Re-run this reviewer with @ironloopai review --agent reviewer if you only changed this reviewer's findings.
  3. Re-run all reviewers with @ironloopai review when the fix may affect multiple areas.

.as_ref()
.is_some_and(|connection| {
connection.strategy
== ironclaw_product::RebornChannelConnectStrategy::WebGeneratedCode

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Leaving WebGeneratedCode summaries here makes model-visible extension_search include manifest-provided instructions, labels, and error text. Imported third-party bundles can control these non-empty-only-validated fields, so this creates a prompt-injection path. Keep channel_connection out of the model output (or use host-authored/enveloped guidance) and add a regression test.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 08:13 Destroyed
…r-and-projections

# Conflicts:
#	crates/ironclaw_product/src/projection/tests/nested_dispatch_stream.rs
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 15:34 Destroyed
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 16:12 Destroyed
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 16:28 Destroyed
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 16:47 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (4)
crates/ironclaw_reborn_composition/src/factory.rs (1)

2598-2606: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use a stable namespace for local-dev mem0 memory.

The local-dev app_id is the mem0 partition key, but DefaultHasher is not stable across Rust releases/platforms. A toolchain/platform upgrade using the same storage root can scope memories to a different namespace and make existing local-dev memory appear missing. Store a workspace ID or use a stable, versioned digest with migration/compatibility handling.

Rule cited: persisted state must remain reconstructible after interruption (.claude/rules/database.md:67).

🤖 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_reborn_composition/src/factory.rs` around lines 2598 - 2606,
Replace the DefaultHasher-based app_id generation in the memory_service_resolver
block with a stable, versioned namespace derived from the local-dev storage
root, or reuse a persisted workspace ID. Add compatibility handling or migration
for namespaces previously generated by the old hash so existing mem0 memories
remain accessible across Rust toolchain and platform changes.

Source: Coding guidelines

crates/ironclaw_reborn_cli/Cargo.toml (1)

26-33: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove the forwarding-only CLI feature.

memory-mem0 in crates/ironclaw_reborn_cli/Cargo.toml:32 is only ironclaw_reborn_composition/memory-mem0, which violates the workspace Cargo-feature rule: forwarding features belong on the dependency declaration, and this alias has no direct CLI consumers. Select ironclaw_reborn_composition/memory-mem0 from the build invocation instead, or make this a CLI-owned build shape with another consumer.

🤖 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_reborn_cli/Cargo.toml` around lines 26 - 33, Remove the
forwarding-only memory-mem0 feature from the CLI crate’s [features] section.
Update the build invocation or dependency configuration to select
ironclaw_reborn_composition/memory-mem0 directly, unless the CLI gains a direct
consumer that justifies retaining a CLI-owned feature.

Source: Coding guidelines

crates/ironclaw_reborn_composition/src/lib.rs (1)

173-177: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Keep concrete memory factories out of the composition facade. Mem0ConnectionConfig, MemoryProviderDeps, and provider-construction functions expose filesystem/transport wiring rather than facade-shaped composition handles.

  • crates/ironclaw_reborn_composition/src/lib.rs#L173-L177: make these crate-private or move the public factory contract to its owning memory/configuration crate.
  • docs/plans/composition-pubuse.snapshot#L84-L88: remove the corresponding public snapshot entries after narrowing the facade.

As per coding guidelines, “Own only top-level Reborn composition … [and] expose only facade-shaped handles.”

🤖 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_reborn_composition/src/lib.rs` around lines 173 - 177, Narrow
the composition facade exports in memory_provider_factory: make
Mem0ConnectionConfig, MemoryProviderDeps, build_memory_service_resolver, and
create_document_store_provider crate-private or relocate their public contract
to the owning memory/configuration crate. Remove the corresponding entries from
docs/plans/composition-pubuse.snapshot at lines 84-88; no other facade-shaped
exports should change.

Source: Coding guidelines

crates/ironclaw_reborn_composition/src/runtime.rs (1)

3971-3979: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pass the native-memory audit sink to resolve_document_store.

NativeMemoryService::from_filesystem passes the sink into RepositoryMemoryBackend; threads/<turn_run_id>.md writes use that backend path and emit checked/warned/bypass safety events. With None, native-after-turn writes on protected paths can fail to emit required events. Use the composition-local audit sink during resolver construction and keep the same None behavior only where the production tool path is already covered.

🤖 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_reborn_composition/src/runtime.rs` around lines 3971 - 3979,
Update the local runtime document-store resolution around
memory_service_resolver.resolve_document_store to pass the composition-local
native-memory audit sink instead of None. Preserve the existing filesystem
argument and ensure the sink used by NativeMemoryService::from_filesystem and
RepositoryMemoryBackend reaches this resolver, retaining None only for the
already-covered production tool path.

Source: Coding guidelines

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

Outside diff comments:
In `@crates/ironclaw_reborn_cli/Cargo.toml`:
- Around line 26-33: Remove the forwarding-only memory-mem0 feature from the CLI
crate’s [features] section. Update the build invocation or dependency
configuration to select ironclaw_reborn_composition/memory-mem0 directly, unless
the CLI gains a direct consumer that justifies retaining a CLI-owned feature.

In `@crates/ironclaw_reborn_composition/src/factory.rs`:
- Around line 2598-2606: Replace the DefaultHasher-based app_id generation in
the memory_service_resolver block with a stable, versioned namespace derived
from the local-dev storage root, or reuse a persisted workspace ID. Add
compatibility handling or migration for namespaces previously generated by the
old hash so existing mem0 memories remain accessible across Rust toolchain and
platform changes.

In `@crates/ironclaw_reborn_composition/src/lib.rs`:
- Around line 173-177: Narrow the composition facade exports in
memory_provider_factory: make Mem0ConnectionConfig, MemoryProviderDeps,
build_memory_service_resolver, and create_document_store_provider crate-private
or relocate their public contract to the owning memory/configuration crate.
Remove the corresponding entries from docs/plans/composition-pubuse.snapshot at
lines 84-88; no other facade-shaped exports should change.

In `@crates/ironclaw_reborn_composition/src/runtime.rs`:
- Around line 3971-3979: Update the local runtime document-store resolution
around memory_service_resolver.resolve_document_store to pass the
composition-local native-memory audit sink instead of None. Preserve the
existing filesystem argument and ensure the sink used by
NativeMemoryService::from_filesystem and RepositoryMemoryBackend reaches this
resolver, retaining None only for the already-covered production tool path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7f82a905-c9d0-4442-a143-8681966261f6

📥 Commits

Reviewing files that changed from the base of the PR and between 3d44f61 and aebcdc1.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/Cargo.lock
📒 Files selected for processing (15)
  • Cargo.toml
  • crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs
  • crates/ironclaw_host_api/src/lib.rs
  • crates/ironclaw_product/src/projection/display_preview.rs
  • crates/ironclaw_product/src/projection/tests/display_preview.rs
  • crates/ironclaw_reborn_cli/Cargo.toml
  • crates/ironclaw_reborn_cli/src/runtime/mod.rs
  • crates/ironclaw_reborn_composition/Cargo.toml
  • crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs
  • crates/ironclaw_reborn_composition/src/factory.rs
  • crates/ironclaw_reborn_composition/src/input.rs
  • crates/ironclaw_reborn_composition/src/lib.rs
  • crates/ironclaw_reborn_composition/src/runtime.rs
  • crates/ironclaw_reborn_composition/src/runtime/local_dev.rs
  • docs/plans/composition-pubuse.snapshot
💤 Files with no reviewable changes (1)
  • crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6615 July 24, 2026 17:06 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs (1)

295-309: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restore full redaction for model-visible search results.

Clearing only error_message leaves manifest-controlled display_name, strategy, instructions, input_placeholder, and submit_label in model-visible output. Imported bundles can control these fields, reintroducing the prompt-injection path rejected by the existing model-visible redaction invariant and prior review.

Keep the full connection requirement on the product/display-preview path, but set extension.summary.channel_connection = None for ExtensionSearch; update the Telegram assertion at Line 745 through Line 752 accordingly.

Proposed fix
         Some(LifecycleProductPayload::ExtensionSearch { extensions, .. }) => {
             for extension in extensions {
-                if let Some(connection) = extension.summary.channel_connection.as_mut() {
-                    connection.error_message.clear();
-                }
+                extension.summary.channel_connection = None;
             }
         }
🤖 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_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs`
around lines 295 - 309, Update without_model_visible_connection_chrome so
ExtensionSearch entries set extension.summary.channel_connection to None instead
of only clearing error_message, fully redacting manifest-controlled connection
guidance from model-visible search results. Preserve the full connection
requirement for the product/display-preview path, and update the Telegram
assertion in the relevant lifecycle test to expect no channel_connection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs`:
- Around line 295-309: Update without_model_visible_connection_chrome so
ExtensionSearch entries set extension.summary.channel_connection to None instead
of only clearing error_message, fully redacting manifest-controlled connection
guidance from model-visible search results. Preserve the full connection
requirement for the product/display-preview path, and update the Telegram
assertion in the relevant lifecycle test to expect no channel_connection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 03e43583-9954-4b39-9bab-1e7d75ba66f1

📥 Commits

Reviewing files that changed from the base of the PR and between aebcdc1 and 0abefc4.

⛔ Files ignored due to path filters (3)
  • tests/snapshots/golden_payload__context_surfacing.snap is excluded by !**/*.snap, !tests/snapshots/**
  • tests/snapshots/golden_payload__parallel_tool_calls.snap is excluded by !**/*.snap, !tests/snapshots/**
  • tests/snapshots/golden_payload__tool_call.snap is excluded by !**/*.snap, !tests/snapshots/**
📒 Files selected for processing (1)
  • crates/ironclaw_reborn_composition/src/extension_host/extension_lifecycle_capabilities.rs

@ilblackdragon
ilblackdragon merged commit 5efb0e2 into main Jul 24, 2026
62 checks passed
@ilblackdragon
ilblackdragon deleted the agent/extract-operator-and-projections branch July 24, 2026 17:36
BenKurrek added a commit that referenced this pull request Jul 27, 2026
… composition

Continues the direction of #6615/#6616/#6619: composition is assembly, and
these files were not assembly. Backend selection already happened upstream —
composition hands them a `ScopedFilesystem`. What is left is contract policy
owned by the port they implement: alias confinement with the explicit
sibling-prefix guard, sensitive-filename omission from listings, the
TOCTOU-hardened two-stage size guard, extension→MIME derivation that must
match the download `Content-Type`, and the substrate→port error sanitization
table (including the deliberate MountNotFound→503 vs Contract→400 split).

`ProjectScopedFilesystemReader` and the attachment lander/reader therefore
move to `ironclaw_product::scoped_fs`, beside the `ProjectFilesystemReader` /
`InboundAttachmentLander` traits they implement. Every import they need was
already a production dependency of that crate, and the in-crate precedent is
`filesystem_ledger.rs`, which likewise hosts a generic `ScopedFilesystem`
implementation of a product-owned port.

`mount_filesystem_reader.rs` deliberately stays in composition: its
`alias_for(FsMount)` table is the "which mounts does this deployment serve"
decision, which is composition's job. It now consumes the shared scoped-path
helpers from the owner crate by name.

Composition src: 66,998 → 66,208 LOC (10.39% → 10.27% of production).

Two gates caught this change and were fixed rather than silenced: the struct
ratchet entry follows the file to its new path, and the extension-specificity
gate rejected a comment naming a concrete vendor in generic code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK
BenKurrek added a commit that referenced this pull request Jul 31, 2026
* feat(channels): add attachment transfer vocabulary to egress descriptors

Re-applied from codex/telegram-slack-attachments: ironclaw_attachments
materialized-file/budget/workspace-ref types + ChannelEgressDescriptor
paths/path_prefixes/body-limit bounds with fail-closed validation.

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

* feat(channels): transfer inbound/outbound channel attachments through restricted egress

Re-applied from codex/telegram-slack-attachments onto the restructured
tree: ChannelAdapter::fetch_attachment seam + AttachmentTransfer error in
ironclaw_host_api's product_adapter contract; envelope-transient
channel_attachment_refs; ChannelInboundProductSurface transfer door with
fail-closed default; post-policy fetch/validate/land orchestration in
ironclaw_product's inbound turn service; workspace-file materialization in
the delivery coordinator; Telegram getFile/download + sendDocument via the
manifest's path-constrained egress; Slack fails closed both directions;
composition wiring for the per-request policy-enforced channel egress.

InboundAttachment/MaterializedFile moved down into ironclaw_host_api (the
trait contract owner) because ironclaw_attachments depends on host_api.

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

* test(reborn): prove channel attachment journeys on the production mount

Relocates the composition-resident attachment journey coverage to the
extension-delivery integration lane per the tests/integration-first rule:
the telegram delivery scenario now drives a document update through the
production ingress mount — transient getFile failure releases the ledger
attempt (503), the vendor redelivery refetches through the manifest's
path-constrained egress with the token injected host-side, bytes land at
the canonical /workspace/attachments ref exactly once, duplicate replay
does no vendor I/O, and a follow-up conversation's final reply
referencing the landed file is materialized through the real
project-scoped reader and delivered natively via sendDocument. The
envelope's transient-refs serde(skip) contract is pinned beside the type
in ironclaw_host_api; sink-level door routing and inherited fail-closed
transfer stay as local contract tests beside the moved code.

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

* chore(reborn): quality-gate fixes for the attachment re-application

Bundle the delivery coordinator's materialization inputs (clippy arg
budget), reuse the VendorResponseRouter alias, drop a dead test accessor,
update the ingress contract fixture to the current manifest schema
(admin_configuration-declared verification handle), and annotate
large-file growth per the arch-sprawl gate.

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

* chore(telegram): move channel test modules under src/tests/

The no-panics production gate exempts src/**/tests/*.rs; the flat
channel_*_tests.rs siblings were test-only (cfg(test) #[path] mounts)
but not recognizable as such by the path heuristic.

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

* fix(channels): correct attachment transfer bugs and retire the large-file exempts

Audit follow-ups on the attachment transfer path.

Functional fixes, each with a regression test that fails without it:

- The fetched/declared MIME check normalized only the fetched side, so a
  descriptor carrying a parameter (`text/plain; charset=utf-8` — what
  Telegram clients routinely report for text documents) never matched and
  rejected the whole message, caption included. Compare canonical forms on
  both sides so the check catches a real provider mismatch instead.
- The descriptor filename overwrote the fetched one unconditionally,
  discarding the name the adapter recovers from the `getFile` path for
  payloads that carry none (photos, voice notes, stickers). Keep it when
  the descriptor has no filename.
- The multipart boundary was derived from `bytes.len()` and searched in a
  `0..=u32::MAX` loop with a full payload scan per iteration. The payload is
  attacker-authored (an inbound attachment can be landed and later referenced
  by a reply), so a sender could pad a file with collisions and force
  unbounded rescans. Use a v4 UUID and scan once.
- The manifest declared 5 MiB transfer caps while the code enforced the
  10 MiB host budget, so a file in between passed every code check and was
  then refused at egress — inbound reported as "denied" rather than "too
  large", outbound after the coordinator had committed to the send. Derive
  one bound (TELEGRAM_MAX_TRANSFER_BYTES), pre-flight the assembled
  multipart body against the declared request cap, map ResponseTooLarge to
  a size error, and pin constants to the manifest with a test.
- The Bot API target's 64 KiB response cap also covered sendMessage, whose
  response echoes `reply_to_message` now that replies are threaded; an
  oversize echo failed a send the user had already received. Keep the host
  default there and document why.
- The two fail-closed defaults for "this layer cannot transfer attachments"
  disagreed: the product surface said retryable, the inbound turn service
  said permanent. Retryable left the vendor redelivering forever while the
  user got nothing at all. Both are permanent now; a missing deployment
  egress transport stays retryable as an operator-fixable condition, and a
  permanent Invalid outcome is logged rather than settling silently.

Security and hygiene:

- `path_prefixes` matched by raw byte prefix, so a declared
  `/file/bot{token}` also authorized `/file/bot{token}Evil/…`. Require a
  trailing `/` at descriptor validation and match on a segment boundary.
- `InboundAttachment` (new in this PR) derived `Debug` over raw bytes while
  its sibling in the same file hand-writes a redacting one with a leak test.
  Both now redact, and `ProjectFsFile` — the wire type — does too.
- `MaterializedFile<P>` had exactly one instantiation; collapsed to
  `WorkspaceFile`.
- Removed 39 stale committed frontend build artifacts (3.5 MB) under
  `crates/ironclaw_webui_v2/`, a crate folded into `ironclaw_webui`; nothing
  reads the path. Closed the `.gitignore` gap that admitted them.
- Dropped all four `arch-exempt: large_file` annotations by shrinking the
  files instead: two were spurious (one on a 1,242-line file the 1,500-line
  gate never fires on, one licensing a semantically no-op edit), and the
  exempt grep scans the whole file body, so each would have disabled the
  gate for that file permanently. Test modules carved into their own files
  per the composition budget's own carve-out guidance.
- Deleted a sink test whose distinguishing assertion read the test double's
  own payload construction; the telegram journey covers door selection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(attachments): close the egress prefix bypass and collapse duplicated vocabulary

Security:

- `path_prefixes` matched by raw byte prefix, so a declared
  `/file/bot{token}` also authorized `/file/bot{token}Evil/…` — a sibling
  path on the same pinned host and credential that the manifest author never
  allowed. Descriptor validation now requires the prefix to end on a segment
  boundary, and the matcher enforces the boundary independently so a prefix
  reaching policy by another route still cannot authorize a sibling.

Duplicated vocabulary, per .claude/rules/type-placement.md:

- `mime_hint` was written in two adapters as a verbatim copy of
  `descriptor.mime_type` and read in zero production locations. Deleted with
  its writers; the one new production consumer already reads the descriptor.
- `AttachmentRef` named two different concepts — the durable byte-free
  transcript reference and this transient vendor fetch reference — which
  forced an `as ChannelAttachmentRef` import alias where both appeared. The
  channel one is now `ChannelAttachmentRef` at its definition and the alias
  is gone.
- `ProductAttachmentCapabilities` re-declared `AttachmentBudgets`' three
  fields and hand-copied each. Embedded with `#[serde(flatten)]`; the JSON
  shape is unchanged (the wire assertions still read the same keys) and a new
  budget field now reaches the browser with no intermediate edit.
- The `/workspace` prefix was defined independently in `ironclaw_attachments`
  (deciding which model-text refs become egress attachments) and in
  composition (deciding which paths are readable at all). Divergence would be
  a silently undelivered file or an extraction/confinement mismatch, so
  `ironclaw_attachments` owns it, composition imports it, and a test pins the
  prefix to the alias.
- `NoProjectFilesystem` was defined verbatim in three crates. One inert double
  now lives beside the trait it implements, under `test-support`.

Also corrects two comments that asserted guarantees the code no longer made:
the reader's "same 25 MiB limit" (the delivery instance is deliberately
tighter) and `ProjectFsEntryKind`'s "without depending on that crate" (the
dependency exists; it is a wire projection, which is the real reason).

Retains the cause of a provider parse failure server-side via `tracing::debug`
while keeping the user-facing reason a fixed literal, and constructs the
declared bot-token handle in one place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(product): move the scoped project-filesystem adapters out of composition

Continues the direction of #6615/#6616/#6619: composition is assembly, and
these files were not assembly. Backend selection already happened upstream —
composition hands them a `ScopedFilesystem`. What is left is contract policy
owned by the port they implement: alias confinement with the explicit
sibling-prefix guard, sensitive-filename omission from listings, the
TOCTOU-hardened two-stage size guard, extension→MIME derivation that must
match the download `Content-Type`, and the substrate→port error sanitization
table (including the deliberate MountNotFound→503 vs Contract→400 split).

`ProjectScopedFilesystemReader` and the attachment lander/reader therefore
move to `ironclaw_product::scoped_fs`, beside the `ProjectFilesystemReader` /
`InboundAttachmentLander` traits they implement. Every import they need was
already a production dependency of that crate, and the in-crate precedent is
`filesystem_ledger.rs`, which likewise hosts a generic `ScopedFilesystem`
implementation of a product-owned port.

`mount_filesystem_reader.rs` deliberately stays in composition: its
`alias_for(FsMount)` table is the "which mounts does this deployment serve"
decision, which is composition's job. It now consumes the shared scoped-path
helpers from the owner crate by name.

Composition src: 66,998 → 66,208 LOC (10.39% → 10.27% of production).

Two gates caught this change and were fixed rather than silenced: the struct
ratchet entry follows the file to its new path, and the extension-specificity
gate rejected a comment naming a concrete vendor in generic code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(channels): invert the pairing-outcome observer to a host-owned trait

The sink is generic channel machinery, but its pairing-outcome observer was
an enum naming a concrete composition type (`RunDeliveryPostAdmissionObserver`)
plus a `#[cfg(test)]` `Recording` variant compiled into the production type.
That is the coupling that keeps the generic ingress sink pinned to composition.

It is now a trait: the delivery observer implements it, and tests supply an
ordinary double instead of a variant. This is also the seam that has to invert
before the sink itself can move to `ironclaw_extension_host`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(extension-host): move the generic channel ingress sink out of composition

`ironclaw_extension_host` already owned the ingress *port* (`InboundSink`,
`InboundAdmission`, the router and verifier); composition owned the
*implementation*. The boundary test's own inventory shows the neighbourhood was
already evacuated — `reply_contexts`, `channel_delivery`, `channel_dm_targets`
and `channel_lifecycle` are all listed as externalized generic modules, and
`extension_ingress` appeared on neither that list nor the internal one. It was
the holdout.

Moved to `ironclaw_extension_host::ingress::sink`: the registration table
behind the router's ports, the trusted-evidence mint, the pairing
pre-admission gate, `GenericChannelInboundSink`, `StaticIngressSecrets`, and
the `build_extension_ingress` factory — module-owned initialization, as the
composition guide requires. The pairing outcome vocabulary moves with it to
`ingress::pairing`; the pairing *service* (CAS claim, identity bind,
completion fan-out) stays in composition and implements the host trait.

Composition keeps `mod serve_mount`: `ingress/mod.rs` states the crate is
deliberately transport-neutral, so the axum `PublicRouteMount` stays on the
composition side. Its public re-export is preserved, so downstream binaries
and tests are unaffected — only the source crate changed, which is what the
pub-use snapshot update records.

`host-auth-mint` is enabled on the host crate's `ironclaw_product` dependency
with the rationale the feature rule requires: it is a privilege boundary, and
this crate is one of the host runtimes entitled to mint verified evidence
after the router has executed the manifest's verification recipe.

Composition src: 66,205 → 65,560 LOC (10.27% → 10.17%). Across this branch:
66,998 → 65,560, with the file itself going 1,242 → 186 lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* fix(llm): point the fault-injection doc example at its own crate

The example imported `ironclaw::testing::fault_injection`, a path that died
with the v1 monolith, so the doc-test failed to compile. It only runs under
workspace-wide feature unification — the root dev-dependency enables
`ironclaw_llm/test-support`, which compiles the `testing` module — so
`cargo test -p ironclaw_llm --doc` alone reports zero tests and never
surfaced it. `cargo test --workspace` has been failing on it.

The doc-test is its own regression test: it now compiles, where before it
could not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* fix(cli): pin channel authority in native ingress test

* fix(egress): enforce body limits after secret injection

* fix(product): preserve delivery failure semantics

* fix(telegram): allow attachments without size hints

* docs: design generic cross-channel attachments

* docs: plan generic cross-channel attachments

* feat(filesystem): add atomic subtree creation

* fix(attachments): land inbound batches atomically

* test(attachments): isolate channel lander seam

* feat(outbound): persist reply attachment intents

* feat(attachments): complete cross-channel reply delivery

* feat(attachments): durably assemble provider batches

* fix(slack): verify batched attachment delivery

* docs: record cross-channel attachment verification

* fix(attachments): harden replay and provider boundaries

* fix(attachments): repair merge-blocking CI coverage

* fix(attachments): retain test reply intent store

* fix(attachments): reuse outbound test store seam

* fix(attachments): render durable file references cleanly

* docs(attachments): define structured multimodal replies

* feat(attachments): complete multimodal kind vocabulary

* feat(attachments): add opaque reply attachment handles

* feat(attachments): return opaque handles to the model

* refactor(attachments): make structured replies canonical

* fix(ci): close attachment coverage gates

* fix mixed attachment cleanup snapshots

* fix(attachments): harden cross-channel reply delivery

* fix(ci): qualify attachment integration type

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
BenKurrek added a commit that referenced this pull request Jul 31, 2026
…nst HEAD (#6944)

* docs(guidance): WS11.3 drift hotfixes — make agent guidance true against HEAD

Fix developer guidance that is wrong today against origin/main: references to
crates, symbols, paths, cargo features and tests that do not exist. Guidance
and docs only — no code, test, or manifest changes.

Classes fixed:
- Seven nonexistent crates presented as current architecture (ironclaw_engine,
  _tui, _gateway, _oauth, _skill_learning, _webui_v2, _product_context), plus
  the deleted root `src/` monolith and the phantom ironclaw_hooks_{postgres,
  libsql,parity} / ironclaw_product_adapters / _product_adapter_registry.
- `build_reborn_services` (crates/Architecture.md, tests/integration/CLAUDE.md)
  — no such function exists; the entry point is `build_reborn_runtime`.
- `NetworkPolicyDecider` and six other phantom trait names in root CLAUDE.md's
  "key traits for extensibility" line.
- The composition guide's phantom `src/webui/` and `src/projection.rs` paths
  (moved to ironclaw_webui / ironclaw_product by #6618 / #6615) and its
  `llm_admin::llm_catalog` ownership claim (that module is ironclaw_operator's).
- Stale feature-gating claims: ironclaw_product's `storage`/`libsql`/`postgres`,
  openai_compat's and event_store's feature gates (neither crate declares any
  features), webui's `openai-compat-beta`/`dev-in-memory-session`, and
  ironclaw_llm's `testing` feature (the real gate is `test-support`).
- Stale measured numbers in .claude/rules (fan-in figures, type/trait counts,
  duplicate-scan candidates, too_many_arguments allows) and two phantom
  cross-references to headings that do not exist.

Every edit was re-verified against origin/main @ cd5d3fe; per-item evidence
is in the PR body, including items recorded as already-resolved or
verified-correct with no change made.

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

* docs: record the WS11.3 drift-hotfix tranche in the checklist

Annotates the two WS11 guidance rows this PR partially discharges. Both boxes
stay unchecked: only the drift-hotfix half landed (claims wrong against HEAD
today); the rewrite half — thin family index, family model, family map, new
crate guides — waits on the restructure. Edit is confined to those two rows to
stay clear of the concurrent WS0 deletion PRs (#6942, #6943), which touch other
rows in this file.

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

* docs(guidance): track the hooks coverage gap and harden the dead-command banner

Closes the two gaps the WS11.3 review flagged, both surfaced by this PR's own
verification rather than by the checklist.

- `ironclaw_hooks/CLAUDE.md`: the cross-run hook-isolation semantic this PR
  found unpinned now carries its tracking pointer (#6945), plus two facts a
  reader needs to size the risk — production wires the isolating
  `with_hook_dispatcher_builder_factory`, so the property holds today, and the
  existing `poisoned_during_dispatch_skips_subsequent_invocations` covers
  poisoning within one dispatcher, not across host builds.

- `.claude/commands/add-sse-event.md`: the warning is now a banner that names
  every dead step (1-3 `src/channels/`, 4-5 `crates/ironclaw_gateway/static/`,
  6 `src/agent/`+`src/worker/`; only step 7 survives) and states plainly that
  the command needs rewriting onto the `ironclaw_webui` streaming path. It
  deliberately does not invent that procedure — replacing a scaffold is new
  guidance, not a drift fix. Every path and crate named in the banner was
  verified to exist.

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

* docs(guidance): address CodeRabbit review on the WS11.3 drift hotfixes

Eight of eleven comments were valid; three were residual drift I left behind in
the first pass, and two were self-inflicted by it.

Residual drift I missed:
- `ironclaw_hooks/CLAUDE.md` still named `BuiltinHookSink`/`TrustedHookSink`/
  `InstalledHookSink` at lines 33/36/39; I had only fixed the module bullet.
  All three are phantom types. Replaced with the real privileged/restricted
  families, with the tier mapping read out of the `install_*` signatures
  (`install_builtin_*`/`install_trusted_*` take `Privileged*Hook`;
  `install_installed_*` takes `Restricted*Hook`, whose gate sink has no
  `allow`). `SelfAuthoredHookSink` is real and was left alone.
- `reborn-extension-surfaces` still said `[channel.config]` twice; no manifest
  declares that section.
- `ironclaw_llm/CLAUDE.md` still claimed the binary supplies adapters in
  `src/llm_host.rs` and plugs impls into `SessionManager`.

Self-inflicted by the first pass:
- `triage-prs.md`: I put `crates/ironclaw_agent_loop/` in Agent Core while it
  was already in the Reborn-stack row.
- `composition/AGENTS.md`: appending the llm_catalog correction left the
  sentence contradicting itself mid-clause.

Also: repo-qualified the parity paths in the testing skill, merged the
duplicate `ironclaw_webui` row in `crates/README.md`, made the worked-example
re-verify command execute the equivalence proof instead of `ls`, and noted the
`ironclaw_silk_decoder` workspace exclusion in the de-slop loop.

Two claims I wrote in this round were wrong on first draft and corrected after
running them: the parity command needs `--features integration` (the targets do
not build without it), and `build_reborn_runtime` delegates to `build_runtime`,
not the reverse.

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
* feat(channels): add attachment transfer vocabulary to egress descriptors

Re-applied from codex/telegram-slack-attachments: ironclaw_attachments
materialized-file/budget/workspace-ref types + ChannelEgressDescriptor
paths/path_prefixes/body-limit bounds with fail-closed validation.

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

* feat(channels): transfer inbound/outbound channel attachments through restricted egress

Re-applied from codex/telegram-slack-attachments onto the restructured
tree: ChannelAdapter::fetch_attachment seam + AttachmentTransfer error in
ironclaw_host_api's product_adapter contract; envelope-transient
channel_attachment_refs; ChannelInboundProductSurface transfer door with
fail-closed default; post-policy fetch/validate/land orchestration in
ironclaw_product's inbound turn service; workspace-file materialization in
the delivery coordinator; Telegram getFile/download + sendDocument via the
manifest's path-constrained egress; Slack fails closed both directions;
composition wiring for the per-request policy-enforced channel egress.

InboundAttachment/MaterializedFile moved down into ironclaw_host_api (the
trait contract owner) because ironclaw_attachments depends on host_api.

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

* test(reborn): prove channel attachment journeys on the production mount

Relocates the composition-resident attachment journey coverage to the
extension-delivery integration lane per the tests/integration-first rule:
the telegram delivery scenario now drives a document update through the
production ingress mount — transient getFile failure releases the ledger
attempt (503), the vendor redelivery refetches through the manifest's
path-constrained egress with the token injected host-side, bytes land at
the canonical /workspace/attachments ref exactly once, duplicate replay
does no vendor I/O, and a follow-up conversation's final reply
referencing the landed file is materialized through the real
project-scoped reader and delivered natively via sendDocument. The
envelope's transient-refs serde(skip) contract is pinned beside the type
in ironclaw_host_api; sink-level door routing and inherited fail-closed
transfer stay as local contract tests beside the moved code.

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

* chore(reborn): quality-gate fixes for the attachment re-application

Bundle the delivery coordinator's materialization inputs (clippy arg
budget), reuse the VendorResponseRouter alias, drop a dead test accessor,
update the ingress contract fixture to the current manifest schema
(admin_configuration-declared verification handle), and annotate
large-file growth per the arch-sprawl gate.

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

* chore(telegram): move channel test modules under src/tests/

The no-panics production gate exempts src/**/tests/*.rs; the flat
channel_*_tests.rs siblings were test-only (cfg(test) #[path] mounts)
but not recognizable as such by the path heuristic.

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

* fix(channels): correct attachment transfer bugs and retire the large-file exempts

Audit follow-ups on the attachment transfer path.

Functional fixes, each with a regression test that fails without it:

- The fetched/declared MIME check normalized only the fetched side, so a
  descriptor carrying a parameter (`text/plain; charset=utf-8` — what
  Telegram clients routinely report for text documents) never matched and
  rejected the whole message, caption included. Compare canonical forms on
  both sides so the check catches a real provider mismatch instead.
- The descriptor filename overwrote the fetched one unconditionally,
  discarding the name the adapter recovers from the `getFile` path for
  payloads that carry none (photos, voice notes, stickers). Keep it when
  the descriptor has no filename.
- The multipart boundary was derived from `bytes.len()` and searched in a
  `0..=u32::MAX` loop with a full payload scan per iteration. The payload is
  attacker-authored (an inbound attachment can be landed and later referenced
  by a reply), so a sender could pad a file with collisions and force
  unbounded rescans. Use a v4 UUID and scan once.
- The manifest declared 5 MiB transfer caps while the code enforced the
  10 MiB host budget, so a file in between passed every code check and was
  then refused at egress — inbound reported as "denied" rather than "too
  large", outbound after the coordinator had committed to the send. Derive
  one bound (TELEGRAM_MAX_TRANSFER_BYTES), pre-flight the assembled
  multipart body against the declared request cap, map ResponseTooLarge to
  a size error, and pin constants to the manifest with a test.
- The Bot API target's 64 KiB response cap also covered sendMessage, whose
  response echoes `reply_to_message` now that replies are threaded; an
  oversize echo failed a send the user had already received. Keep the host
  default there and document why.
- The two fail-closed defaults for "this layer cannot transfer attachments"
  disagreed: the product surface said retryable, the inbound turn service
  said permanent. Retryable left the vendor redelivering forever while the
  user got nothing at all. Both are permanent now; a missing deployment
  egress transport stays retryable as an operator-fixable condition, and a
  permanent Invalid outcome is logged rather than settling silently.

Security and hygiene:

- `path_prefixes` matched by raw byte prefix, so a declared
  `/file/bot{token}` also authorized `/file/bot{token}Evil/…`. Require a
  trailing `/` at descriptor validation and match on a segment boundary.
- `InboundAttachment` (new in this PR) derived `Debug` over raw bytes while
  its sibling in the same file hand-writes a redacting one with a leak test.
  Both now redact, and `ProjectFsFile` — the wire type — does too.
- `MaterializedFile<P>` had exactly one instantiation; collapsed to
  `WorkspaceFile`.
- Removed 39 stale committed frontend build artifacts (3.5 MB) under
  `crates/ironclaw_webui_v2/`, a crate folded into `ironclaw_webui`; nothing
  reads the path. Closed the `.gitignore` gap that admitted them.
- Dropped all four `arch-exempt: large_file` annotations by shrinking the
  files instead: two were spurious (one on a 1,242-line file the 1,500-line
  gate never fires on, one licensing a semantically no-op edit), and the
  exempt grep scans the whole file body, so each would have disabled the
  gate for that file permanently. Test modules carved into their own files
  per the composition budget's own carve-out guidance.
- Deleted a sink test whose distinguishing assertion read the test double's
  own payload construction; the telegram journey covers door selection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(attachments): close the egress prefix bypass and collapse duplicated vocabulary

Security:

- `path_prefixes` matched by raw byte prefix, so a declared
  `/file/bot{token}` also authorized `/file/bot{token}Evil/…` — a sibling
  path on the same pinned host and credential that the manifest author never
  allowed. Descriptor validation now requires the prefix to end on a segment
  boundary, and the matcher enforces the boundary independently so a prefix
  reaching policy by another route still cannot authorize a sibling.

Duplicated vocabulary, per .claude/rules/type-placement.md:

- `mime_hint` was written in two adapters as a verbatim copy of
  `descriptor.mime_type` and read in zero production locations. Deleted with
  its writers; the one new production consumer already reads the descriptor.
- `AttachmentRef` named two different concepts — the durable byte-free
  transcript reference and this transient vendor fetch reference — which
  forced an `as ChannelAttachmentRef` import alias where both appeared. The
  channel one is now `ChannelAttachmentRef` at its definition and the alias
  is gone.
- `ProductAttachmentCapabilities` re-declared `AttachmentBudgets`' three
  fields and hand-copied each. Embedded with `#[serde(flatten)]`; the JSON
  shape is unchanged (the wire assertions still read the same keys) and a new
  budget field now reaches the browser with no intermediate edit.
- The `/workspace` prefix was defined independently in `ironclaw_attachments`
  (deciding which model-text refs become egress attachments) and in
  composition (deciding which paths are readable at all). Divergence would be
  a silently undelivered file or an extraction/confinement mismatch, so
  `ironclaw_attachments` owns it, composition imports it, and a test pins the
  prefix to the alias.
- `NoProjectFilesystem` was defined verbatim in three crates. One inert double
  now lives beside the trait it implements, under `test-support`.

Also corrects two comments that asserted guarantees the code no longer made:
the reader's "same 25 MiB limit" (the delivery instance is deliberately
tighter) and `ProjectFsEntryKind`'s "without depending on that crate" (the
dependency exists; it is a wire projection, which is the real reason).

Retains the cause of a provider parse failure server-side via `tracing::debug`
while keeping the user-facing reason a fixed literal, and constructs the
declared bot-token handle in one place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(product): move the scoped project-filesystem adapters out of composition

Continues the direction of nearai#6615/nearai#6616/nearai#6619: composition is assembly, and
these files were not assembly. Backend selection already happened upstream —
composition hands them a `ScopedFilesystem`. What is left is contract policy
owned by the port they implement: alias confinement with the explicit
sibling-prefix guard, sensitive-filename omission from listings, the
TOCTOU-hardened two-stage size guard, extension→MIME derivation that must
match the download `Content-Type`, and the substrate→port error sanitization
table (including the deliberate MountNotFound→503 vs Contract→400 split).

`ProjectScopedFilesystemReader` and the attachment lander/reader therefore
move to `ironclaw_product::scoped_fs`, beside the `ProjectFilesystemReader` /
`InboundAttachmentLander` traits they implement. Every import they need was
already a production dependency of that crate, and the in-crate precedent is
`filesystem_ledger.rs`, which likewise hosts a generic `ScopedFilesystem`
implementation of a product-owned port.

`mount_filesystem_reader.rs` deliberately stays in composition: its
`alias_for(FsMount)` table is the "which mounts does this deployment serve"
decision, which is composition's job. It now consumes the shared scoped-path
helpers from the owner crate by name.

Composition src: 66,998 → 66,208 LOC (10.39% → 10.27% of production).

Two gates caught this change and were fixed rather than silenced: the struct
ratchet entry follows the file to its new path, and the extension-specificity
gate rejected a comment naming a concrete vendor in generic code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(channels): invert the pairing-outcome observer to a host-owned trait

The sink is generic channel machinery, but its pairing-outcome observer was
an enum naming a concrete composition type (`RunDeliveryPostAdmissionObserver`)
plus a `#[cfg(test)]` `Recording` variant compiled into the production type.
That is the coupling that keeps the generic ingress sink pinned to composition.

It is now a trait: the delivery observer implements it, and tests supply an
ordinary double instead of a variant. This is also the seam that has to invert
before the sink itself can move to `ironclaw_extension_host`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* refactor(extension-host): move the generic channel ingress sink out of composition

`ironclaw_extension_host` already owned the ingress *port* (`InboundSink`,
`InboundAdmission`, the router and verifier); composition owned the
*implementation*. The boundary test's own inventory shows the neighbourhood was
already evacuated — `reply_contexts`, `channel_delivery`, `channel_dm_targets`
and `channel_lifecycle` are all listed as externalized generic modules, and
`extension_ingress` appeared on neither that list nor the internal one. It was
the holdout.

Moved to `ironclaw_extension_host::ingress::sink`: the registration table
behind the router's ports, the trusted-evidence mint, the pairing
pre-admission gate, `GenericChannelInboundSink`, `StaticIngressSecrets`, and
the `build_extension_ingress` factory — module-owned initialization, as the
composition guide requires. The pairing outcome vocabulary moves with it to
`ingress::pairing`; the pairing *service* (CAS claim, identity bind,
completion fan-out) stays in composition and implements the host trait.

Composition keeps `mod serve_mount`: `ingress/mod.rs` states the crate is
deliberately transport-neutral, so the axum `PublicRouteMount` stays on the
composition side. Its public re-export is preserved, so downstream binaries
and tests are unaffected — only the source crate changed, which is what the
pub-use snapshot update records.

`host-auth-mint` is enabled on the host crate's `ironclaw_product` dependency
with the rationale the feature rule requires: it is a privilege boundary, and
this crate is one of the host runtimes entitled to mint verified evidence
after the router has executed the manifest's verification recipe.

Composition src: 66,205 → 65,560 LOC (10.27% → 10.17%). Across this branch:
66,998 → 65,560, with the file itself going 1,242 → 186 lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* fix(llm): point the fault-injection doc example at its own crate

The example imported `ironclaw::testing::fault_injection`, a path that died
with the v1 monolith, so the doc-test failed to compile. It only runs under
workspace-wide feature unification — the root dev-dependency enables
`ironclaw_llm/test-support`, which compiles the `testing` module — so
`cargo test -p ironclaw_llm --doc` alone reports zero tests and never
surfaced it. `cargo test --workspace` has been failing on it.

The doc-test is its own regression test: it now compiles, where before it
could not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5yb6tF3rwMq8KSvuMDhyK

* fix(cli): pin channel authority in native ingress test

* fix(egress): enforce body limits after secret injection

* fix(product): preserve delivery failure semantics

* fix(telegram): allow attachments without size hints

* docs: design generic cross-channel attachments

* docs: plan generic cross-channel attachments

* feat(filesystem): add atomic subtree creation

* fix(attachments): land inbound batches atomically

* test(attachments): isolate channel lander seam

* feat(outbound): persist reply attachment intents

* feat(attachments): complete cross-channel reply delivery

* feat(attachments): durably assemble provider batches

* fix(slack): verify batched attachment delivery

* docs: record cross-channel attachment verification

* fix(attachments): harden replay and provider boundaries

* fix(attachments): repair merge-blocking CI coverage

* fix(attachments): retain test reply intent store

* fix(attachments): reuse outbound test store seam

* fix(attachments): render durable file references cleanly

* docs(attachments): define structured multimodal replies

* feat(attachments): complete multimodal kind vocabulary

* feat(attachments): add opaque reply attachment handles

* feat(attachments): return opaque handles to the model

* refactor(attachments): make structured replies canonical

* fix(ci): close attachment coverage gates

* fix mixed attachment cleanup snapshots

* fix(attachments): harden cross-channel reply delivery

* fix(ci): qualify attachment integration type

---------

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
…nst HEAD (nearai#6944)

* docs(guidance): WS11.3 drift hotfixes — make agent guidance true against HEAD

Fix developer guidance that is wrong today against origin/main: references to
crates, symbols, paths, cargo features and tests that do not exist. Guidance
and docs only — no code, test, or manifest changes.

Classes fixed:
- Seven nonexistent crates presented as current architecture (ironclaw_engine,
  _tui, _gateway, _oauth, _skill_learning, _webui_v2, _product_context), plus
  the deleted root `src/` monolith and the phantom ironclaw_hooks_{postgres,
  libsql,parity} / ironclaw_product_adapters / _product_adapter_registry.
- `build_reborn_services` (crates/Architecture.md, tests/integration/CLAUDE.md)
  — no such function exists; the entry point is `build_reborn_runtime`.
- `NetworkPolicyDecider` and six other phantom trait names in root CLAUDE.md's
  "key traits for extensibility" line.
- The composition guide's phantom `src/webui/` and `src/projection.rs` paths
  (moved to ironclaw_webui / ironclaw_product by nearai#6618 / nearai#6615) and its
  `llm_admin::llm_catalog` ownership claim (that module is ironclaw_operator's).
- Stale feature-gating claims: ironclaw_product's `storage`/`libsql`/`postgres`,
  openai_compat's and event_store's feature gates (neither crate declares any
  features), webui's `openai-compat-beta`/`dev-in-memory-session`, and
  ironclaw_llm's `testing` feature (the real gate is `test-support`).
- Stale measured numbers in .claude/rules (fan-in figures, type/trait counts,
  duplicate-scan candidates, too_many_arguments allows) and two phantom
  cross-references to headings that do not exist.

Every edit was re-verified against origin/main @ cd5d3fe; per-item evidence
is in the PR body, including items recorded as already-resolved or
verified-correct with no change made.

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

* docs: record the WS11.3 drift-hotfix tranche in the checklist

Annotates the two WS11 guidance rows this PR partially discharges. Both boxes
stay unchecked: only the drift-hotfix half landed (claims wrong against HEAD
today); the rewrite half — thin family index, family model, family map, new
crate guides — waits on the restructure. Edit is confined to those two rows to
stay clear of the concurrent WS0 deletion PRs (nearai#6942, nearai#6943), which touch other
rows in this file.

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

* docs(guidance): track the hooks coverage gap and harden the dead-command banner

Closes the two gaps the WS11.3 review flagged, both surfaced by this PR's own
verification rather than by the checklist.

- `ironclaw_hooks/CLAUDE.md`: the cross-run hook-isolation semantic this PR
  found unpinned now carries its tracking pointer (nearai#6945), plus two facts a
  reader needs to size the risk — production wires the isolating
  `with_hook_dispatcher_builder_factory`, so the property holds today, and the
  existing `poisoned_during_dispatch_skips_subsequent_invocations` covers
  poisoning within one dispatcher, not across host builds.

- `.claude/commands/add-sse-event.md`: the warning is now a banner that names
  every dead step (1-3 `src/channels/`, 4-5 `crates/ironclaw_gateway/static/`,
  6 `src/agent/`+`src/worker/`; only step 7 survives) and states plainly that
  the command needs rewriting onto the `ironclaw_webui` streaming path. It
  deliberately does not invent that procedure — replacing a scaffold is new
  guidance, not a drift fix. Every path and crate named in the banner was
  verified to exist.

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

* docs(guidance): address CodeRabbit review on the WS11.3 drift hotfixes

Eight of eleven comments were valid; three were residual drift I left behind in
the first pass, and two were self-inflicted by it.

Residual drift I missed:
- `ironclaw_hooks/CLAUDE.md` still named `BuiltinHookSink`/`TrustedHookSink`/
  `InstalledHookSink` at lines 33/36/39; I had only fixed the module bullet.
  All three are phantom types. Replaced with the real privileged/restricted
  families, with the tier mapping read out of the `install_*` signatures
  (`install_builtin_*`/`install_trusted_*` take `Privileged*Hook`;
  `install_installed_*` takes `Restricted*Hook`, whose gate sink has no
  `allow`). `SelfAuthoredHookSink` is real and was left alone.
- `reborn-extension-surfaces` still said `[channel.config]` twice; no manifest
  declares that section.
- `ironclaw_llm/CLAUDE.md` still claimed the binary supplies adapters in
  `src/llm_host.rs` and plugs impls into `SessionManager`.

Self-inflicted by the first pass:
- `triage-prs.md`: I put `crates/ironclaw_agent_loop/` in Agent Core while it
  was already in the Reborn-stack row.
- `composition/AGENTS.md`: appending the llm_catalog correction left the
  sentence contradicting itself mid-clause.

Also: repo-qualified the parity paths in the testing skill, merged the
duplicate `ironclaw_webui` row in `crates/README.md`, made the worked-example
re-verify command execute the equivalence proof instead of `ls`, and noted the
`ironclaw_silk_decoder` workspace exclusion in the de-slop loop.

Two claims I wrote in this round were wrong on first draft and corrected after
running them: the parity command needs `--features integration` (the targets do
not build without it), and `build_reborn_runtime` delegates to `build_runtime`,
not the reverse.

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-6615 — 0abefc4b Deployed Jul 24, 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: dependencies Dependency updates scope: docs Documentation size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant