Skip to content

fix(reborn): stabilize tool activity display and gate resume flows - #4978

Merged
think-in-universe merged 18 commits into
mainfrom
codex/reborn-webui-deny-gate-activity
Jun 17, 2026
Merged

think-in-universe merged 18 commits into
mainfrom
codex/reborn-webui-deny-gate-activity

Conversation

@hanakannzashi

@hanakannzashi hanakannzashi commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Surface denied approval/auth-gate resumes as model-visible authorization failures so the model can continue instead of leaving the tool pending or re-dispatching forever.
  • Keep denied tool activity visible and terminal across live updates and refreshes by emitting durable CapabilityActivityFailed milestones.
  • Store pending resume activity_id explicitly in checkpoint state; deny replay now consumes that field, with legacy resume-token fallback only for older checkpoints.
  • Move tool activity ordering into backend projections via activity_order / first activity cursor, then have WebUI live/history rendering consume that canonical order.
  • Remove frontend-only synthetic ordering and preserve terminal activity state when stale gate previews arrive.
  • Normalize live/history activity names and add regression coverage for deny visibility, missing tools, same-capability siblings, and refresh parity.
  • Default NEAR AI tool-result handling back to standard tool-role messages, avoiding user-role tool-result shims unless explicitly enabled.
  • Fix the event-stream contract fixture for the new CapabilityActivityProjection::first_cursor field.

Tests

  • node --test crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/history-messages.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/message-groups.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useHistory.test.mjs
  • cargo test -p ironclaw_product_adapters --lib capability_activity
  • cargo test -p ironclaw_webui_v2 --test webui_v2_schema_contract
  • cargo test -p ironclaw_reborn_composition projection::tests::runtime_stream::webui_event_stream_delivers_prior_completed_activity_before_pending_approval_preview
  • cargo test -p ironclaw_product_workflow --test auth_interaction_contract denied_auth_without_flow_record_resumes_parked_auth_run_with_denial_disposition
  • cargo test -p ironclaw_agent_loop pending_auth_resume -- --nocapture
  • cargo test -p ironclaw_agent_loop denied_approval_resume -- --nocapture
  • cargo test -p ironclaw_agent_loop denied_auth_resume -- --nocapture
  • cargo test -p ironclaw_reborn deny_disposition -- --nocapture
  • cargo clippy -p ironclaw_agent_loop --all-features --all-targets -- -D warnings
  • cargo clippy -p ironclaw_reborn --all-features --all-targets -- -D warnings
  • cargo clippy -p ironclaw_event_streams --all-features --all-targets -- -D warnings
  • cargo fmt --all -- --check
  • git diff --check

Fixes #4977
Closes #4762
Closes #4764
Closes #4983
Closes #4853

Emit denied capability activity as a durable failed activity, surface denied gates back into the model loop, and preserve live WebUI activity across gate resolution/history refresh.
@railway-app

railway-app Bot commented Jun 16, 2026

Copy link
Copy Markdown

This PR was not deployed automatically as @hanakannzashi does not have access to the Railway project.

In order to get automatic PR deploys, please add @hanakannzashi to your workspace on Railway.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3ac07e10-0787-4274-abd6-ccce9adef581

📥 Commits

Reviewing files that changed from the base of the PR and between e1323bf and 3626229.

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

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added deterministic capability activity ordering using first-cursor-based timelines, with activity-order metadata surfaced through previews and outbound payloads.
    • Introduced a new capability authorization failure progress event when capability gates are denied.
    • Enhanced chat UX with auto-expanding active tool runs and improved tool-activity state tracking.
  • Bug Fixes

    • Fixed denied auth/approval resume flows to correctly correlate authorization failures, clear pending resume state, and continue execution.
    • Improved timeline refresh/history merging to better preserve runtime activity cards and ordering.
  • Documentation

    • Updated NEAR AI guidance: standard tool messages are kept by default; legacy flattening is opt-in only.

Walkthrough

Denied capability resumes now emit correlated authorization failures. Activity projections carry stable ordering metadata, WebUI tool-state/history flows preserve terminal tool entries, and NEAR AI tool-message flattening is now opt-in.

Changes

Denied authorization failure, activity ordering, and WebUI reconciliation

Layer / File(s) Summary
Core type contracts
crates/ironclaw_turns/src/ids.rs, crates/ironclaw_turns/src/run_profile/host.rs, crates/ironclaw_event_projections/src/lib.rs
CapabilityActivityId::parse(value: &str) is added; LoopProgressEvent::CapabilityActivityFailed { activity_id, capability_id, reason_kind } and its kind_name() arm are added; CapabilityActivityProjection gains first_cursor and activity_order_cursor().
Outbound schema for activity order and gate invocation
crates/ironclaw_product_adapters/src/outbound.rs, crates/ironclaw_threads/src/capability_display_preview.rs, crates/ironclaw_webui_v2/tests/webui_v2_handlers_contract.rs, crates/ironclaw_webui_v2/tests/webui_v2_schema_contract.rs
CapabilityActivityView, CapabilityDisplayPreviewView, and GatePromptView gain optional activity_order and invocation_id fields with serde compatibility; schema tests populate these fields with expected values.
Persisted resume activity_id state
crates/ironclaw_agent_loop/src/state.rs
PendingAuthResume and PendingApprovalResume gain activity_id, plus activity_id_for_resume() helpers and capability_activity_id_from_resume_token(); checkpoint round-trip and legacy-payload tests cover the new field.
GateStage derives resume activity ids
crates/ironclaw_agent_loop/src/executor/gates.rs, crates/ironclaw_reborn/src/planned_driver.rs
GateStage derives resume activity_id from tokens and stores it in pending auth/approval resumes; staged resume fixtures are updated with activity_id: None.
Denied resume emits correlated capability failure
crates/ironclaw_agent_loop/src/executor/capabilities.rs, crates/ironclaw_agent_loop/src/executor/tests.rs, crates/ironclaw_agent_loop/src/executor/capability_helpers.rs
Denied auth/approval resumes thread an optional denied activity id into short_circuit_denied_resume, which emits CapabilityActivityFailed for authorization failures before the existing error path; tests assert correlated progress events and model-visible authorization errors.
Loop progress milestone routing
crates/ironclaw_reborn/src/loop_driver_host/port_adapters.rs
CapabilityActivityFailed is routed to capability_failed milestone emission.
Projection ordering and replay windowing
crates/ironclaw_event_projections/src/runtime_projection.rs, crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs, crates/ironclaw_reborn_composition/src/projection/display_preview.rs, crates/ironclaw_reborn_composition/src/projection/live_progress.rs, crates/ironclaw_reborn_composition/src/runtime/local_dev.rs
Projection output limit selection uses a separate window comparator; projection sort uses activity_order_cursor() then invocation_id; runtime/replay payloads are re-windowed by recency then projection order; preview store locking is centralized with poison recovery; payloads now carry activity_order; live progress sets activity_order: None where needed.
Gate prompt invocation propagation
crates/ironclaw_reborn_composition/src/projection/turn_events.rs, crates/ironclaw_reborn_composition/src/projection/tests/turn_stream.rs, crates/ironclaw_reborn_composition/src/slack_delivery.rs
Approval prompt lookup now carries both context and invocation id into gate prompt views; non-approval prompts pass None for invocation_id; test assertions verify invocation_id population.
Projection and stream fixture alignment
crates/ironclaw_reborn_composition/src/projection/tests/*, crates/ironclaw_event_projections/tests/replay_projection_contract.rs, crates/ironclaw_event_streams/tests/event_stream_manager_contract/support/builders.rs
Projection, replay, and stream tests now seed first_cursor, activity_order, and invocation_id metadata; ordering expectations are updated for bounded window emission and pending preview behavior.
WebUI tool activity state model
crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.js, tool-activity-state.test.js, tool-activity-state.test.mjs
New terminal-aware tool activity state helpers track messages by invocation, merge gate/tool updates with stable matching, preserve terminal status, and carry durable ordering metadata.
WebUI event projection and local gate resolution
crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js, useChatEvents.test.mjs
Projection handling now respects locally resolved gates, suppresses stale restoration, and settles prompt runs after local resolution; tool lifecycle updates use shared tool-activity-state; tests cover approval-gate annotation, stale projection suppression, and durable ordering.
WebUI useChat denial resolution wiring
crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js, lib/useChat-send.test.mjs, src/assets.rs
useChat tracks per-thread tool activity state, normalizes resolveGate responses via resolveGateOutcome(), applies authorization failure updates on denied-resumed outcomes, and updates tests for new response shape.
WebUI history merge and activity ordering
crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/history-messages.js, history-messages.test.mjs, lib/message-groups.js, message-groups.test.mjs, message-groups.test.js, hooks/useHistory.js, useHistory.test.mjs, lib/gates.js
Tool cards carry activityOrder/activityOrderSource metadata, activity runs are deterministically ordered, full-history merges preserve runtime activity cards, and gate cards retain invocationId.
ActivityRun expansion behavior
crates/ironclaw_webui_v2_static/static/js/pages/chat/components/activity-run.js, activity-run.test.mjs
ActivityRun auto-expands on active tool signals, with unit test covering the expansion path.

NEAR AI tool-message flattening made opt-in

Layer / File(s) Summary
NEAR AI default behavior and request shape
crates/ironclaw_llm/src/nearai_chat.rs, crates/ironclaw_llm/src/nearai_tool_message_flattening.rs, crates/ironclaw_llm/CLAUDE.md, src/agent/dispatcher.rs
NearAiChatProvider::new and ::new_with_timeout default flatten_tool_messages to false; build_chat_completion_request omits tool_choice when no tools exist; comments now describe flattening as an opt-in legacy path; tests assert standard role:"tool" serialization and omitted tool_choice.

Sequence Diagram(s)

sequenceDiagram
  participant CapabilityStage
  participant short_circuit_denied_resume
  participant LoopProgressEvent
  participant Model

  CapabilityStage->>CapabilityStage: denied_activity_id = pending.activity_id_for_resume()
  CapabilityStage->>short_circuit_denied_resume: denied_activity_id
  short_circuit_denied_resume->>LoopProgressEvent: emit CapabilityActivityFailed(Authorization)
  short_circuit_denied_resume->>Model: handle_capability_error
Loading
sequenceDiagram
  participant useChat
  participant failGateToolActivity
  participant useChatEvents
  participant locallyResolvedGatesRef

  useChat->>failGateToolActivity: denied resolution
  failGateToolActivity->>useChat: append authorization failure tool card
  useChat->>locallyResolvedGatesRef: record local resolution
  useChatEvents->>locallyResolvedGatesRef: check stale gate before restoring
  useChatEvents->>useChatEvents: settleTerminalRunAfterResolvedPrompt()
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

  • nearai/ironclaw#4899: Shares the same crates/ironclaw_agent_loop/src/executor/capabilities.rs denied-resume control flow.
  • nearai/ironclaw#4954: Directly extends the approval/auth denied-resume path that this PR finishes with activity-id correlation.

Suggested reviewers

  • serrrfirat
  • think-in-universe

Poem

Denied gates now leave a trace,
with CapabilityActivityFailed in place.
Cursors line up, and history stays true,
while tool cards keep their names and hue.
The old flattening cloak steps back to rest,
and tool messages travel the test. 🚀

@github-actions github-actions Bot added size: XL 500+ changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Jun 16, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements support for user-denied approval gates in the agent loop execution, surfacing a model-visible Authorization failure rather than cancelling the run. It also introduces activity ordering (activity_order) to ensure stable rendering of tool activities in the WebUI, and adds frontend state management to track and display synthetic gate activities and denied gates. The review feedback highlights several opportunities to improve robustness in the frontend JavaScript code by defensively using optional chaining to prevent potential TypeError exceptions when handling activity lists, message indices, and history refreshes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useHistory.js Outdated
…ny-gate-activity

# Conflicts:
#	crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useHistory.js
#	crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js
#	crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.test.mjs
#	crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useHistory.test.mjs

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
crates/ironclaw_agent_loop/src/executor/tests.rs (1)

5540-5554: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the stale doc comment to match approval-deny semantics.

Line 5540 describes an auth-deny (pending_auth_resume) path, but the test at Line 5556 exercises approval-deny via pending_approval_resume. Please update the comment to match the test behavior to avoid misleading future maintenance.

As per coding guidelines: “When you change behavior in a function, re-read its docstring and adjacent comments — update or delete them in the same change.”

🤖 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_agent_loop/src/executor/tests.rs` around lines 5540 - 5554,
The doc comment for the test starting at line 5540 describes an auth-deny
scenario with `pending_auth_resume` and `disposition = Some(Denied)`, but the
actual test exercises an approval-deny path using `pending_approval_resume`.
Update the comment to accurately reflect the approval-deny semantics that the
test actually validates, replacing references to auth-deny terminology with
approval-deny terminology to keep the documentation consistent with the test
implementation.

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.

Inline comments:
In `@crates/ironclaw_agent_loop/src/executor/capabilities.rs`:
- Around line 125-168: The pending_approval_resume state is being cleared at
line 128 before handle_capability_error() can snapshot it for the retry guard,
causing the handler to lose context that this was a denied resume. Additionally,
the partition at lines 129-131 matches only by capability_id, potentially
failing multiple sibling calls with the same capability when only one should be
denied. Move the state.pending_approval_resume = None assignment to after the
for loop completes, so handle_capability_error() can still access it during
processing. Update the partition predicate to match both the denied_cap_id and
the denied_activity_id (parsed from the resume token) to ensure only the
specific denied invocation is failed, not all calls with the same capability_id.

In `@crates/ironclaw_product_workflow/tests/reborn_services_contract.rs`:
- Around line 604-609: The approval-deny contract test assertion at line 3812
still expects RebornResolveGateResponse::Cancelled(_), but the code now returns
a queued ResumeTurnResponse indicating a resumed turn. Update the assertion at
line 3812 and the surrounding test code in the range 3785-3814 to expect and
validate the correct response type that matches the new approval-deny resume
path behavior instead of the cancelled response, ensuring the test actually
drives the real caller behavior and validates the UI/model-visible resume
outcome.

In `@crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs`:
- Around line 125-140: The issue is that capability_activities is sorted in
ascending order (oldest first), but the current code uses
.take(activity_payloads) to select the first N items, which discards the newest
activities and violates the invariant that terminal/completed activities must
remain visible. To fix this in the runtime_payload_candidates() function,
reverse the selection to keep the tail (newest items) instead of the head—either
by sorting in descending order before truncating, or by using the
select_nth_unstable_by pattern from
compare_capability_activities_for_output_window() as a reference model. The same
fix applies to append_activity_replay_candidates() which has the analogous issue
with selecting from an unsorted transitions iterator without prioritizing
terminal activities.

In `@crates/ironclaw_reborn/src/planned_driver.rs`:
- Around line 169-173: The code silently ignores the
`approval_resume_disposition` when `pending_approval_resume` is not present,
which violates the fail-closed requirement for approvals. When
`request.approval_resume_disposition` is Some but
`initial.pending_approval_resume` is None, this mismatch should be treated as an
error condition rather than being silently ignored. Add error handling that
returns an error or halts execution when this condition is detected, ensuring
that an explicit user denial cannot be lost due to a missing or invalid
checkpoint.

---

Outside diff comments:
In `@crates/ironclaw_agent_loop/src/executor/tests.rs`:
- Around line 5540-5554: The doc comment for the test starting at line 5540
describes an auth-deny scenario with `pending_auth_resume` and `disposition =
Some(Denied)`, but the actual test exercises an approval-deny path using
`pending_approval_resume`. Update the comment to accurately reflect the
approval-deny semantics that the test actually validates, replacing references
to auth-deny terminology with approval-deny terminology to keep the
documentation consistent with the test implementation.
🪄 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: ae8d8f49-4dc2-4c94-8d85-163518cb136c

📥 Commits

Reviewing files that changed from the base of the PR and between 89203b0 and 84ffb25.

📒 Files selected for processing (61)
  • crates/ironclaw_agent_loop/src/executor/capabilities.rs
  • crates/ironclaw_agent_loop/src/executor/gates.rs
  • crates/ironclaw_agent_loop/src/executor/tests.rs
  • crates/ironclaw_agent_loop/src/state.rs
  • crates/ironclaw_event_projections/src/runtime_projection.rs
  • crates/ironclaw_product_adapters/src/outbound.rs
  • crates/ironclaw_product_workflow/src/approval_interaction/service.rs
  • crates/ironclaw_product_workflow/src/approval_interaction/types.rs
  • crates/ironclaw_product_workflow/src/auth_continuation.rs
  • crates/ironclaw_product_workflow/src/auth_interaction/service.rs
  • crates/ironclaw_product_workflow/src/reborn_services.rs
  • crates/ironclaw_product_workflow/tests/approval_interaction_contract.rs
  • crates/ironclaw_product_workflow/tests/auth_interaction_contract.rs
  • crates/ironclaw_product_workflow/tests/product_workflow_contract.rs
  • crates/ironclaw_product_workflow/tests/reborn_services_contract.rs
  • crates/ironclaw_reborn/src/loop_driver_host/port_adapters.rs
  • crates/ironclaw_reborn/src/loop_exit_applier/tests/support.rs
  • crates/ironclaw_reborn/src/planned_driver.rs
  • crates/ironclaw_reborn/src/subagent/completion_observer.rs
  • crates/ironclaw_reborn/src/turn_runner.rs
  • crates/ironclaw_reborn/src/turn_runner/tests/mod.rs
  • crates/ironclaw_reborn/tests/hooks_integration.rs
  • crates/ironclaw_reborn/tests/loop_driver_host.rs
  • crates/ironclaw_reborn/tests/loop_milestone_event_projection.rs
  • crates/ironclaw_reborn/tests/planned_driver_e2e.rs
  • crates/ironclaw_reborn_composition/src/factory/auth_tests.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.rs
  • crates/ironclaw_reborn_composition/src/projection/tests/runtime_stream.rs
  • crates/ironclaw_reborn_composition/src/trigger_poller.rs
  • crates/ironclaw_turns/src/events.rs
  • crates/ironclaw_turns/src/ids.rs
  • crates/ironclaw_turns/src/lib.rs
  • crates/ironclaw_turns/src/memory.rs
  • crates/ironclaw_turns/src/request.rs
  • crates/ironclaw_turns/src/run_profile/driver.rs
  • crates/ironclaw_turns/src/run_profile/host.rs
  • crates/ironclaw_turns/src/status.rs
  • crates/ironclaw_turns/src/store.rs
  • crates/ironclaw_turns/tests/agent_loop_host_contract.rs
  • crates/ironclaw_turns/tests/checkpoint_state_store_contract.rs
  • crates/ironclaw_turns/tests/turn_coordinator_contract.rs
  • crates/ironclaw_webui_v2/tests/webui_v2_handlers_contract.rs
  • crates/ironclaw_webui_v2/tests/webui_v2_schema_contract.rs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/components/activity-run.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/components/activity-run.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useHistory.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/history-messages.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/history-messages.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/message-groups.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/message-groups.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChat-send.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useHistory.test.mjs

Comment thread crates/ironclaw_agent_loop/src/executor/capabilities.rs Outdated
Comment thread crates/ironclaw_product_workflow/tests/reborn_services_contract.rs
Comment thread crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs Outdated
Comment thread crates/ironclaw_reborn/src/planned_driver.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: 2

Caution

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

⚠️ Outside diff range comments (1)
crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js (1)

622-631: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Inconsistent return type: false vs null for missing state.

Line 623 returns false when !runId, but lines 625 and 630 return null. Callers check truthiness so it works, but the mixed types obscure intent and could confuse future maintenance.

 function locallyResolvedStateForRun(locallyResolvedGatesRef, runId) {
-  if (!runId) return false;
+  if (!runId) return null;
   const resolved = locallyResolvedGatesRef?.current;
🤖 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_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js`
around lines 622 - 631, The function locallyResolvedStateForRun has inconsistent
return types across different code paths: it returns false when !runId is true
(line 623), but returns null when resolved state is missing or no matching entry
is found (lines 625 and 630). To fix this, change the return statement on line
623 to return null instead of false, ensuring all code paths that fail to find a
valid state return the same consistent type.
🤖 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_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js`:
- Around line 335-348: The call to settleSuccessfulRunAfterResolvedPrompt at
useChatEvents.js lines 335-348 is passing parameters onRunCompleted and
completedRunsRef that don't exist in scope, causing a naming mismatch. In
useChatEvents.js lines 335-348, change the parameters passed to
settleSuccessfulRunAfterResolvedPrompt from onRunCompleted to onRunSettled and
from completedRunsRef to settledRunsRef. In useChatEvents.js lines 546-570,
rename the settleSuccessfulRunAfterResolvedPrompt helper function parameters and
all internal references from onRunCompleted to onRunSettled and from
completedRunsRef to settledRunsRef to match. In useChatEvents.test.mjs at line
673, change the test assertion from harness.completedRuns to harness.settledRuns
and ensure it validates the expected shape with runId and success fields.

In
`@crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.test.mjs`:
- Around line 622-674: The test assertion at the end of the test "useChatEvents:
parent completion after resumed auth cancel clears typing and refetches"
references harness.completedRuns which does not exist on the harness object.
Replace harness.completedRuns with harness.settledRuns in the final
assert.deepEqual call to match the actual property exposed by the test harness,
ensuring the test correctly validates the expected behavior of settled runs
after a parent completion event.

---

Outside diff comments:
In `@crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js`:
- Around line 622-631: The function locallyResolvedStateForRun has inconsistent
return types across different code paths: it returns false when !runId is true
(line 623), but returns null when resolved state is missing or no matching entry
is found (lines 625 and 630). To fix this, change the return statement on line
623 to return null instead of false, ensuring all code paths that fail to find a
valid state return the same consistent type.
🪄 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: a5e8bbaf-1b0a-4e47-a63d-f9ec87966e58

📥 Commits

Reviewing files that changed from the base of the PR and between cc8886e and 929d51f.

📒 Files selected for processing (4)
  • crates/ironclaw_product_workflow/src/auth_interaction/service.rs
  • crates/ironclaw_product_workflow/tests/auth_interaction_contract.rs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.test.mjs

Comment thread crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js 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

Caution

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

⚠️ Outside diff range comments (1)
crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js (1)

331-348: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Settle every terminal parent run after a resumed prompt, not only success.

When the locally resolved prompt run differs from the terminal parent runId, this branch marks the parent status stale. Today only successful parent terminals are handled; failed, cancelled, or recovery_required hit continue, leaving typing active and skipping timeline refetch/error UI. This breaks the resumed auth-cancel/deny completion path. Add a caller-level regression for a failed/recovery parent terminal after a resumed local gate.

🐛 Proposed direction
-        if (
-          SUCCESS_RUN_STATUSES.has(status) &&
-          activeResolvedPromptState?.outcome === "resumed"
-        ) {
-          settleSuccessfulRunAfterResolvedPrompt({
+        if (activeResolvedPromptState?.outcome === "resumed") {
+          settleTerminalRunAfterResolvedPrompt({
             runId,
             activePromptRunId: activeRunRef?.current?.runId,
+            success: SUCCESS_RUN_STATUSES.has(status),
+            status,
+            failureCategory,
+            failureSummary,
+            setMessages,
             setIsProcessing,
             setPendingGate,
             setActiveRun,
             onRunSettled,
             settledRunsRef,
@@
-function settleSuccessfulRunAfterResolvedPrompt({
+function settleTerminalRunAfterResolvedPrompt({
   runId,
   activePromptRunId,
+  success,
+  status,
+  failureCategory,
+  failureSummary,
+  setMessages,
   setIsProcessing,
   setPendingGate,
   setActiveRun,
@@
-  settleRun(settledRunsRef, onRunSettled, runId, true);
+  settleRun(settledRunsRef, onRunSettled, runId, success);
+  if (status === "failed" || status === "recovery_required") {
+    appendRunFailureMessage(setMessages, {
+      runId,
+      status,
+      failureCategory,
+      failureSummary,
+    });
+  }
 }

Also applies to: 546-567

🤖 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_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js`
around lines 331 - 348, The current condition only handles successful parent run
terminals using SUCCESS_RUN_STATUSES when the actively resolved prompt state
outcome is resumed, but it should handle all terminal parent statuses (including
failed, cancelled, and recovery_required) to properly settle the parent run and
update the UI. Replace the SUCCESS_RUN_STATUSES check with a broader condition
that captures all terminal status types, ensuring that
settleSuccessfulRunAfterResolvedPrompt (or equivalent settlement logic) is
called for any terminal parent run after a resumed prompt, not just successful
ones, to prevent typing from remaining active and ensure timeline refetch and
error UI are properly displayed.

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.

Inline comments:
In `@crates/ironclaw_reborn_composition/src/projection/display_preview.rs`:
- Around line 215-228: The has_pending_input_for_activity() function currently
returns true if any pending input exists for the entire run, rather than
checking if the pending input specifically belongs to the given activity. You
need to scope the pending check to the specific activity by validating that
pending input refs belong to this activity's InvocationId or activity identifier
before returning true. Instead of just checking if refs exist for the run_id,
verify that the pending refs are associated with this particular activity (e.g.,
by matching activity identifiers or invocation IDs). The same fix also needs to
be applied at the other location around lines 274-286 which has the same scoping
issue.

---

Outside diff comments:
In `@crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js`:
- Around line 331-348: The current condition only handles successful parent run
terminals using SUCCESS_RUN_STATUSES when the actively resolved prompt state
outcome is resumed, but it should handle all terminal parent statuses (including
failed, cancelled, and recovery_required) to properly settle the parent run and
update the UI. Replace the SUCCESS_RUN_STATUSES check with a broader condition
that captures all terminal status types, ensuring that
settleSuccessfulRunAfterResolvedPrompt (or equivalent settlement logic) is
called for any terminal parent run after a resumed prompt, not just successful
ones, to prevent typing from remaining active and ensure timeline refetch and
error UI are properly displayed.
🪄 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: 9dab8ad8-44e8-4120-b316-f4f60c99144c

📥 Commits

Reviewing files that changed from the base of the PR and between 929d51f and a7b149a.

📒 Files selected for processing (6)
  • crates/ironclaw_reborn_composition/src/projection/display_preview.rs
  • crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs
  • crates/ironclaw_reborn_composition/src/slack_delivery.rs
  • crates/ironclaw_reborn_composition/src/slack_serve/e2e_tests.rs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.test.mjs

Comment thread crates/ironclaw_reborn_composition/src/projection/display_preview.rs Outdated
@hanakannzashi
hanakannzashi force-pushed the codex/reborn-webui-deny-gate-activity branch from a7b149a to 0bd9fe7 Compare June 16, 2026 16:11
@github-actions github-actions Bot added scope: agent Agent core (agent loop, router, scheduler) scope: docs Documentation risk: medium Business logic, config, or moderate-risk modules and removed risk: low Changes to docs, tests, or low-risk modules labels Jun 17, 2026

@think-in-universe think-in-universe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

✅ Code review approval for head acc571ac39baf5fb5cd9e87a8e93b06c99d9e0e7.

I reviewed the approval-deny activity ordering changes across the runtime projection, Reborn composition projection, WebUI event handling, and targeted tests. The gate/activity flow now keeps denied capability activity visible and preserves durable activity ordering through replay.

I found one CI compile issue after the new CapabilityActivityProjection::first_cursor field was added: crates/ironclaw_event_streams/tests/event_stream_manager_contract/support/builders.rs still used the old fixture shape. I pushed acc571ac3 to add the fixture field.

Local verification:

  • node --test crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChatEvents.test.mjs
  • node --test crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.test.mjs
  • node --test crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/message-groups.test.mjs
  • cargo test -p ironclaw_event_streams --test event_stream_manager_contract
  • cargo check -p ironclaw_event_streams --tests
  • cargo test -p ironclaw_reborn_composition projection::tests::runtime_stream -- --nocapture

The replacement CI run is queued, so I am not posting final human-review guidance yet.

@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

Caution

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

⚠️ Outside diff range comments (1)
crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js (1)

66-74: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

resolveGateOutcome misclassifies fallback responses as cancelled.

Line 70 uses response?.already_terminal !== undefined, which is true for both true and false. That can force "cancelled" and skip setActiveRun(...) at Line 476 even when the run continues, leaving live gate/run state inconsistent.

Suggested fix
 function resolveGateOutcome(response) {
   if (response?.outcome) return response.outcome;
   const status = String(response?.status || "").toLowerCase();
   if (status === "queued" || status === "running") return "resumed";
-  if (status === "cancelled" || response?.already_terminal !== undefined) {
+  if (status === "cancelled" || response?.already_terminal === true) {
     return "cancelled";
   }
   return null;
 }

Also applies to: 468-482

🤖 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_webui_v2_static/static/js/pages/chat/hooks/useChat.js` around
lines 66 - 74, The resolveGateOutcome function incorrectly checks if
response?.already_terminal is defined using !== undefined, which evaluates to
true for both true and false values, causing false positives where non-terminal
responses are classified as "cancelled". Fix this by changing the condition to
explicitly check if already_terminal is true (either using === true or relying
on truthiness), so that only responses with already_terminal actually set to
true trigger the "cancelled" outcome and prevent the run state inconsistency
issue.
♻️ Duplicate comments (1)
crates/ironclaw_agent_loop/src/executor/capabilities.rs (1)

141-150: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve and match the specific parked denied resume.

The call sites clear pending_*_resume before handle_capability_error() snapshots resume-origin state at Lines 774-783, so the retry guard can treat a denied resume as a fresh call. The helper also partitions by capability_id only, so one denied gate can fail every same-capability sibling while emitting CapabilityActivityFailed for only the first via denied_activity_id.take(). Keep the pending resume available or pass an explicit resume-origin flag, and match the parked call by full resume identity (capability_id, input_ref, surface_version, or activity id where available); if no call matches, fail loud instead of clearing and continuing. This is the same failure mode noted in the previous review, still visible after the helper extraction. As per coding guidelines, Rust code must “Fail closed for auth, approvals, trust…” and the repo invariant says “Fail loud” rather than silently poisoning downstream state.

Also applies to: 185-194, 1051-1059

🤖 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_agent_loop/src/executor/capabilities.rs` around lines 141 -
150, The code is clearing state.pending_auth_resume before calling
short_circuit_denied_resume, which prevents proper matching of the specific
parked denied resume. Instead of clearing pending_auth_resume upfront, preserve
it and pass it explicitly to short_circuit_denied_resume (or keep it available).
Modify the logic to match the denied resume by its full identity using
capability_id, input_ref, surface_version, or activity_id rather than
partitioning by capability_id only, to prevent one denied gate from affecting
sibling capabilities. If no matching parked call is found, fail loudly with an
error instead of silently clearing the state and continuing, per the requirement
to "Fail closed for auth" and "Fail loud" rather than poisoning downstream
state. This applies to all three call sites at lines 141-150, 185-194, and
1051-1059.

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.

Inline comments:
In `@crates/ironclaw_reborn_composition/src/projection/display_preview.rs`:
- Around line 121-123: The early return in the error handling of
self.pending.lock() causes silent failures when the lock is poisoned, making all
subsequent record_input calls no-ops and losing preview input state invisibly.
Instead of returning early when the lock acquisition fails in the `else` clause,
log an error message to make the failure visible to developers, adhering to the
"fail loud" principle and ensuring state-update failures are flagged rather than
silently ignored.

---

Outside diff comments:
In `@crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js`:
- Around line 66-74: The resolveGateOutcome function incorrectly checks if
response?.already_terminal is defined using !== undefined, which evaluates to
true for both true and false values, causing false positives where non-terminal
responses are classified as "cancelled". Fix this by changing the condition to
explicitly check if already_terminal is true (either using === true or relying
on truthiness), so that only responses with already_terminal actually set to
true trigger the "cancelled" outcome and prevent the run state inconsistency
issue.

---

Duplicate comments:
In `@crates/ironclaw_agent_loop/src/executor/capabilities.rs`:
- Around line 141-150: The code is clearing state.pending_auth_resume before
calling short_circuit_denied_resume, which prevents proper matching of the
specific parked denied resume. Instead of clearing pending_auth_resume upfront,
preserve it and pass it explicitly to short_circuit_denied_resume (or keep it
available). Modify the logic to match the denied resume by its full identity
using capability_id, input_ref, surface_version, or activity_id rather than
partitioning by capability_id only, to prevent one denied gate from affecting
sibling capabilities. If no matching parked call is found, fail loudly with an
error instead of silently clearing the state and continuing, per the requirement
to "Fail closed for auth" and "Fail loud" rather than poisoning downstream
state. This applies to all three call sites at lines 141-150, 185-194, and
1051-1059.
🪄 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: 4df1cb71-1884-4568-bb59-ce03893035fa

📥 Commits

Reviewing files that changed from the base of the PR and between 0bd9fe7 and aeef465.

📒 Files selected for processing (34)
  • crates/ironclaw_agent_loop/src/executor/capabilities.rs
  • crates/ironclaw_agent_loop/src/executor/tests.rs
  • crates/ironclaw_event_projections/src/lib.rs
  • crates/ironclaw_event_projections/src/runtime_projection.rs
  • crates/ironclaw_event_projections/tests/replay_projection_contract.rs
  • crates/ironclaw_llm/CLAUDE.md
  • crates/ironclaw_llm/src/nearai_chat.rs
  • crates/ironclaw_llm/src/nearai_tool_message_flattening.rs
  • crates/ironclaw_loop_support/tests/compaction_task_contract.rs
  • crates/ironclaw_product_adapters/src/outbound.rs
  • crates/ironclaw_reborn_composition/src/projection.rs
  • crates/ironclaw_reborn_composition/src/projection/display_preview.rs
  • crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs
  • crates/ironclaw_reborn_composition/src/projection/tests/display_preview.rs
  • crates/ironclaw_reborn_composition/src/projection/tests/display_preview_runtime.rs
  • crates/ironclaw_reborn_composition/src/projection/tests/runtime_stream.rs
  • crates/ironclaw_reborn_composition/src/projection/tests/turn_stream.rs
  • crates/ironclaw_reborn_composition/src/projection/turn_events.rs
  • crates/ironclaw_reborn_composition/src/runtime/local_dev.rs
  • crates/ironclaw_reborn_composition/src/slack_delivery.rs
  • crates/ironclaw_threads/src/capability_display_preview.rs
  • crates/ironclaw_threads/tests/filesystem_session_thread_contract.rs
  • crates/ironclaw_threads/tests/session_thread_contract.rs
  • crates/ironclaw_webui_v2/tests/webui_v2_schema_contract.rs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/gates.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/history-messages.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/message-groups.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/message-groups.test.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/message-groups.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.test.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChat-send.test.mjs
  • src/agent/dispatcher.rs

Comment thread crates/ironclaw_reborn_composition/src/projection/display_preview.rs Outdated

@think-in-universe think-in-universe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

✅ Code review approval for head 5a29a86994e9dc5126f298a8ae7e9a53992c3ea7.

I addressed the unresolved review thread about silently dropping display-preview input capture on a poisoned lock. record_input now logs and recovers the poisoned pending guard with into_inner() instead of returning early. I also updated the display-preview runtime tests to match this PR's current activity-before-preview replay contract.

Verified locally:

  • cargo test -p ironclaw_reborn_composition projection::tests::display_preview -- --nocapture
  • cargo test -p ironclaw_reborn_composition projection::tests::runtime_stream -- --nocapture
  • git diff --check

CI has restarted for the new head, so I am still holding final human-review guidance until the check rollup is green.

Copy link
Copy Markdown
Collaborator

✅ Code review approval for head e064f2c0e25ea96a72322168bdd1abc6d38660b1.

I reviewed the new commit e064f2c0 (Fix WebUI gate outcome and preview cursor handling) after the previously reviewed head. I checked the delta in:

  • crates/ironclaw_reborn_composition/src/projection/display_preview.rs
  • crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChat-send.test.mjs

I did not find actionable issues. The update makes completed-preview cursor holding conditional on an actual pending input for the run, recovers display-preview mutex poisoning instead of silently dropping previews, interleaves activity/preview replay candidates to preserve cursor semantics, and fixes already_terminal: false gate responses to keep the UI in resumed/processing state. The new tests cover the poisoned preview-store recovery and the already_terminal: false gate response path.

I am not formally approving the PR. CI is still pending on this head, so I am not posting final human-review guidance yet.

@think-in-universe think-in-universe changed the title Fix Reborn WebUI approval-deny activity ordering fix(reborn): Reborn WebUI approval-deny activity ordering Jun 17, 2026
@think-in-universe

Copy link
Copy Markdown
Collaborator

@claude review

@claude

claude Bot commented Jun 17, 2026

Copy link
Copy Markdown

Code Review: PR #4978

Reviewed against root CLAUDE.md, ironclaw_agent_loop/CLAUDE.md, and ironclaw_turns/run_profile/CLAUDE.md.

Found 2 Issues:

  1. [LOW:HIGH] State contract boundary concern — Violation of ironclaw_agent_loop specification

    The new activity_id: Option<CapabilityActivityId> fields on both PendingApprovalResume and PendingAuthResume store domain identifiers in resumable state. Per CLAUDE.md spec: "State stores refs, cursors, counters, versions, and safe summaries only." While the implementation is functionally correct, the PR already provides fallback logic via activity_id_for_resume() to reconstruct the ID from resume_token when absent—suggesting explicit storage may not be necessary for correctness. Consider whether this explicit field is required or if reconstruction-on-demand is sufficient.

  2. [LOW:MEDIUM] Implicit codec contract between CapabilityActivityId and CapabilityResumeToken

    The code assumes CapabilityActivityId::parse(resume_token.as_str()) will succeed when the token was originally constructed from an activity ID. There is no explicit validation that the token format matches the UUID format expected by the parser. If a resume token were ever constructed differently, the .ok() in capability_activity_id_from_resume_token() silently drops the error. This creates a latent gap: two types with different semantics accidentally overlap in wire encoding. Consider adding a formal token format contract or explicit validation.

No Issues Found:

  • ✅ Security: No panics, unwrap/expect in production code, race conditions, or unsafe blocks
  • ✅ Bugs: Denial short-circuit logic is correct; activity_id consumed exactly once via .take()
  • ✅ Performance: No N+1 emissions, unbounded loops, or blocking in async code
  • ✅ Tests: New test coverage properly validates activity_id emission in progress events

@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_webui_v2_static/static/js/pages/chat/hooks/useChat.js (1)

466-469: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard denied tool-failure marking by normalized gate outcome.

failGateToolActivity(...) runs for every "denied" click, even when the server reports a stale/terminal gate (already_terminal: true → "cancelled"). That can reintroduce the exact state-drift this PR is fixing (terminal activity overwritten by synthetic auth failure).

Suggested fix
-      if (resolution === "denied") {
-        failGateToolActivity(setMessages, pendingGate, toolActivityStateRef);
-      }
       const outcome = resolveGateOutcome(response);
+      if (resolution === "denied" && outcome === "resumed") {
+        failGateToolActivity(setMessages, pendingGate, toolActivityStateRef);
+      }

Also applies to: 470-477

🤖 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_webui_v2_static/static/js/pages/chat/hooks/useChat.js` around
lines 466 - 469, The failGateToolActivity function is being called based on the
raw resolution value "denied" before the gate outcome is normalized through
resolveGateOutcome. This can cause the function to incorrectly mark an activity
as failed when the server actually indicates a stale/terminal gate
(already_terminal: true, which normalizes to "cancelled"). Move the
failGateToolActivity call to occur after the normalized outcome is calculated,
and guard it by checking that the normalized outcome from
resolveGateOutcome(response) is "denied" rather than checking the raw resolution
value.
🤖 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_webui_v2_static/static/js/pages/chat/hooks/useChat.js`:
- Around line 466-469: The failGateToolActivity function is being called based
on the raw resolution value "denied" before the gate outcome is normalized
through resolveGateOutcome. This can cause the function to incorrectly mark an
activity as failed when the server actually indicates a stale/terminal gate
(already_terminal: true, which normalizes to "cancelled"). Move the
failGateToolActivity call to occur after the normalized outcome is calculated,
and guard it by checking that the normalized outcome from
resolveGateOutcome(response) is "denied" rather than checking the raw resolution
value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 982d52c6-f52a-471f-9757-c4ba251bf2c0

📥 Commits

Reviewing files that changed from the base of the PR and between 3e677e9 and e064f2c.

📒 Files selected for processing (4)
  • crates/ironclaw_reborn_composition/src/projection/display_preview.rs
  • crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChat-send.test.mjs

Copy link
Copy Markdown
Collaborator

Human final-review guidance for current head e064f2c0e25ea96a72322168bdd1abc6d38660b1.

Current status:

  • CI check rollup is green.
  • GitHub reports the PR as mergeable with no conflicts.
  • The PR is non-draft.
  • I reviewed the current head and did not find actionable code issues; I have not formally approved the PR.

Suggested human review focus:

  1. Review the approval-deny/resume activity ordering semantics end to end, especially that pending auth/approval resume state now carries explicit activity_id while preserving compatibility with older checkpoints via fallback parsing.
  2. Check the display-preview cursor behavior in crates/ironclaw_reborn_composition/src/projection/display_preview.rs and runtime_replay.rs: completed-preview cursor holding should happen only when a run actually has pending input, and replay interleaving should preserve activity/preview order.
  3. Verify the WebUI gate handling in crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js, especially that already_terminal: false responses keep the UI in a resumed/processing state instead of prematurely treating the gate as terminal.
  4. Review the new regression coverage for poisoned preview-store recovery and already_terminal: false gate responses, and decide whether the coverage is enough for the production caller paths involved in approval deny/resume flows.

@think-in-universe think-in-universe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

❌ Code review request changes for head 0309973c55d54d92e88973f3f34af230dc54349c.

I reviewed the new commit 0309973c (Fix stale gate deny activity marking). I found one Medium issue: the new already_terminal: true path avoids synthesizing a failed activity, but the caller still unconditionally marks the UI as processing after the terminal response.

I am not formally requesting changes via GitHub review state and I am not approving the PR. CI is running again on this head, so no final human-review guidance yet.

@hanakannzashi hanakannzashi changed the title fix(reborn): Reborn WebUI approval-deny activity ordering fix(reborn): stabilize tool activity display and gate resume flows Jun 17, 2026

Copy link
Copy Markdown
Collaborator

✅ Code review approval for head 9d79441d6ec8d807fce1662e6c14ede685f4b8aa.

I reviewed the new commit 9d79441d (Fix terminal gate processing state) after the previously reviewed head. I checked the delta in:

  • crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChat-send.test.mjs

The change addresses the prior terminal-gate finding: already_terminal: true no longer turns isProcessing back on, active run state is cleared for non-resumed outcomes, and the caller-level regression asserts that behavior. I found no new actionable issues in this delta. I did not formally approve the PR.

…ny-gate-activity

# Conflicts:
#	crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js
#	crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChat-send.test.mjs

Copy link
Copy Markdown
Collaborator

✅ Code review update for current head e1323bfbe9089e83ca79b7d8512f4d641a25e2f6.

I reviewed the new merge commit e1323bfb (Merge remote-tracking branch 'origin/main' into codex/reborn-webui-deny-gate-activity) and the conflict-resolution surface in:

  • crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/useChat-send.test.mjs
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/tool-activity-state.js

I found no new PR-scoped issues. The previous Medium already_terminal: true processing bug is addressed: resolveGateOutcome() now maps already_terminal === true to the terminal/cancelled path, clears processing/active run, and the caller-level useChat.approve deny with already_terminal true... test asserts processing is not turned back on.

CI is still pending and the classify check is failing on this head, so no final human-review guidance yet. I did not formally approve the PR.

Copy link
Copy Markdown
Collaborator

✅ Reviewed new head 3626229c3245acd20d09bdac07555a2200ce0ca1 (Fix streaming of activity metadata with pending previews).

I focused on the runtime replay candidate ordering and the caller-level runtime_payloads_for_item/delivery-cursor behavior. The new ordering streams later capability activity metadata before pending preview slots, while keeping the resume cursor positioned so the first invocation's preview is delivered once it materializes. I did not find new blocking issues in this commit.

CI is still pending on this head, so this is not ready for human final review yet.

Copy link
Copy Markdown
Collaborator

✅ Human final-review guidance for current head 3626229c3245acd20d09bdac07555a2200ce0ca1.

Current status:

  • CI check rollup is green.
  • GitHub reports the PR as mergeable with no conflicts.
  • The PR is non-draft.
  • I reviewed the current head and did not find new blocking issues.
  • All current review threads appear resolved.

Please focus final human review on:

  • Approval-denial and gate-resume flow across crates/ironclaw_agent_loop/src/executor/capabilities.rs, crates/ironclaw_reborn/src/planned_driver.rs, and crates/ironclaw_webui_v2_static/static/js/pages/chat/hooks/useChat.js: verify denied/stale/already-terminal gate responses fail closed, do not resume the wrong capability invocation, and do not leave the UI stuck in processing.
  • Runtime projection and replay ordering in crates/ironclaw_reborn_composition/src/projection/runtime_replay.rs, display_preview.rs, and crates/ironclaw_event_projections/src/runtime_projection.rs: confirm terminal/completed tool activity and display-preview payloads remain visible under truncation, replay, and mixed pending/completed runs.
  • Tool activity UI state in activity-run.js, tool-activity-state.js, useHistory.js, and useChatEvents.js: verify null/undefined defensive handling, preserved runtime activity during refreshes, resumed auth-cancel behavior, and parent-run completion settlement in the actual chat surface.
  • LLM/tool-message changes in crates/ironclaw_llm/src/nearai_chat.rs and nearai_tool_message_flattening.rs: confirm provider-facing message flattening still matches NEAR AI expectations and does not drop tool output or mis-order replayed messages.
  • Tests should be reviewed as caller-level regressions, not just helper checks, especially the gate-resolution contract tests, projection replay contracts, and WebUI event/hook tests that protect the observed user workflows.

@think-in-universe
think-in-universe added this pull request to the merge queue Jun 17, 2026
Merged via the queue into main with commit d86dbd7 Jun 17, 2026
70 checks passed
@think-in-universe
think-in-universe deleted the codex/reborn-webui-deny-gate-activity branch June 17, 2026 14:43
theredspoon pushed a commit to theredspoon/ironclaw that referenced this pull request Jun 21, 2026
…earai#4978)

* fix(webui-v2): ignore stale denied gate projections

* fix(reborn): keep denied gate activity visible

Emit denied capability activity as a durable failed activity, surface denied gates back into the model loop, and preserve live WebUI activity across gate resolution/history refresh.

* fix(reborn): stabilize webui tool activity state

* Fix WebUI tool activity ordering

* Move tool activity ordering into projections

* Fix approval deny resume edge cases

* Fix auth-cancel resume and live completion state

* Fix resumed prompt terminal settlement

* Fix Reborn tool results and activity ordering

* Fix denied capability activity replay status

* Make pending resume activity ids explicit

* Fix WebUI gate outcome and preview cursor handling

* Fix stale gate deny activity marking

* Fix terminal gate processing state

* Fix streaming of activity metadata with pending previews

---------

Co-authored-by: think-in-universe <46699230+think-in-universe@users.noreply.github.com>
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: agent Agent core (agent loop, router, scheduler) scope: docs Documentation size: XL 500+ changed lines

Projects

None yet

2 participants