test(reborn): wave-3 integration coverage — journeys, multi-user isolation, triggered/outbound, budget/comm-context/hooks seams, golden payloads, denied-edge contracts - #5584
Conversation
Add the discriminating contrast to the E-TRIGGERED-SUBMIT origin test: a normal interactive submit_turn records TurnOriginKind::Inbound, read through the same coordinator.get_run_state(...).product_context.origin boundary the trigger test uses. Without a contrasting turn on the same wire, the existing ScheduledTrigger assertion could pass on a hardcoded origin; the pair proves the origin is genuinely propagated from the submission path. Mutation-verified: breaking the production TrustedTrigger->ScheduledTrigger mapping turns the trigger test RED while the interactive contrast stays GREEN. Also refresh the reborn_group_triggers TODO to record the current disposition: origin coverage DONE (flat test, not this multi-thread group), mid-fire approval gate STILL OUT (now authorable), and outbound-delivery (C-TRIGGERED-DELIVERY) BLOCKED at int tier (deliver_triggered_run is a private fn reachable only via a detached tokio::spawn, unwired in any harness turn lifecycle; branch logic already densely pinned by slack_delivery.rs's #[cfg(test)] module + product_workflow outbound_delivery_contract.rs). Tests-only; no production behavior change. E-OUTBOUND/C-OUTBOUND/ C-TRIGGERED-DELIVERY deferred: the outbound delivery sink is not wired into any harness-reachable production composition path, so per the no-wire-what-production-doesnt-wire rule it is reported, not wired. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cipline doc (Wave-3 infra)
Adds the harness-infra baseline items from the reborn coverage roadmap:
- assertions.rs (additive impl block): history_len() + fail-loud
history_slice, assert_tool_error_since / assert_no_tool_error_since /
assert_tool_error_summary_contains_since (baseline-sliced multi-turn
variants of the documented single-turn-only tool-error asserts), and
assert_conversation_history_contains{,_since} +
assert_conversation_history_role_contains (general persisted-transcript
containment, role-filterable).
- reborn_integration_http_matcher.rs: multi_turn_baseline_sliced_history_assertions
demo/regression — two error-raising turns on one thread; positive,
slice-exclusion, role-discrimination, and out-of-range-baseline
fail-checks.
- tests/support/reborn/CLAUDE.md: documents the new asserts and the
script-discipline rule that a gated tool-call turn consumes exactly
2 script entries (tool call + one post-resume model call) whether the
gate is approved or denied.
Existing asserts untouched (strictly additive for parallel-lane
union-merge). No new harness wiring; helpers read already-persisted
thread history through the existing accessor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extends tests/reborn_group_multiuser with the C-MULTIUSER matrix: proof that two distinct actors sharing the group's ONE capability backend isolate their capability-side state per owner, the way production does. E-MULTIUSER (#5526, `with_actor_id`) isolates only thread HISTORY (turn store subtrees). Capability-side state — memory, auto-approve, approval settings — was NOT isolated in the harness: the capability port factory hardcoded one fixed execution user for every actor. Production instead derives the capability `(tenant, user)` from the run owner/actor (`local_dev_visible_capability_request`, wired via `LocalDevLoopCapabilityPortFactory` at runtime.rs → capability_wiring), falling back to a fixed id only when no owner is present. Seam (tests/support/reborn, additive, default-off so every existing test is byte-identical): `HostRuntimeCapabilityHarness` gains an opt-in `scope_capability_by_run_owner` flag + `dispatch_user_for_run` that mirrors production's owner→actor→fallback resolution; the harness capability port factory now uses it. Two self-contained group constructors (`multiuser_memory_tools`, `multiuser_approvals`) enable it, plus per-owner auto-approve `enable/disable_auto_approve_for_owner` helpers (same `AutoApproveSettingStore` + `(tenant,user)` key production uses). Scenarios: - memory_isolation_across_actors: A writes memory, distinct actor B cannot search it, A still can (pins the tool-path isolation #5460's reporter confirmed; #5460 itself is the WebUI read surface). - auto_approve_isolation_across_actors: A's always-allow grant lets A's write_file skip the gate while B (own always-allow OFF) still raises a real BlockedApproval on the identical call. Mutation-verified: forcing the fixed user (ignoring the flag) turns BOTH isolation scenarios RED for the right reasons (B reads A's memory; B no longer gates), while two_actors_own_threads stays green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
skill_activate ContextBudgetExceeded is the one synthetic-capability failure route coverable with ZERO new harness wiring: an oversized system skill seeded through the existing seed_system_skill_for_test seam drives the real selection path (reserve_skill_budget → ContextBudgetExceeded → CapabilityOutcome::Failed) and surfaces as a model-visible recoverable Failed tool error; the run completes and the failed skill's instructions are proven NOT injected. Remaining C-SYNTH routes (outbound_delivery_* — unwired, needs facade double + constructor enabler; project_create Conflict/Unavailable — needs whitebox service-error injection; skill AmbiguousSkill + source B-routes) are deferred per the spike inventory; project_create Denied/NotFound arms are unreachable from the real create_project path (no require_role call) and stay unit-tier only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ET/C-COMMCTX/E-HOOK-INFRA/C-HOOKS) Wave-3 rev-3 + hooks int-tier coverage. All changes are tests-only; the three DefaultPlannedRuntimeParts seams wired here are production-active (verified against build_reborn_runtime: model_budget_accountant, communication_context_provider, hook_dispatcher_builder_factory are all Some in production), so wiring them in the harness closes genuine drift (A1 audit), not new behavior. - C-BUDGET: RebornIntegrationGroupBuilder::budget_accounting() + with_budget_accounting() wire the production build_default_budget_accountant (in-memory governor/gate-store/zero-cost-table + compiled-default seeding) into the group's one planned runtime; assert_budget_user_cap_seeded reads back the seeded daily cap. Wiring-liveness only — semantics stay at crate tier (budget_e2e.rs). tests/reborn_integration_budget.rs (+ negative guard). - C-COMMCTX: RecordingCommunicationContextProvider double + communication_context_provider()/with_communication_context_provider() seams; assert the delivery-target + connected-channel slice renders into the model request. tests/reborn_integration_comm_context.rs (+ negative guard). The provider's facade->context mapping stays crate-tier covered. - E-HOOK-INFRA: recording hook doubles (RecordingHookLog + observer/before-cap hooks) + recording_hook_factory/denying_hook_factory builders, wired via hook_dispatcher_builder_factory()/with_hook_factory(). - C-HOOKS: two scenarios are #[ignore]d RED regressions pinning a genuine cross-crate production bug discovered here: HookedLoopCheckpointPort (crates/ironclaw_hooks/src/middleware/checkpoint_port.rs) overrides only checkpoint() and does NOT forward stage_checkpoint_payload/load_checkpoint_payload to its inner port, so those fall through to the LoopCheckpointPort trait's fail-closed defaults. Any hook-dispatcher-active coordinator turn dies driver_unavailable at checkpoint_before_model. Hooks are off by default in production so this latent wrapper bug was never exercised — this is the first full-turn-with-hooks path. TODO(reborn-hooks-checkpoint-forward). Fix is cross-crate, out of scope for this tests-only lane; un-ignore once forwarded. Shared support-file edits (group.rs/builder.rs/assertions.rs) are strictly additive (new fns/fields appended) for clean union-merge with sibling lanes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…int tier
Extend the E-TRIGGERED-SUBMIT seam with submit_triggered_turn_scripted:
mirrors the production trigger poller's materializer
(ConversationContentRefMaterializer::materialize_prompt — pub(crate),
hence mirrored) instead of the for_fire shortcut: resolve the fire's
conversation binding (mints the scope pre-submit), record the prompt as
a real inbound thread message (without this the run dies
driver_unavailable on "unknown thread" — found empirically), build the
real thread-message:<id> content ref, register a scripted gateway for
the EXACT resolved scope (no fallback mechanism, no race), then submit
through the production TrustedTriggerFireSubmitter. Adds scope-aware
wait_for_status_in_scope / approve_gate_in_scope / deny_gate_in_scope /
resume_run_in_scope twins (builder.rs twins poll self.turn_scope;
additive-only constraint on shared support files defers consolidation).
New coverage:
- triggered_run_completes_and_persists_reply_in_trigger_thread
(reborn_integration_triggered_submit): a triggered run completes and
its final reply persists in the trigger's OWN thread — the
int-tier-observable half of triggered delivery (the state production's
deliver_triggered_run reads before pushing). Probe finding pinned in
the test doc: no harness composition routes a completed run through
render_outbound/OutboundDeliverySink; the push leg stays with the
services-shell spike.
- triggered_gate_group (reborn_group_triggers,
scenario_triggered_gate::{run_approve,run_deny}): a triggered fire
raises a REAL BlockedApproval gate mid-fire; approve re-runs the gated
write (file persisted), deny surfaces a non-retryable authorization
failure (file absent). Finding: nothing in the scheduled_trigger
surface/deny-map (#5505) suppresses approval gates — trigger-origin
runs gate exactly like interactive runs.
Once{at} post-fire completion derivation deliberately NOT re-authored:
already pinned at crate tier (repository_contract.rs clear_active_fire →
Completed; trigger_poller_e2e.rs settle).
Mutation-verified: (1) skipping the scope-gateway registration turns the
completion test RED (model_error via the scope-miss sentinel);
(2) resuming in the harness thread scope instead of the fire's scope
turns both gate arms RED ("turn run not found"). Both reverted.
Tests-only; no production behavior change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… slackloop) Add a deterministic crate-tier twin of the live Slack canary flow to slack_serve/e2e_tests.rs: a triggered run (personal, foreign thread scope) blocks on approval; the production TriggeredRunDeliveryDriver posts the approval prompt to the creator's Slack DM through a fake protocol egress and auto-records a delivered gate route; an inbound bare `approve` in that DM then resolves the gate on the run's foreign scope via the DRIVER-recorded route (not a hand-seeded one). This welds two production assemblies that were previously pinned only in isolation: the triggered-delivery route recording (slack_delivery cfg(test)) and the inbound delivered-route resolution (bare_approve_in_dm_resolves_gate_recorded_by_observer). Both the driver and the workflow share one DeliveredGateRouteStore; test doubles substitute only at seams the production triggered factory (build_triggered_run_delivery_hook_from_parts) fills (egress real, binding_service Noop). The final-reply tail after approve is pinned separately by slack_approval_reply_resumes_and_delivers_final_reply; stitching it here would race the triggered driver's own delivery loop against the live observer's (two independent active_delivery_run_ids sets), a cross-assembly dedup question outside this test's scope. Test-only; zero production changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wire the two production local-dev synthetic capabilities builtin.outbound_delivery_targets_list / builtin.outbound_delivery_target_set into the Reborn integration harness at the production-wired OutboundPreferencesProductFacade trait seam: - test_support wrap accessor reusing the REAL outbound_delivery_capabilities + wrap_local_dev_synthetic_capabilities + StoreApprovalSettingsProvider (mirrors the project_create / skill_activate seam precedent; all additions #[cfg(feature = "test-support")]-gated, zero production-behavior change) - FakeOutboundPreferencesFacade double (in-memory targets; unknown id -> NotFound) - outbound_target_tools() harness preset + group constructor; disable_auto_approve() harness method (gate arm); RecordingApprovalRequestStore so port-level synthetic gates record their approval scope for approve/deny_local_dev_gate - New test bin tests/reborn_integration_outbound_target.rs covering: targets_list happy path; target_set happy path with facade read-back; settings-Deny -> Failed/policy_denied; facade NotFound -> Failed/invalid_input; approval gate approve -> resume applies; deny -> facade never reached Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add exact-match "golden inference payload" assertions to the Reborn integration tier. Where assert_system_prompt_contains proves a substring reached the model, this pins end-to-end prompt construction: the FULL model-visible payload per inference iteration (system prompt, all turns, and tool-call/tool-result messages) plus a compact ordered tool surface, snapshotted with insta (the repo's established snapshot tool), and the exact final user-visible reply. Canonicalization renders the captured request to sorted-key JSON and normalizes exactly one genuinely nondeterministic value — the runtime context's model-visible wall clock — to <TIMESTAMP>. Everything else is byte-exact: tool-call ids (deterministic call-N) and the surface sha256 content hash stay verbatim so real drift is caught. Three representative scenarios: single-turn greeting (base prompt), a tool-call turn (both iterations — pins tool-result feed-back with matching tool_call_id), and a two-user-turn thread (pins history accumulation). Tool JSON schemas are excluded from the golden deliberately (they are pinned by the surface sha256 in the exact-matched system prompt and by each tool's own tests) to avoid coupling these goldens to unrelated builtin-tool edits. Regenerate drift with `cargo insta review`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tive-origin contrast # Conflicts: # tests/reborn_group_triggers/main.rs
# Conflicts: # crates/ironclaw_reborn_composition/src/factory.rs # tests/support/reborn/harness.rs
…ture
Enablers for multi-turn journey coverage (auth gate -> resolve -> approval
gate -> approve -> follow-up on ONE conversation):
- HostRuntimeCapabilityHarness::file_and_github_auth_tools(): ONE
build_reborn_services local-dev runtime surfacing BOTH file tools at
PermissionMode::Ask (BlockedApproval) and an unseeded github.get_repo
(BlockedAuth via the REAL ProductAuthRuntimeCredentialResolver). Making
github.* genuinely dispatchable needed two test-support-gated composition
accessors (publish into the active-extension registry + real asset-dir
copy into the harness mount) — no production wiring changes.
- RebornIntegrationHarness::resolve_auth_gate(): the 'user submitted
credentials' happy arm — seeds a real GitHub credential account WITH
secret material through the production manual-token flow, then resumes
with BlockedAuthGate precondition so the parked capability re-dispatches.
- RebornIntegrationGroup::live_auth_and_approval() preset
(group_constructors.rs), subject-user-aligned like live_approvals.
- HARNESS BUG FIX: RecordingHostRuntime never forwarded the defaulted
auth_resume_capability, so every auth resume died on the trait's
fail-loud default ('auth-resume is unsupported by this host runtime') —
latent until the first happy-path auth resume exercised it.
- HARNESS BUG FIX: resume_run idempotency key was run_id-only, so a run
resuming through TWO gates (approval then auth) replayed the first
resume's cached response and wedged at BlockedAuth; key is now
(run_id, gate_ref)-scoped.
- assert_model_request_contains_all(): multi-turn context-carryover
assertion (all needles in ONE captured model request).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…urneys) Deterministic twins of the live canary multi-turn use cases, chaining gate -> resume -> next turn on ONE conversation over the group's one shared runtime: - interactive_approval_journey: approve turn -> deny turn -> follow-up; asserts per-turn side effects (approved write persists, denied write absent, no cross-turn bleed) and context carryover (turn 3's ONE model request carries turn 1's and turn 3's user text). - auth_then_approval_journey: turn 1 github.get_repo chains BlockedApproval -> approve -> BlockedAuth -> resolve (real manual-token flow) -> the SAME parked capability re-dispatches and its result carries the scripted network fixture body; turn 2 approval gate; turn 3 follow-up sees history across both gate classes. - auth_deny_then_retry_journey (mixed arm): turn 1 auth gate DENIED (run continues, #4944 semantics); turn 2 raises fresh approval+auth gates, resolve succeeds — a denied auth gate does not poison a later resolve. - multi_actor_gate_isolation: RED #[ignore]d — blocked on the unmerged C-MULTIUSER scope_capability_by_run_owner harness seam (distinct actor's gated dispatch is scoped to the canonical user on this base); TODO(reborn-multiuser-gate) pins the coverage for un-ignore. Mutation-verified: seeding disabled -> journeys wedge at BlockedAuth (RED); bogus needle -> context-carryover assert RED. Single-gate mechanics stay pinned by reborn_group_approvals / reborn_integration_auth_gate (docs cross-referenced); journey value is the CHAINING. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # tests/support/reborn/group.rs # tests/support/reborn/harness.rs
…vate over-budget routing (infra) # Conflicts: # tests/reborn_integration_skill_activate.rs # tests/support/reborn/assertions.rs
…ET/C-COMMCTX/E-HOOK-INFRA/C-HOOKS) # Conflicts: # tests/support/reborn/builder.rs # tests/support/reborn/group.rs
…sers (C-WIREFMT) Pins every legal wire framing at first-party external-response parse sites, per the rule: if a request's Accept header names N content types, cover >= N response-format fixtures. - ironclaw_mcp: crate-tier matrix through the unified parse_mcp_response dispatch (plain JSON, SSE single-event, SSE multi-event w/ keepalive, error-object in both framings, empty-body rejection in both framings). main already parses both framings via one shared entry — pinned so a future JSON-only sibling parse site can't ship untested. - mock_mcp_server + reborn_integration_mcp: additive enable_sse_framing() toggle (default off; existing tests unaffected) and an int-tier case driving the real MCP client over an SSE-framed initialize/tools/list/ tools/call handshake on loopback HTTP. - gsuite_core: empty-body 204 framing through the real google-calendar.delete_event handler (response_body_json Null branch). - oauth_provider_client: malformed 200 token-exchange body surfaces TokenExchangeFailed instead of panicking/succeeding. Fixtures are hand-authored (no live-captured MCP/Google bodies exist under tests/fixtures/); SSE shapes mirror the production unit fixtures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…EFMT) Int-tier twins of #5573's crate-tier SSE-framing coverage: SSE-framed MCP initialize through the real Reborn web-access handler + a both-legs-SSE sibling-parity case. #5573 (already on base) shipped only the crate-tier matrix; these exercise the full dispatch pipeline. Tests-only; the fix itself rides in via #5573. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…olation (journey) # Conflicts: # crates/ironclaw_reborn_composition/src/factory.rs # tests/support/reborn/CLAUDE.md # tests/support/reborn/harness.rs
The C-MULTIUSER scope_capability_by_run_owner harness seam merged in this fold, so the RED #[ignore]d journey now runs on multiuser_approvals() (per- actor capability dispatch). The scenario disables auto-approve per owner so both actors raise a real BlockedApproval, pinning that gate resolution + resume state stay bound to the raising actor. Green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pted seam Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lowError Journey's publish_bundled_extension_for_test is the sole user of ProductWorkflowError in factory.rs and is test-support-gated, so importing it unconditionally warned (unused) under default features. Fully-qualify at the use site to keep the default-features build warning-clean and the addition fully behavior-neutral. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 9 HIGH rows plus the mechanical MED/LOW rows from the wave-3 assertion audit: exact-token pins for gate-declined and hook-deny summaries, payload-shape asserts on outbound target set/list, bounded shape-filtered polling for the slack approval prompt (deterministic under the ScriptedTriggerCoordinator auto-advance race), deny-arm reply/egress pins in the auth journey, load-bearing auto-approve grant proof, sliced-history fail-checks, SSE handshake method-order and egress-count pins. Makes FakeOutboundPreferencesFacade stateful and consolidates the duplicated trigger-fire/actor-pairing setup in triggered_submit.rs now that the sibling lanes have landed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prose-only pass over wave-3-added doc-comments and test-body comments: drops lane-history narration, play-by-play that restates adjacent asserts, and cross-file duplicate explanations (one canonical copy + cross-reference kept). Compresses the CLAUDE.md script-discipline and sliced-history sections, corrects the now-stale "must add baseline scoping first" caveats to point at the landed *_since variants, fixes the stale ignored-test module doc on multi_actor_gate_isolation, and scopes the mock MCP server's enable_sse_framing doc to what callers actually exercise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ns and support Prose-only pass over the whole Reborn suite (integration bins, group scenarios, qa, parity, test-support helpers): deletes play-by-play narration, lane/wave/PR-process history, comments restating adjacent asserts, and cross-file duplicate mechanism explanations (one canonical copy kept with cross-references). Why-pins, seam contracts, mock-fidelity caveats, and C-XXX tags retained. Net ~-200 comment lines; mechanically verified comment/blank-only via -U0 diffs; zero code changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two contracts for the global DISABLED_CAPABILITY_IDS deny (CapabilitySurfaceDenyFilter, outermost): the id is stripped from the model-facing tool surface (non-vacuous — the manifest stub and spawn decorator would otherwise surface it; builtin__http as presence control), and a hallucinated call to it anyway is rejected at the model gateway before registration, failing the run terminally with failure category model_error and zero capability dispatch. General-surface sibling of the scheduled-trigger deny coverage (#5515). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds golden_context_surfacing to the dedicated full-payload monitoring suite: a wired communication-context provider plus the real builtin capability surface on one turn, snapshotting byte-for-byte how the communication section and capability surface render together in the system prompt. Module doc now states the suite's purpose (watching prompt-construction drift on a deliberately small scenario set). Compaction golden is blocked and documented in-file: the byte-cap overflow force-compact flag (CapabilityResultOverflow) is a dead letter under ActiveTaskPreservingCompactionStrategy, which never reads force_compact_on_next_iteration — needs a production decision first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The panic-lint scans files standalone and does not follow the parent's #[cfg(test)] mod attribute, so the three test-only .expect() calls in wait_for_gate_route / wait_for_approval_prompt_messages need explicit // safety: suppression comments, matching sibling handler_tests.rs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
rustfmt moves trailing comments off an if-let scrutinee, which breaks the lint's same-line suppression. Bind the loaded route first so the // safety: comment sits on a plain statement rustfmt leaves alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/support/reborn/assertions.rs (1)
270-303: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winDuplicate collector logic — unify on
[baseline..].
persisted_tool_error_summariesandpersisted_tool_error_summaries_sinceare identical except for the slice (full history vs.[baseline..]). The full-history variant can just delegate to the baseline-scoped one withbaseline = 0, removing ~30 duplicated lines of decode/truncate logic that would otherwise need synchronized edits.♻️ Proposed dedup
async fn persisted_tool_error_summaries(&self) -> HarnessResult<Vec<String>> { - let history = self - .thread_harness - .history(self.binding.thread_id.clone()) - .await?; - history - .iter() - .filter(|message| message.kind == ironclaw_threads::MessageKind::ToolResultReference) - .map(|message| { - let Some(content) = message.content.as_deref() else { - return Err("ToolResultReference message missing content".into()); - }; - serde_json::from_str::<ironclaw_threads::ToolResultReferenceEnvelope>(content) - .map(|envelope| envelope.safe_summary.as_str().to_string()) - .map_err(|err| { - let truncated = match content.char_indices().nth(200) { - Some((cutoff, _)) => format!("{}...[truncated]", &content[..cutoff]), - None => content.to_string(), - }; - format!( - "failed to decode ToolResultReferenceEnvelope: {err}; raw: {truncated}" - ) - .into() - }) - }) - .collect() + self.persisted_tool_error_summaries_since(0).await }Also applies to: 693-722
🤖 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 `@tests/support/reborn/assertions.rs` around lines 270 - 303, The two collectors duplicate the same history filtering and ToolResultReferenceEnvelope decode/truncation logic; make `persisted_tool_error_summaries` delegate to `persisted_tool_error_summaries_since` with a baseline of 0. Keep the shared implementation in `persisted_tool_error_summaries_since` and have both methods use the same `thread_harness.history` handling, `MessageKind::ToolResultReference` filter, and error formatting so future edits stay in one place.
🤖 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/runtime.rs`:
- Around line 906-943: Add the required arch-exempt marker immediately above the
`#[allow(clippy::too_many_arguments)]` on
`wrap_outbound_delivery_capabilities_for_test`; the forwarder currently has only
a doc comment, so include a `// arch-exempt: too_many_args, ... , plan `#NNNN``
tag with a short rationale. Keep the annotation paired with the existing
test-only wrapper in `runtime.rs`, and ensure the tag is placed directly before
the allow attribute so the exemption is recognized.
In
`@crates/ironclaw_reborn_composition/src/runtime/local_dev/outbound_delivery.rs`:
- Around line 82-133: The `wrap_outbound_delivery_capabilities_for_test`
function has a `#[allow(clippy::too_many_arguments)]` without the required
arch-exempt tag. Add the missing `// arch-exempt: too_many_args, <reason>, plan
`#NNNN`` comment directly above the allow on this function, using the function
name to locate it; this is the root implementation in
`runtime/local_dev/outbound_delivery.rs`, and the same rule still applies
separately to any downstream forwarders that also carry their own
`#[allow(...)]`.
In `@crates/ironclaw_reborn_composition/src/test_support/outbound_delivery.rs`:
- Around line 26-59: The wrap_outbound_delivery_capabilities_for_test function
is missing the required arch-exempt note for its clippy suppression. Add the
repo-standard `// arch-exempt: too_many_args, <reason>, plan `#NNNN`` comment
immediately associated with the existing `#[allow(clippy::too_many_arguments)]`
in this test-support wrapper so it matches the pattern used in the other
outbound_delivery helpers.
In `@tests/reborn_integration_golden_payload.rs`:
- Around line 136-171: The blocker note documents a real production gap in
ActiveTaskPreservingCompactionStrategy::should_compact, where the
force-compaction flag from
state.compaction_state.force_compact_on_next_iteration is never consulted.
Please verify there is a tracking issue for this dead-letter path and, if not,
add one and reference it from the reborn_integration_golden_payload test comment
so the finding is not left only as narrative text.
In `@tests/reborn_integration_hooks.rs`:
- Around line 1-146: The tests are intentionally RED and should stay ignored
until the checkpoint wrapper bug is fixed; keep the module doc, `#[ignore]`
reasons, and `TODO(reborn-hooks-checkpoint-forward)` aligned on the same root
cause in `HookedLoopCheckpointPort`. If you are doing the follow-up, implement
forwarding for `stage_checkpoint_payload` and `load_checkpoint_payload` through
`ironclaw_hooks::middleware::checkpoint_port::HookedLoopCheckpointPort` to its
inner port, then remove the `#[ignore]`s and update the regression notes so the
tests describe the now-working coordinator-path hook flow.
In `@tests/reborn_integration_skill_activate.rs`:
- Around line 143-187: The final assertion in
skill_activate_over_budget_surfaces_recoverable_failed uses a bare is_err() on
assert_model_request_contains, which can hide unrelated
request-capture/serialization failures. Update that check to follow the same
expect_err(...) plus message-prefix validation pattern already used in
skill_criteria_auto_activation_stays_off_on_coordinator_path, so the test only
passes when the skill instructions were truly not injected and not because of an
infra error.
---
Outside diff comments:
In `@tests/support/reborn/assertions.rs`:
- Around line 270-303: The two collectors duplicate the same history filtering
and ToolResultReferenceEnvelope decode/truncation logic; make
`persisted_tool_error_summaries` delegate to
`persisted_tool_error_summaries_since` with a baseline of 0. Keep the shared
implementation in `persisted_tool_error_summaries_since` and have both methods
use the same `thread_harness.history` handling,
`MessageKind::ToolResultReference` filter, and error formatting so future edits
stay in one place.
🪄 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: 0600beab-9405-4f6e-8f58-cae3d4af9dec
⛔ Files ignored due to path filters (5)
Cargo.lockis excluded by!**/*.lock,!**/Cargo.locktests/snapshots/golden_payload__context_surfacing.snapis excluded by!**/*.snap,!tests/snapshots/**tests/snapshots/golden_payload__greeting.snapis excluded by!**/*.snap,!tests/snapshots/**tests/snapshots/golden_payload__multi_turn.snapis excluded by!**/*.snap,!tests/snapshots/**tests/snapshots/golden_payload__tool_call.snapis excluded by!**/*.snap,!tests/snapshots/**
📒 Files selected for processing (72)
Cargo.tomlcrates/ironclaw_first_party_extensions/tests/gsuite_core.rscrates/ironclaw_mcp/src/lib.rscrates/ironclaw_reborn_composition/src/extension_lifecycle.rscrates/ironclaw_reborn_composition/src/factory.rscrates/ironclaw_reborn_composition/src/oauth_provider_client/tests.rscrates/ironclaw_reborn_composition/src/runtime.rscrates/ironclaw_reborn_composition/src/runtime/local_dev.rscrates/ironclaw_reborn_composition/src/runtime/local_dev/outbound_delivery.rscrates/ironclaw_reborn_composition/src/slack_serve/e2e_tests.rscrates/ironclaw_reborn_composition/src/test_support/mod.rscrates/ironclaw_reborn_composition/src/test_support/outbound_delivery.rstests/reborn_group_approvals/main.rstests/reborn_group_approvals/scenario_approve_always_persists_cross_thread.rstests/reborn_group_approvals/scenario_concurrent_dual_gate_resume.rstests/reborn_group_approvals/scenario_failure_category_demasked.rstests/reborn_group_approvals/scenario_gate_ref_edge_cases.rstests/reborn_group_approvals/scenario_gate_then_approve.rstests/reborn_group_extensions/scenario_remove_then_absent_cross_thread.rstests/reborn_group_journeys/main.rstests/reborn_group_journeys/scenario_auth_deny_then_retry_journey.rstests/reborn_group_journeys/scenario_auth_then_approval_journey.rstests/reborn_group_journeys/scenario_interactive_approval_journey.rstests/reborn_group_journeys/scenario_multi_actor_gate_isolation.rstests/reborn_group_multiuser/main.rstests/reborn_group_multiuser/scenario_auto_approve_isolation_across_actors.rstests/reborn_group_multiuser/scenario_memory_isolation_across_actors.rstests/reborn_group_triggers/main.rstests/reborn_group_triggers/scenario_trigger_self_create_denied.rstests/reborn_group_triggers/scenario_triggered_gate.rstests/reborn_integration_auth_failure.rstests/reborn_integration_auth_gate.rstests/reborn_integration_backend_matrix.rstests/reborn_integration_budget.rstests/reborn_integration_cancel.rstests/reborn_integration_comm_context.rstests/reborn_integration_durable.rstests/reborn_integration_golden_payload.rstests/reborn_integration_hooks.rstests/reborn_integration_http_matcher.rstests/reborn_integration_mcp.rstests/reborn_integration_oauth_connect.rstests/reborn_integration_oauth_refresh.rstests/reborn_integration_outbound_target.rstests/reborn_integration_profile.rstests/reborn_integration_safety.rstests/reborn_integration_secret_injection.rstests/reborn_integration_secrets.rstests/reborn_integration_skill_activate.rstests/reborn_integration_tool_call.rstests/reborn_integration_triggered_submit.rstests/reborn_integration_web_access.rstests/reborn_trace_first_party_tool_coverage.rstests/support/mock_mcp_server.rstests/support/reborn/CLAUDE.mdtests/support/reborn/assertions.rstests/support/reborn/builder.rstests/support/reborn/capability_backend.rstests/support/reborn/comm_context.rstests/support/reborn/github.rstests/support/reborn/golden.rstests/support/reborn/group.rstests/support/reborn/group_constructors.rstests/support/reborn/harness.rstests/support/reborn/harness_mcp.rstests/support/reborn/harness_web_access.rstests/support/reborn/hooks.rstests/support/reborn/mod.rstests/support/reborn/outbound_preferences.rstests/support/reborn/session_thread.rstests/support/reborn/triggered_submit.rstests/support/trace_llm.rs
💤 Files with no reviewable changes (3)
- tests/reborn_integration_durable.rs
- tests/reborn_integration_backend_matrix.rs
- tests/reborn_integration_safety.rs
serrrfirat
left a comment
There was a problem hiding this comment.
Ran code-review-multi --force plus a thermonuclear code-quality pass on 1df3266a8a5bd939e489942c69b92a34eb0ddeb6.
Posted 4 inline findings:
- Medium maintainability: the central Reborn harness absorbs several new capability-specific seams instead of following the existing split-module pattern.
- Medium maintainability: triggered submit test support hand-mirrors the trusted trigger materializer path and can drift from the owning layer.
- Medium test coverage: MCP SSE framing coverage still does not drive
tools/listdiscovery. - Low concurrency/design: the outbound preferences facade records related state under separate mutexes.
Reviewer summary:
- Security: no findings.
- Bugs: no findings; that reviewer timed out initially and returned
[]after a best-effort stop request. - Performance/concurrency: 1 low finding posted.
- Tests: 1 medium finding posted. I did not post the ignored-hooks finding because the file documents the production checkpoint-forwarding blocker and the removal path.
- Conventions + thermonuclear review: 2 structural findings posted.
Note: the codebase graph was present but still incomplete after reindexing in this worktree, so I used targeted PR-worktree inspection for the final review. No local test suite was run by this review pass.
| /// both over); `None` for the lower-level constructors and the Echo backend. | ||
| /// Read back via `attachment_test_support_for_test`. | ||
| attachment_test_support: Option<ironclaw_reborn_composition::AttachmentTestSupport>, | ||
| /// Backing handles for the synthetic `outbound_delivery_*` capabilities |
There was a problem hiding this comment.
Thermo/code-quality: this PR adds outbound state, product-auth, bundled-extension activation, synthetic wrappers, and GitHub auth setup into the central harness, taking it from 4709 to 5326 lines. The support guide already treats harness_mcp.rs and group_constructors.rs as split precedent and says harness_auth.rs is tracked. Can we decompose this before merge? A cleaner shape is harness_outbound.rs for OutboundTargetToolsParts/wrapping/assertion handles and harness_auth.rs for GitHub/auth/bundled-extension setup, leaving harness.rs as the shared state/delegation shell. Anchors: tests/support/reborn/CLAUDE.md:99-108, tests/support/reborn/CLAUDE.md:109-114.
There was a problem hiding this comment.
Confirmed the numbers — harness.rs is 4709→5326 lines this PR. That's genuinely large, but the harness_auth.rs split is already tracked independent of this PR (the CLAUDE.md note predates this branch — verified against origin/main), and the PR body's Follow-ups section explicitly lists the harness.rs split under the consolidation task. Given this branch is 30+ commits deep and every other comment on this file is anchored to exact line numbers, a structural move now would invalidate those anchors mid-review with no correctness gain. Sharpening the follow-up commitment though: new capability-specific state should be born in harness_outbound.rs / harness_auth.rs as pub(super) factories going forward (the harness_mcp.rs pattern) instead of landing inline in harness.rs — so the file stops regrowing wave over wave rather than getting a one-time retroactive split.
| /// are poller-side pre-flight already pinned by | ||
| /// `trigger_poller_trusted_submit.rs`'s own crate-tier tests, and neither | ||
| /// affects the submit→run wire this seam exists to drive. | ||
| pub(crate) async fn submit_triggered_turn_scripted( |
There was a problem hiding this comment.
This helper now hand-mirrors most of ConversationContentRefMaterializer::materialize_prompt and then intentionally omits authorize_trigger_fire and validate_trusted_trigger_prompt. Even though this is test-only, trusted-trigger materialization is an ownership boundary called out in AGENTS.md:61, and duplicating it here means future changes to trigger binding/thread recording can drift from the integration path. Can we move a crate-owned test-support helper beside the real materializer that returns (TriggerMaterializedPrompt, TurnScope) for scripted gateway registration, then have this test support call that instead of rebuilding the resolve/thread/content-ref sequence by hand? That keeps trusted ingress construction in the owning layer and deletes most of this long function.
There was a problem hiding this comment.
Agreed this is a real mirror, not a coincidence — submit_triggered_turn_scripted reconstructs the exact shape of trigger_resolve_request + record_trigger_prompt + the thread-message content ref from materialize_prompt (trigger_poller_trusted_submit.rs:171-213), and both helpers are already pub(crate) in that same file, alongside an existing TenantScopedTrustedTriggerFireAuthorizer test double. So a materialize_trigger_prompt_for_test(..) -> (TriggerMaterializedPrompt, TurnScope) helper reusing all three — running the REAL authorize+validate steps instead of skipping them — is a small mechanical extraction, and that's the exact shape planned. Holding it out of THIS diff deliberately: the branch's bar is zero new production-crate surface mid-review, and the extraction deserves its own test-first cycle with a mutation check proving harness output tracks production. Tracking as a fast-follow with this exact shape, not the general backlog.
serrrfirat
left a comment
There was a problem hiding this comment.
Thermo-nuclear maintainability review: behaviorally this looks test/support scoped, but I think there are a few structural cleanups worth considering before this harness gets harder to evolve.
| /// are poller-side pre-flight already pinned by | ||
| /// `trigger_poller_trusted_submit.rs`'s own crate-tier tests, and neither | ||
| /// affects the submit→run wire this seam exists to drive. | ||
| pub(crate) async fn submit_triggered_turn_scripted( |
There was a problem hiding this comment.
[P1] This helper mirrors production trigger materialization instead of reusing it. It reconstructs the trusted inbound binding, resolve request, thread recording, thread-message:<id> content ref, and gateway setup by hand, while the production path already owns most of that shape in ConversationContentRefMaterializer / record_trigger_prompt. This is a drift trap: future trigger-thread shape changes can break production while this harness keeps passing, or vice versa. Can we expose a #[cfg(feature = "test-support")] materializer helper/result from ironclaw_reborn_composition instead of duplicating the field-by-field flow in test support?
There was a problem hiding this comment.
Agreed this is a real mirror, not a coincidence — submit_triggered_turn_scripted reconstructs the exact shape of trigger_resolve_request + record_trigger_prompt + the thread-message content ref from materialize_prompt (trigger_poller_trusted_submit.rs:171-213), and both helpers are already pub(crate) in that same file, alongside an existing TenantScopedTrustedTriggerFireAuthorizer test double. So a materialize_trigger_prompt_for_test(..) -> (TriggerMaterializedPrompt, TurnScope) helper reusing all three — running the REAL authorize+validate steps instead of skipping them — is a small mechanical extraction, and that's the exact shape planned. Holding it out of THIS diff deliberately: the branch's bar is zero new production-crate surface mid-review, and the extraction deserves its own test-first cycle with a mutation check proving harness output tracks production. Tracking as a fast-follow with this exact shape, not the general backlog.
| /// (C-SYNTH outbound seam). `Some` only for `outbound_target_tools()`; | ||
| /// `create_capability_port` wraps the port with the two capabilities via | ||
| /// `apply_synthetic_capability_wrappers` when this is `Some`. | ||
| outbound_target_tools: Option<OutboundTargetToolsParts>, |
There was a problem hiding this comment.
[P1] HostRuntimeCapabilityHarness is turning into an omnibus fixture with unrelated optional seams: outbound targets, run-owner scoping, product auth, network egress overrides, bundled extension activation, synthetic wrappers, and approval bookkeeping. This file is already over 5k lines after the PR, and this continues the pattern of making it a second composition framework. Can we decompose this first into fixture-family modules/structs, e.g. auth+extension harness, outbound-target harness, approval recording wrapper, and run-owner dispatch policy?
There was a problem hiding this comment.
Confirmed the numbers — harness.rs is 4709→5326 lines this PR. That's genuinely large, but the harness_auth.rs split is already tracked independent of this PR (the CLAUDE.md note predates this branch — verified against origin/main), and the PR body's Follow-ups section explicitly lists the harness.rs split under the consolidation task. Given this branch is 30+ commits deep and every other comment on this file is anchored to exact line numbers, a structural move now would invalidate those anchors mid-review with no correctness gain. Sharpening the follow-up commitment though: new capability-specific state should be born in harness_outbound.rs / harness_auth.rs as pub(super) factories going forward (the harness_mcp.rs pattern) instead of landing inline in harness.rs — so the file stops regrowing wave over wave rather than getting a one-time retroactive split.
| // ones `outbound_delivery_capabilities` consumes in production (the | ||
| // auto-approve + approval-request/lease stores are reused from the | ||
| // sibling `auto_approve_settings` / `approval_parts` harness fields). | ||
| if let Some(parts) = &self.outbound_target_tools { |
There was a problem hiding this comment.
[P2] This adds another capability-specific branch into apply_synthetic_capability_wrappers. The function is becoming the synthetic-capability dispatcher with per-feature special cases. If more synthetic capabilities land, this becomes the central spaghetti switch. A small registry/list of wrapper objects would let each fixture contribute a wrapper without editing this function every wave.
There was a problem hiding this comment.
Looked hard at whether a registry earns its keep here. The three branches share only the outer 'if let Some(state) = &self.field { wrap_x(...) }' skeleton — the real complexity is the non-uniform wrap_* argument lists (project_create ~6 args; outbound 12 across facade/approval/auto-approve/overrides/policies; skill_activate a different backing-source type). A trait-object registry needs each wrapper to close over its backing state at construction, which just moves today's if-let into a build_wrappers() constructor — every new capability still needs a new struct + wire-up line, so per-wave editing cost is unchanged and we add a trait + boxes + dynamic dispatch on the same amount of code. Declining at n=3; will reconsider if a 4th capability lands with the same single-field shape. The outbound 12-arg signature is being fixed directly (typed parts bundle) per the sibling thread.
…ing over-budget assert, SSE tools/list contract, atomic fake state Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IronLoop Review StatusHead: Current reviewers:
Recent activity:
Commands:
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/support/reborn/harness.rs (1)
2582-2584: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRetain
network_egresson the harness
file_and_github_auth_tools()moves the injectedArc<dyn NetworkHttpEgress>intoRebornBuildInput, butSelf { network_egress: None }drops the harness-side handle.network_http_requests()then stays empty for this fixture, so tests can’t assert the actual outbound GitHub calls. Clone theArcbefore passing it intowith_network_http_egress_for_testand store the clone onSelf.🤖 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 `@tests/support/reborn/harness.rs` around lines 2582 - 2584, The harness is dropping its `network_egress` handle in `file_and_github_auth_tools`, so `network_http_requests()` cannot observe outbound GitHub traffic for this fixture. Update the `RebornBuildInput` setup to clone the injected `Arc<dyn NetworkHttpEgress>` before calling `with_network_http_egress_for_test`, and keep that clone on `Self { network_egress: ... }` so the harness retains access to the same requests.
🤖 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 `@tests/support/reborn/harness.rs`:
- Around line 2582-2584: The harness is dropping its `network_egress` handle in
`file_and_github_auth_tools`, so `network_http_requests()` cannot observe
outbound GitHub traffic for this fixture. Update the `RebornBuildInput` setup to
clone the injected `Arc<dyn NetworkHttpEgress>` before calling
`with_network_http_egress_for_test`, and keep that clone on `Self {
network_egress: ... }` so the harness retains access to the same requests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 41afe109-b337-4eee-a9ff-b3c1495d16b6
📒 Files selected for processing (8)
crates/ironclaw_mcp/tests/mcp_adapter_contract.rscrates/ironclaw_reborn_composition/src/runtime.rscrates/ironclaw_reborn_composition/src/runtime/local_dev/outbound_delivery.rscrates/ironclaw_reborn_composition/src/test_support/mod.rscrates/ironclaw_reborn_composition/src/test_support/outbound_delivery.rstests/reborn_integration_skill_activate.rstests/support/reborn/harness.rstests/support/reborn/outbound_preferences.rs
…5574 Merge brings the engine-v2 removal (#5545) and step-efficient tool guidance (#5574). The tool-guidance change shifts the tool-surface content hash inside the golden system prompts — the goldens' only diff is the surface sha256 line, which is exactly the drift they exist to pin. Regenerated both affected snapshots; all wave-3 bins re-verified green on the merged base. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Cargo.toml (1)
283-285: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winGate
ironclaw_hooksout of the default dependency graph. It’s added as a normal[dependencies]entry, so every default build pulls it in; move it behindtest-supportor into[dev-dependencies]if it’s only for the new test/support path.🤖 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 `@Cargo.toml` around lines 283 - 285, The Cargo manifest currently pulls ironclaw_hooks into the default dependency graph through a normal dependency entry. Move the ironclaw_hooks declaration out of the main [dependencies] section and place it behind the test-support path or into [dev-dependencies] if it is only used by tests/support code, keeping the existing intent around the hooks builders intact.
🤖 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 `@Cargo.toml`:
- Around line 283-285: The Cargo manifest currently pulls ironclaw_hooks into
the default dependency graph through a normal dependency entry. Move the
ironclaw_hooks declaration out of the main [dependencies] section and place it
behind the test-support path or into [dev-dependencies] if it is only used by
tests/support code, keeping the existing intent around the hooks builders
intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0f5791a5-9c32-4233-af45-1bf096f2f75b
⛔ Files ignored due to path filters (3)
Cargo.lockis excluded by!**/*.lock,!**/Cargo.locktests/snapshots/golden_payload__context_surfacing.snapis excluded by!**/*.snap,!tests/snapshots/**tests/snapshots/golden_payload__tool_call.snapis excluded by!**/*.snap,!tests/snapshots/**
📒 Files selected for processing (1)
Cargo.toml
…pport (#5609) * test(reborn): extract trigger-prompt materializer test-support helper (wave-4 lane C) Committed follow-up on PR #5584's review thread: submit_triggered_turn_scripted hand-mirrored ConversationContentRefMaterializer::materialize_prompt (trigger_resolve_request + record_trigger_prompt + the content-ref shape, field-by-field) instead of reusing it, and — as flagged — deliberately SKIPPED authorize_trigger_fire and validate_trusted_trigger_prompt. Flagged as a drift trap (trusted-trigger materialization is an ownership boundary, AGENTS.md:61); the review agreed the fix is a #[cfg(feature = "test-support")] materializer helper returning (TriggerMaterializedPrompt, TurnScope) living beside the real materializer, held out of #5584 as a fast-follow with this exact shape. New production-crate (test-support-gated, compiles out of default builds) surface in ironclaw_reborn_composition: - trigger_poller_trusted_submit.rs: materialize_trigger_prompt_for_test, #[cfg(any(test, feature = "test-support"))] — runs the REAL production pipeline via ConversationContentRefMaterializer::materialize_prompt (authorize + validate + resolve + record + content-ref), then an idempotent second resolve_or_create_binding_with_trusted_scope call (safe — same request, same already-created binding) to also return the TurnScope the trait method computes internally but never exposes. Plus two crate-tier unit tests: positive (returned scope/content-ref match an independent ground-truth resolve) and negative (an unsafe prompt is rejected by the REAL safety validator). - test_support/trigger_materializer.rs: pub, feature="test-support"-gated thin wrapper re-exported from test_support/mod.rs — the established wrap_project_create_capability_for_test-style pattern. tests/support/reborn/triggered_submit.rs: submit_triggered_turn_scripted now calls this ONE production-owned helper instead of hand-mirroring; deletes ~90 net lines of duplicated resolve/thread-record/content-ref logic. Verified default-features build of ironclaw_reborn_composition stays warning-free (function/import correctly compile out). Flip-checked at the INTEGRATION level (not just the new crate-unit tests): forced an injection-pattern prompt through submit_triggered_turn_scripted — every triggered-gate scenario correctly failed with "rejected by safety scan", proving the old hand-mirrored path's skip of validate_trusted_trigger_prompt is now closed. Reverted before commit. All touched integration test bins (reborn_group_triggers, reborn_integration_triggered_submit, plus every other wave-4 lane-C bin) rerun green after the extraction. * test(reborn): address trigger materializer review
… pack, triggered auth delivery, attachments, golden/synthetic expansions (#5610) * test(reborn): doc/text + multi-attachment coverage (W4-ATTACH-VARIANTS) Adds submit_turn_with_attachments (generalizes the image-only submit_turn_with_image_attachment to N attachments of any mime type) and two int-tier tests: a text/plain attachment's extracted text reaching the model, and two attachments in one turn both reaching the model with distinct index ordinals. Closes the doc/multi-attachment gap in C-ATTACH (only single-image coverage existed before). * test(reborn): W4-AUTHGATE-WIRE — runtime-401 provider-gate + cancel-no-replay (wave-4 row 1) Pins the #5174/#5180 bug class (empty credential_requirements leaving AuthPromptView.provider null, "Could not save the token" with no network request) through the FULL scripted-gateway integration harness — a tier below the existing crate-level pins, which drive CapabilityHost::invoke_json or HostRuntimeServices::invoke_capability directly and never exercise the real submit_turn -> BlockedAuth wire the WebUI depends on. - tests/reborn_integration_auth_gate.rs: new runtime_401_after_injection_populates_provider_credential_requirement (github credential resolves OK but the runtime HTTP call 401s; asserts the resulting BlockedAuth gate's credential_requirements carries provider=github + ManualToken setup), cancel_blocked_auth_gate_leaves_no_stale_replay (cancelling a BlockedAuth run lands directly on Cancelled with no active worker, and the SAME real gate ref can no longer resume it afterward — closes the #5067/#4957 class of gates staying "live"), and deny_auth_gate_rejects_a_non_auth_gate_ref_prefix (negative companion). Flip-check: temporarily bypassed the host.rs enrichment call site, confirmed the flagship test fails with the exact pre-fix empty-list shape, restored (crates/ironclaw_capabilities/src/host.rs left byte-identical — no production diff). - tests/support/reborn/harness.rs: RecordingNetworkHttpEgress gains an additive FIFO status_queue (default empty -> unchanged hardcoded-200 behavior) + install_network_status_script accessor. Needed because GithubIssueTools' real WASM HTTP call flows through the network-egress lane, not the runtime-egress lane the existing ScriptedHttpResponse matcher scripts (try_with_host_http_egress overwrites the runtime port — see reborn_integration_secret_injection.rs's module doc) — the prior double had no way to script a non-200 status on that lane at all. - tests/support/reborn/builder.rs: with_github_network_status(status) builder method (FIFO) threading github_network_statuses through RebornCapabilityBackend::install. - tests/support/reborn/capability_backend.rs: wires keyed_http_responses (previously dropped for this backend) and the new github_network_statuses into the GithubIssueTools install arm; no-op for existing empty-vec callers. - tests/support/reborn/assertions.rs: assert_network_egress_count, sibling of assert_egress_count for the network-lane call-count proofs above. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(reborn): unknown extension_id fails extension_install safely (W4-EXT-MANIFEST-ERR) Narrowed from the originally-scoped manifest-content arms (schema mismatch/reserved id/forbidden trust level): extension_install's only input is a catalog-resolved extension_id over a fixed, compile-time-embedded bundled catalog, so raw manifest TOML never reaches ManifestV2Error validation through this capability in production. The one reachable, wired arm is an unknown extension_id, which fails Failed{invalid_input} rather than panicking or no-oping. * test(reborn): W4-PROVIDER-VALIDATE — password/traceback caller-gap coverage #5001 (PinchBench bucket D) removed the crude SENSITIVE_PROVIDER_TEXT_MARKERS substring scan on provider reasoning/response_reasoning/signature text (bare words like "password"/"traceback" were false-positive-rejected, driving retry/give-up loops); the entropy-based LeakDetector is the real guard now. That contract was pinned only at the private free-function level (capability_port/provider_validation.rs's own unit test calling validate_provider_tool_call directly) — the #5001 caller gap. Adds provider_tool_call_registration_accepts_password_and_traceback_reasoning_text in crates/ironclaw_loop_support/src/capability_port.rs's existing test module, alongside the crate's other caller-level `port.validate_provider_tool_call(&call)` tests: drives the REAL production caller (LoopCapabilityPort::validate_provider_tool_call / register_provider_tool_call / invoke_capability on HostRuntimeLoopCapabilityPort, the same port the agent loop calls) with "password"/"traceback" in all three metadata fields, and proves genuine acceptance through to a real Completed dispatch (not just a non-error return). Flip-check: temporarily bloated response_reasoning past PROVIDER_METADATA_TEXT_MAX_BYTES to confirm the assertion mechanism discriminates a genuine rejection (fails with the expected "exceeds 16384 bytes" error), then restored the password/traceback content. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(reborn): W4-MCP-SSO-WIRING — NEAR AI host-managed fallback through build_reborn_services #5439 fixed NEAR AI MCP token resolution for SSO users: a Google-SSO user in the same tenant/agent as the boot owner, with no NEAR AI token of their own, now falls back to the host-managed (boot-owner) NEAR AI credential instead of being prompted for one. That contract was pinned only at the private rule/selector level (product_auth_runtime_credentials/tests.rs never calls build_reborn_services) — the composition-wiring gap this row targets. Adds local_dev_nearai_runtime_selection_falls_back_to_host_managed_account_for_sso_user to extension_lifecycle_capabilities_auth_tests.rs (extending the existing in-crate #[cfg(test)] composition-test file — same pattern as the sibling github manual-token test above, template: product_auth_refresh_composition.rs's "drive build_reborn_services directly" style). Drives ONLY the public surface: build_reborn_services (local-dev always derives nearai_mcp_host_managed_scope from the boot owner, so no live NEAR AI config injection is needed) plus the crate-internal runtime_credential_account_selection_service() accessor this file already had precedent for calling. Two discriminating arms on one composed `services`: an SSO user in the owner's tenant/agent (different project -- local-dev's host scope is project-unscoped by design) resolves via fallback; an SSO user under a different tenant does not (CredentialMissing) -- proving the positive arm is a real scope match, not the selector always succeeding. Flip-check: temporarily short-circuited RebornProductAuthServices::runtime_credential_account_selection_service to always return the un-decorated selector (pre-#5439 behavior), confirmed the new test's positive arm fails with CredentialMissing, restored (auth.rs left byte-identical -- no production diff). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(reborn): C-SYNTH deferred arms — AmbiguousSkill seeding + project_create fault-injection (wave-4 lane C) Two carry-over arms deferred from wave-3 PR #5584: - skill_activate AmbiguousSkill: seed a system-scoped AND a user-scoped skill sharing one name (both SkillTrust::Trusted per FilesystemSkillBundleRoot::system/user) so the real validate_explicit_mentions_are_unambiguous reject path fires end-to-end, not just at the skill_activation.rs unit-test level. New seed_user_skill_for_test harness helper (additive, mirrors seed_system_skill_for_test). - project_create fault-injection: new FaultInjectingProjectService test double (project_service_fault.rs) wrapping the real ProjectService at the production-wired Arc<dyn ProjectService> seam, forcing ProjectServiceError::Denied for a sentinel project name and delegating everything else to the real store. New project_tools_with_fault_injection()/project_lifecycle_fault_injected() harness+group constructors (additive). Deliberately NOT ProjectServiceError::Unavailable/Internal: investigation found both route through DefaultRecoveryStrategy's capability-retry branch, whose retry re-dispatch hits a real, confirmed production bug for provider-tool-call-originated invocations under local-dev composition — LocalDevCapabilityIo::resolve_capability_input rejects the reused input_ref on the retry with InvalidInvocation/"capability input ref was not staged for this loop run", collapsing the documented "retry twice, then a model-visible Failed" contract into an immediate terminal driver_unavailable. Documented in project_service_fault.rs; reported separately (not fixed — production change, out of this lane's scope). Both flip-checked (mutated seed/fault-injection to prove discriminating failure) and reverted before commit. * test(reborn): golden payload expansions — parallel tool_calls, image attachment, gated-turn resume (wave-4 lane C) Three scenario expansions to tests/reborn_integration_golden_payload.rs (carry-over from wave-3 PR #5584): - golden_parallel_tool_calls: new RebornScriptedReply::tool_calls([..]) constructor (additive to reply.rs) scripts ONE assistant response with TWO tool_calls[] entries, pinning that multiple calls in one turn each get a distinct id and each following tool-role message's tool_call_id lines up in order — a shape the existing single-call golden_tool_call_feedback can't exercise. - golden_image_attachment_turn: an inline image landed through the real submit_inbound_with_attachments entry point (RebornIntegrationGroup::attachment_tools()), routed through a vision-pattern model id, pinning the multimodal ContentPart::ImageUrl data: URL alongside the text part byte-for-byte. - golden_gated_turn_approve: a real BlockedApproval gate raised, approved, and resumed (RebornIntegrationGroup::live_approvals()), snapshotting BOTH inference calls around the gate — proving the resume doesn't drop, duplicate, or reorder accumulated turn history. Two normalization fixes to golden.rs, both needed for these scenarios to be reproducible (discovered while authoring, not pre-existing regressions): - Attachment-landing scenarios embed today's real UTC date in the landed project path (chrono::Utc::now(), no test seam) — added a second <DATE> filter alongside the existing loop-start-clock <TIMESTAMP> filter, or the image golden would bit-rot on every day boundary. - Tool-call ids come from a NEXT_TOOL_CALL_ID counter shared by every test in this one compiled binary; running more than one tool-call-scripting golden test concurrently (the default `cargo test` thread pool) makes the raw id values order-dependent. Added normalize_tool_call_ids: renumbers every call-<N> to a canonical call-1, call-2, … in order of first appearance per rendered payload, preserving the id/tool_call_id linkage the golden actually cares about without depending on the racy raw value. Confirmed behavior-preserving for the four pre-existing snapshots (no diff) and confirmed the race is fixed (5 consecutive full-suite green runs). Flip-checked (forced two parallel tool_calls to share one id; golden correctly failed) and reverted before commit. * test(reborn): W4-ASK-EACH-ONCE — ask-each-time approval resumes exactly once #5306 fixed an unresumable BlockedApproval loop: require_approval_for_profile_policy checked the explicit ask_each_time override (and the hard-floor force-approval class) BEFORE consulting the matching one-shot approval lease a resume carries, so an approved AskEachTime-gated resume re-hit the ask_each_time branch and re-gated instead of completing. Only a Python E2E test (test_tool_approval.py) exercised this class before; no Rust harness coverage existed. Adds scenario_ask_each_time_resumes_once.rs to the reborn_group_approvals binary (both approvals_group_e2e and its libsql variant), run LAST because it installs a persistent, group-wide ToolPermissionOverride::AskEachTime override on builtin.write_file that would force-gate every sibling scenario's plain-Ask-mode writes. Submits under the override, approves the resulting BlockedApproval gate, and proves the resume reaches Completed in ONE round trip with the write actually persisted — plus a companion "resumes exactly once" proof that re-approving the same now-resolved gate_ref fails NotPending (not a fresh re-raised gate). tests/support/reborn/harness.rs: adds a generic tool_permission_overrides: Option<Arc<dyn ToolPermissionOverrideStore>> field (mirrors the existing auto_approve_settings field's pattern — populated only by new_with_options, None elsewhere) and set_ask_each_time_override_for_test, generalizing disable_outbound_target_set_tool's override-store access beyond outbound_target_tools() to any host-runtime-backed harness/group. Flip-check: temporarily restored the pre-#5306 check order in profile_approval_authorization.rs (ask_each_time/hard-floor before the one-shot lease), confirmed the new scenario fails (the approved write never persists), restored (file left byte-identical — no production diff). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(reborn): triggered-origin chained gated journey (wave-4 lane C) Carry-over from wave-3 PR #5584: a triggered fire whose run raises a BlockedApproval gate, gets resolved, then CHAINS into a SECOND BlockedApproval gate in the SAME run (the post-resume model call issues another gated tool call instead of finalizing), driven through submit_triggered_turn_scripted (E-TRIGGERED-SUBMIT). New scenario_triggered_chained_gate::run_chained_approve, registered as its own live_approvals group in reborn_group_triggers::triggered_gate_group. Re-reads TurnOriginKind::ScheduledTrigger fresh at the coordinator boundary at THREE checkpoints (first park, second/chained park, final Completed) — not just trusting the initial TriggeredSubmission — closing the gap that a resume path rebuilding product_context from a non-trigger-aware default on the SECOND hop would otherwise slip through undetected. Also asserts both gate_refs are genuinely distinct, both chained writes persisted, and the final reply persisted in the trigger's own thread. Flip-checked (asserted the wrong origin kind; the checkpoint helper correctly failed with the real ScheduledTrigger value in the diagnostic) and reverted before commit. Also folds in `cargo fmt` whitespace-only fixes surfaced while formatting this new file (golden.rs, harness.rs, reply.rs, and two golden/skill-activate test files touched by prior lane-C commits) — no semantic change, reran their test bins green after formatting. * test(reborn): extract trigger-prompt materializer test-support helper (wave-4 lane C) Committed follow-up on PR #5584's review thread: submit_triggered_turn_scripted hand-mirrored ConversationContentRefMaterializer::materialize_prompt (trigger_resolve_request + record_trigger_prompt + the content-ref shape, field-by-field) instead of reusing it, and — as flagged — deliberately SKIPPED authorize_trigger_fire and validate_trusted_trigger_prompt. Flagged as a drift trap (trusted-trigger materialization is an ownership boundary, AGENTS.md:61); the review agreed the fix is a #[cfg(feature = "test-support")] materializer helper returning (TriggerMaterializedPrompt, TurnScope) living beside the real materializer, held out of #5584 as a fast-follow with this exact shape. New production-crate (test-support-gated, compiles out of default builds) surface in ironclaw_reborn_composition: - trigger_poller_trusted_submit.rs: materialize_trigger_prompt_for_test, #[cfg(any(test, feature = "test-support"))] — runs the REAL production pipeline via ConversationContentRefMaterializer::materialize_prompt (authorize + validate + resolve + record + content-ref), then an idempotent second resolve_or_create_binding_with_trusted_scope call (safe — same request, same already-created binding) to also return the TurnScope the trait method computes internally but never exposes. Plus two crate-tier unit tests: positive (returned scope/content-ref match an independent ground-truth resolve) and negative (an unsafe prompt is rejected by the REAL safety validator). - test_support/trigger_materializer.rs: pub, feature="test-support"-gated thin wrapper re-exported from test_support/mod.rs — the established wrap_project_create_capability_for_test-style pattern. tests/support/reborn/triggered_submit.rs: submit_triggered_turn_scripted now calls this ONE production-owned helper instead of hand-mirroring; deletes ~90 net lines of duplicated resolve/thread-record/content-ref logic. Verified default-features build of ironclaw_reborn_composition stays warning-free (function/import correctly compile out). Flip-checked at the INTEGRATION level (not just the new crate-unit tests): forced an injection-pattern prompt through submit_triggered_turn_scripted — every triggered-gate scenario correctly failed with "rejected by safety scan", proving the old hand-mirrored path's skip of validate_trusted_trigger_prompt is now closed. Reverted before commit. All touched integration test bins (reborn_group_triggers, reborn_integration_triggered_submit, plus every other wave-4 lane-C bin) rerun green after the extraction. * test(reborn): W4-TRIGSLACK-SETTLE — auth-gate coverage for TriggeredRunDeliveryDriver TriggeredRunDeliveryDriver was exercised by exactly one crate-tier test (triggered_approval_prompt_route_resolves_dm_approve_on_foreign_scope), covering only the approval-gate path. Add the auth-gate twin: a BlockedAuth triggered run whose auth-prompt preference resolves to the creator's DM must carry the OAuth setup link (triggered_auth_prompt_route_delivers_dm_setup_link_on_foreign_scope), mirroring slack_dm_delivers_auth_prompt_with_setup_link_after_immediate_ack's assertion shape but driven through the real triggered-delivery driver. TriggeredRunDeliveryDriver only ever targets the creator's personal DM (never a channel), so there is no literal "channel" arm to mirror slack_channel_auth_prompt_omits_setup_link_after_immediate_ack. The discriminating negative arm instead exercises the driver's own send-time OAuth-DM backstop (triggered_auth_prompt_oauth_target_not_dm_suppresses_setup_link_and_cancels_run): when the resolved auth-prompt target is not a personal DM, the setup link must never be posted and the blocked run must be cancelled instead. ScriptedTriggerCoordinator gains an additive new_with_first_poll constructor (script an arbitrary first-poll status/gate_ref instead of the hardcoded BlockedApproval/GATE pair) and a functional cancel_run (previously unreachable!, since the approval-only scenario never called it) to support the OAuth-not-DM arm. Test code only; no production changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(reborn): extract trigger-prompt materializer test-support helper (wave-4 lane C) Committed follow-up on PR #5584's review thread: submit_triggered_turn_scripted hand-mirrored ConversationContentRefMaterializer::materialize_prompt (trigger_resolve_request + record_trigger_prompt + the content-ref shape, field-by-field) instead of reusing it, and — as flagged — deliberately SKIPPED authorize_trigger_fire and validate_trusted_trigger_prompt. Flagged as a drift trap (trusted-trigger materialization is an ownership boundary, AGENTS.md:61); the review agreed the fix is a #[cfg(feature = "test-support")] materializer helper returning (TriggerMaterializedPrompt, TurnScope) living beside the real materializer, held out of #5584 as a fast-follow with this exact shape. New production-crate (test-support-gated, compiles out of default builds) surface in ironclaw_reborn_composition: - trigger_poller_trusted_submit.rs: materialize_trigger_prompt_for_test, #[cfg(any(test, feature = "test-support"))] — runs the REAL production pipeline via ConversationContentRefMaterializer::materialize_prompt (authorize + validate + resolve + record + content-ref), then an idempotent second resolve_or_create_binding_with_trusted_scope call (safe — same request, same already-created binding) to also return the TurnScope the trait method computes internally but never exposes. Plus two crate-tier unit tests: positive (returned scope/content-ref match an independent ground-truth resolve) and negative (an unsafe prompt is rejected by the REAL safety validator). - test_support/trigger_materializer.rs: pub, feature="test-support"-gated thin wrapper re-exported from test_support/mod.rs — the established wrap_project_create_capability_for_test-style pattern. tests/support/reborn/triggered_submit.rs: submit_triggered_turn_scripted now calls this ONE production-owned helper instead of hand-mirroring; deletes ~90 net lines of duplicated resolve/thread-record/content-ref logic. Verified default-features build of ironclaw_reborn_composition stays warning-free (function/import correctly compile out). Flip-checked at the INTEGRATION level (not just the new crate-unit tests): forced an injection-pattern prompt through submit_triggered_turn_scripted — every triggered-gate scenario correctly failed with "rejected by safety scan", proving the old hand-mirrored path's skip of validate_trusted_trigger_prompt is now closed. Reverted before commit. All touched integration test bins (reborn_group_triggers, reborn_integration_triggered_submit, plus every other wave-4 lane-C bin) rerun green after the extraction. * test(reborn): review fixes — consolidate slack e2e poll helpers, cite #5608 in fault-injection rationale Factor the three near-identical bounded-poll-for-chat.postMessage helpers in slack_serve/e2e_tests.rs into one predicate-parameterized wait_for_post_messages_matching, and replace "Lane C final report" citations with the filed issue (#5608) in the local-dev retry-path rationale comments. * test(reborn): address wave4 review comments * test(reborn): relax auth gate harness wait --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test(reborn): wave-3 integration coverage — journeys, multi-user isolation, triggered/outbound, budget/comm-context/hooks seams, golden payloads, denied-edge contracts
Summary
Behavior-neutral coverage wave for the Reborn backend integration harness. Zero production-behavior changes: the diff is tests, test-support, and
#[cfg(any(test, feature = "test-support"))]-gated composition accessors (compiled out of default builds — verified). Every test double substitutes only at seams production actively wires.What's covered (new)
reborn_group_journeys): the canary twin — auth gate → resolve → approval gate → approve → action with the authed+approved tool → follow-up with context carry-over; deny arms (auth deny → model-visible failure, run continues); deny-then-retry; multi-actor gate isolation (gate resolution + resume state bound to the raising actor).reborn_group_multiuser): memory and auto-approve/approval-settings non-leak across actors, via a harness flag mirroring production's owner→actor resolution. (Confirmed [QA] Memories in the WebUI workspace are visible to every user in the workspace #5460 is a WebUI read-surface bug; the tool path isolates correctly.)ScheduledTriggervsInbound, mutation-verified at the sole production propagation site); gates raised mid-fire (approve + deny arms with side-effect proofs); triggered completion + reply persistence through a production-mirroring prompt materializer.ProtocolHttpEgressat the production seam) → driver-recorded gate route → inbound bare "approve" resolves the gate on the run's foreign scope.reborn_integration_outbound_target): list/set happy paths, settings-Deny →policy_denied, facade NotFound →invalid_input, approval-gated set (approve applies / deny leaves state untouched) — real production capability code, double only at the facade trait seam.reborn_integration_budget,reborn_integration_comm_context): productionbuild_default_budget_accountantandcommunication_context_providerseams live in the harness (semantics remain crate-tier — not re-authored).reborn_integration_hooks): recording/denying hook factories + wiring; scenarios land#[ignore]d RED on production bug reborn: HookedLoopCheckpointPort does not forward stage_checkpoint_payload/load_checkpoint_payload — any hooks-enabled coordinator turn fails at Checkpoint stage #5572 (checkpoint-forward gap) — un-ignore with the fix.reborn_integration_golden_payload): exact-match canonicalized full model-request payloads (insta snapshots; single volatile field = loop-start clock) + exact final-reply asserts — pins end-to-end prompt construction.reborn_integration_tool_call): disabled ids never offered in the tool surface; hallucinated call to a disabled id → pinned terminalmodel_errorcontract (UX follow-up: reborn: hallucinated call to a disabled capability fails the run as model_error instead of a model-visible denial #5583)."turn run not found", with non-vacuity control), cross-tenant secret read (fail-closedUnknownSecret+ correct-tenant re-read control), triggered self-create deny (int-tier fix(reborn): scheduled-trigger fires cannot create/mutate triggers (#5505) #5515 twin: never dispatched +trigger_listproves nothing created), busy-lock release after a Failed run (accepted resubmit, wedge-class), double-resolve (NotPending) + stale gate-ref resume ("gate resolution reference mismatch"), MissingGate bare-resolve, MCP output-too-large (output_too_large, run recovers). Four rows skipped with documented findings: never-registered capability ids are silently dropped pre-stage (no observable), extension installs are tenant-wide by design (no user-scoped seam), no drain seam on the int harness, HostInternal visibility deferred.assert_tool_error*_since, conversation-history containment asserts (fail-loud on out-of-range baselines), script-discipline docs.Assertion meticulousness + de-bloat
A dedicated audit reviewed all 113 wave-3 assertions against the #5573 failure class (weak asserts letting legal-but-untested behavior slip). All 9 HIGH findings fixed (2 adapted with traced justification), 13/16 MED, 6 LOW. Separate commits trim comment bloat suite-wide (−362/+170 across 29 files): play-by-play and history narration removed; why-pins, product decisions, and seam contracts kept.
Production findings from this wave (issues, not fixes here)
model_errorRecordingHostRuntime; resume idempotency key now(run_id, gate_ref)-scoped.Verification
cargo build --tests --all-features;cargo clippy --all --tests --examples --all-features -- -D warnings--features integration(0 failures)--lib(incl.slack-v2-host-betalane) green at--test-threads=4cargo build -p ironclaw_reborn_compositioncompiles all additions outFollow-ups (tracked, not in this PR)
Consolidation task (group.rs setter extraction,
persisted_tool_error_summaries→_since(0)dedup, harness.rs split); chained triggered-origin gated journey; golden parallel-tool-calls + image-parts scenarios; compaction golden (blocked on #5582); needs-seam edge cases (lease-TTL clock, real egress pipeline under harness, admission-cap builder, reopen-resume-through-gate).🤖 Generated with Claude Code