Skip to content

fix(sidebar): clear the red Failed mark on a crash-cut turn once the chat is seen - #25293

Closed
brennanb2025 wants to merge 6 commits into
mainfrom
brennanb2025/crash-cut-seen-gating
Closed

brennanb2025 wants to merge 6 commits into
mainfrom
brennanb2025/crash-cut-seen-gating

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 46 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​714 $\color{#cf222e}{\Huge{\mathbf{−}}}$​71 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​643
Prod 35 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​338 $\color{#cf222e}{\Huge{\mathbf{−}}}$​116 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​222

ELI5

Sometimes Claude or Codex stops in the middle of a reply without you asking: Orca crashed, you quit Orca, Orca closed an idle chat, or the agent's own process died. When that happened, Orca put a red Failed mark on that agent in the sidebar, on the chat's tab and in a few other places. The mark never went away. It stayed red even after you had opened and read the chat.

Now the red mark lasts only until you have seen the chat. After that it reads Done, like any finished reply.

What Changed

This is part 2 of 2. Part 1, #25043, is merged: a crash-cut reply reads "Worked for N" with one explanation. This PR makes its red mark clear once you have seen the chat.

The problem. When the agent's process goes away mid-reply and nobody pressed Stop, Orca records the turn as ended with no verdict, which it reads as the outcome "interruption". Every agent-status surface turned that into a red Failed mark, and nothing cleared it once you had seen the chat:

  • the workspace card dot
  • the agent rows and their status line
  • the collapsed agent summary
  • the chat's own tab in the tab bar
  • Cmd+J recent rows
  • the review-notes "send to agent" menu
  • the Activity page thread's dot, group and status line

Before (as you see it): a red Failed mark on all of those, which stays after you read the chat.

After (as you see it): the mark reads Failed until you open the chat. Then every one of those surfaces reads Done, like any finished turn.

What did not change:

  • A real failure still reads Failed and stays red after you see it.
  • A Stop you pressed and a turn a newer message replaced still read Interrupted.
  • An end Orca cannot prove still reads "Couldn't confirm".
  • The phone's agent list is unchanged (see Differences).
  • The stored outcome and the wire are unchanged.

Mechanism:

  • One rule: "the user has seen this agent's current state" is isAgentTurnAcknowledged (src/shared/agent-turn-acknowledgement.ts). It is Orca's existing acknowledgement: acknowledged at or after the state began. That is the rule that already un-bolds an agent row when you visit it. It adds no new state.
  • The cut's clock: for the red mark, "the state began" is the later of the row's clock and the main agent's own clock (agentAttentionStartedAt).
    • A subagent can keep the row "working" after the main agent was cut. A look at the chat before the cut must not count as having seen it.
    • Auto-acknowledgement of the chat on screen reads the same clock, and the time it records covers it, so looking at the chat still clears the mark.
  • Required, not optional: the verdict's display (agentVerdictDisplayMark) takes that acknowledgement as a required part of what it reads. A surface that leaves it out fails to compile.
  • Joined once per surface: each surface joins the acknowledgement where it builds its rows:
    • the sidebar's agent rows (useWorktreeAgentRows), which also feed the collapsed summary and the notes send menu
    • the workspace card summary
    • the tab bar
    • Cmd+J recent rows
    • Activity threads
  • Caching: the join keeps one object per entry until the entry or the acknowledgement changes, so memoized rows don't re-render. The card and tab caches key on the acknowledgement map.
  • Only the cut changes: only the "interruption" arm reads the acknowledgement. Failures, Stops, replaced turns and unproven ends keep their marks exactly as before.
  • Shared rule: the Activity unread count, dashboard buckets, notification acknowledgement and auto-acknowledgement used their own copies of the comparison. They now call the shared rule. Each keeps the row's clock as before, except auto-acknowledgement, which uses the cut's clock above.

Why

In the common pattern, the unread watermark is part of the record the indicator is computed from, so every surface reads it the same way. An item that errored is marked only while unread. Orca already records when you last looked at each agent, so this adds no state, and the bold row and the red mark now always agree.

Alternatives considered:

  • Store "seen" as a new flag. Rejected. It duplicates the acknowledgement Orca already keeps.
  • Pass an optional "seen" flag to each caller. Rejected after trying it. Callers that forgot it stayed red, including the chat's own tab next to a sidebar that read Done.

Differences from the common pattern

  • Temporary: the record of what you have seen lives in the desktop app, not on the host. The phone has no access to it, so the phone's agent list keeps a cut-short turn red until the next turn (mobile/src/worktree/agent-row-display.ts). Follow-up: keep the acknowledgement on the host so desktop and phone share it.
  • Temporary: a real failure stays red after you see it. Follow-up: clear real failures the same way, red only until seen. This is left out to keep this PR narrow.

Linked Issue

None. Found during live testing of #24233.

Visual Proof

Live check on a second Mac with a dev build carrying both parts and a real Claude chat (isolated profile, app hidden, driven over the DevTools protocol). These are the sidebar's screenshots; the transcript's are in part 1.

Orca killed mid-reply, then relaunched. Before the chat is opened, the sidebar row reads Failed in red:

Unseen cut chat reads Failed in the sidebar

Once the chat is acknowledged as seen, the row, the workspace card and the chat's own tab read Done (before, then after):

Before acknowledgement After acknowledgement: Done

Unchanged case. A real failure (bad model) stays Failed after viewing:

Real failure stays Failed

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

New tests, one per rule:

  • For each outcome, the mark before and after acknowledgement, with "interruption" the only one that changes.
  • A main agent cut while a subagent holds the row open stays red after a look from before the cut. Auto-acknowledgement targets that chat, and the acknowledgement covers the cut's clock.
  • The card summary, live and retained, including when only the acknowledgement changes.
  • The tab bar, also when only the acknowledgement changes.
  • Cmd+J recent rows.
  • The full agent row, the compact row and the collapsed summary.
  • The Activity thread's group and line.
  • The sidebar rows hook's join.
  • The join's object reuse.
  • The transcript is untouched: the turn bar still reads "Failed after N" for a cut turn.

Sweep: 661 renderer test files that fake the store without the acknowledgement map, plus the activity-status test that needed it, all pass (662 files, 5,605 tests).

Ablations. I removed each rule in turn, and its tests went red (17 rules):

  • the mark gate, the at-or-after comparison, and the cut's clock in the mark, in auto-acknowledgement (live and retained) and in the recorded acknowledgement time
  • the joins in the tab bar, Cmd+J, the card summary (live and retained) and the rows hook
  • the card and tab cache keys
  • the card's bold rule
  • Activity's thread and line joins
  • the join's object reuse

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • SSH and mixed versions: no wire or stored-data change. The comparison uses the renderer's acknowledgement time against the host's state clock, as the bold row already does. Clock skew between a remote host and the desktop affects both the same way.
  • Activity history: the Activity page's per-event history titles ("Agent failed") still record what happened at the time. The thread's dot, group and status line follow the seen rule.
  • Folder workspaces: unaffected. The logic keys on agent status rows, not on git.
  • Performance: each join is cached per entry. Acknowledgements change only when you look at an agent, and they now also invalidate the card and tab caches. Results that come out the same are reused, as before.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…n it

When the agent stopped mid-reply without anyone asking (a crash, a quit, an
eviction), every agent-status surface marked the turn Failed, and nothing
cleared it after the user had read the chat.

The verdict's display now takes Orca's existing acknowledgement as a required
part of what it reads, judged by one shared rule against the turn's attention
clock (the later of the row's and the main agent's, since a subagent can hold
the row open past the main agent's cut). Each surface joins it once where it
builds its rows: the sidebar's agent rows (and through them the compact summary
and the notes send menu), the workspace card summary, the tab bar, Cmd-J
recent rows and Activity threads. Auto-acknowledgement of the chat on screen
reads the same clock, and its stamp covers it. Failures, Stops, replaced turns
and unproven ends keep their marks; the phone, which has no acknowledgement
record, keeps the unseen reading.
…pied

The Activity unread count and dashboard row buckets each spelled the same
"acknowledged at or after the current state began" comparison; they now call
the shared rule, with the row's clock as before.
…ipt change

Only the mark's lines change here, so the label change can touch the rest of
this test without the two changes meeting.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready for review (part 2 of 2)

Split from #25043. This part makes the red Failed mark on a reply cut off by an Orca crash, quit or idle close clear once you have seen the chat. Part 1, #25043, changes how that reply reads in the chat. Each part lands safely on its own, in either order.

The problem. After such a cut, the sidebar, the chat's tab, Cmd+J recent rows, the review-notes send menu and the Activity page marked the agent Failed in red, and that mark never cleared, even after you had opened and read the chat.

What changes for you.

  • The red mark shows only until you have seen the chat. After that, every one of those surfaces reads Done.
  • A real failure still stays red after you see it.
  • If this lands before part 1, the reply itself still reads "Failed after N" until part 1 lands.

How it works.

  • It uses the record Orca already keeps of what you have seen, with one shared rule applied where the mark is computed, so every surface agrees.
  • An earlier version passed an optional "seen" flag to each screen. It left the chat's own tab red next to a sidebar that said Done.
  • "Seen" is judged against the main agent's own clock, so a main agent that dies while a subagent is still running shows red until seen.

Deferred (listed in the PR body)

  • The phone's agent list keeps the mark red, because the seen record lives in the desktop app. Follow-up: keep it on the host.
  • Real failures stay red after viewing.

Verified

  • Live, on a second Mac, with the combined build before the split:
    • A crash-cut chat reads Failed until seen, then the row, workspace card and tab read Done.
    • In the hidden test window this waits for Orca to detect a person at the desktop (the same rule the unread bold already follows); one pointer move cleared it within 3 s.
    • A real failure stays red after viewing.
  • After the split, a narrow re-review found nothing lost from the reviewed version and no part 1 code.
  • Tests: 777 tests, 37 mobile tests, and all 662 renderer test files that mock the store pass. Every rule fails its test when removed.
  • Typechecks (node, web) are clean, and CI is green at 9067b96.

Not verified: a real person's click at the desktop, the phone, SSH hosts, and the Activity page and review-notes menu live.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 4, 2026 20:52

@pullfrog pullfrog 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.

ℹ️ No critical issues — one low-stakes clock question inline.

Reviewed changes — part 2 of two: a turn cut short when the agent process vanished (interruption) now shows the red Failed mark only until the user has seen the chat, then reads Done. A real failure, a user Stop, a superseded turn and an unproven end keep their marks; stored outcome and the wire are unchanged, and the phone keeps the unseen reading.

  • New shared acknowledgement rule — src/shared/agent-turn-acknowledgement.ts adds isAgentTurnAcknowledged and agentAttentionStartedAt (the later of the row's clock and the main agent's own, for a subagent holding the row open past a cut). Auto-acknowledgement, dashboard buckets, Activity unread and notification acknowledgement now route through it.
  • The mark gates on seen-ness only for cuts — agentVerdictDisplayMark takes a required acknowledgedAt; the interruption arm returns null once acknowledged, while failure/cancellation/superseded/unconfirmed are unchanged. The required field makes a surface that omits the ack a compile error.
  • Every surface joins the acknowledgement where it builds rows — useWorktreeAgentRows (and through it the card summary and notes send menu), the worktree card dot, the terminal tab dot, Activity threads, recent and jump-palette rows, Cmd+J, and the dashboard row; the card and tab caches now invalidate on the acknowledgement map.
  • Tests — table-driven per-outcome mark before/after acknowledgement, the held-open-by-subagent cut clock, live and retained card summaries, tab-bar, Activity line/group, the rows hook, and join object reuse.
Reviewed changes, cont.

Verified locally: agent-main-agent-verdict, agent-entry-acknowledgement, agent-attention-acknowledgement, worktree-agent-activity-summary, terminal-tab-activity-status, recent-workspace-tab-rows, worktree-card-agent-summary and DashboardAgentRow tests all pass (137 tests across the two runs).

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

const ackAt = acknowledgedAgentTimes[index] ?? 0
out[agent.paneKey] = ackAt < agent.entry.stateStartedAt
for (const agent of agents) {
out[agent.paneKey] = !isAgentTurnAcknowledged(agent.entry)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This row bold emphasis judges seen-ness on the row's own clock (agent.entry.stateStartedAt), while the red failed mark this PR gates and auto-acknowledgement both judge it on the cut's clock (agentAttentionStartedAt). When a subagent holds a row working after the main agent was cut — row began working at 1000, cut at 3000, user acknowledged at 2000 — the mark reads Failed but the row is not bold, the opposite of the PR's "the bold row and the red mark now always agree."

This may be deliberate (the user did see the working row; only the dot is new news), in which case the rationale wording is what should give. Flagging so the two clocks not disagreeing is a conscious choice.

Technical details
# Bold uses the row clock; the mark and auto-ack use the attention clock

## Affected sites
- `src/renderer/src/components/sidebar/WorktreeCardAgents.tsx:98` — `isAgentTurnAcknowledged(agent.entry)` compares the ack against `entry.stateStartedAt`.
- `src/shared/agent-main-agent-verdict.ts:72-77` — the `interruption` mark compares the same ack against `agentAttentionStartedAt(row)` (`max(stateStartedAt, mainAgent.stateStartedAt)`).
- `src/renderer/src/attention/agent-attention-acknowledgement.ts:56-69` — `computeAgentAcknowledgementTargets` (what auto-acknowledgement clears) uses the attention clock.

## Required outcome
- Decide whether the bold emphasis should follow the cut's clock like the mark, or intentionally stay on the row clock. If the latter, soften the "always agree" rationale / note the divergence on `agentAttentionStartedAt`.

## Open questions for the human
- Should `unvisitedByPaneKey` use `agentAttentionStartedAt(agent.entry)` so a cut the user has not seen keeps the row emphasized, matching the dot?

@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8ba5fc74-b440-4309-8e85-9aac9625eff0
📥 Commits

Reviewing files that changed from the base of the PR and between cc65935 and 97143c5.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9fd37ef2-7b3e-43d4-9525-272849e0e93d
📥 Commits

Reviewing files that changed from the base of the PR and between 9067b96 and cc65935.

📒 Files selected for processing (1)
  • src/renderer/src/components/editor/ReviewNotesSendMenuContent.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The changes add shared acknowledgement timing for agent turns. An acknowledged interruption no longer displays as failed, while failure verdicts remain failed. Renderer row, activity-thread, terminal-tab, and recent-workspace status paths now receive acknowledgement timestamps and apply them when deriving display states. Tests cover acknowledgement timing, live and retained entries, and row identity.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to cc659

The recent-workspace palette can retain an outdated Failed mark after a chat is seen, and a zero-time interruption can be cleared without being seen. These are bounded display issues; other live status paths receive the acknowledgement update.

Security Architecture Review

Security architecture risk: 🔵 Low · up to cc659

The inspected changes affect how interrupted replies are displayed after viewing, rather than granting additional access or permission to send work. Remaining uncertainty concerns clock edge cases and recovery behavior; no increased authority was demonstrated.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected propagation reaches pane-status presentation and attention bookkeeping across desktop surfaces. The reviewed send path does not use acknowledgement to grant eligibility or choose the message destination; wider exposure remains incompletely assessed.

Trust Boundaries and Controls

  • observed — The notes-send menu continues to derive execution targets separately from acknowledged display rows. Sending checks target status, rechecks current eligibility by pane identity, and dispatches using the target’s message destination. The full-base comparison shows only type/import substitutions in the changed menu file.

Resilience and Maintainability Implications

  • observed — The recorded clean-finish helper still classifies interruption as an unclean ending without reading acknowledgement. Clearing the display mark therefore does not itself reclassify the turn as a successful completion for that policy.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.37% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 51 functions across 50 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the main change: the red Failed mark clears after the chat is seen. It is specific and related to the pull request.
Description check ✅ Passed The description is detailed and covers the change, rationale, visual proof, testing, and relevant compatibility notes. The Linked Issue section says “None,” although the description references #24233;…
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

❤️ Share

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: dfb04950-9502-4a63-a668-ab5904bc3159
📥 Commits

Reviewing files that changed from the base of the PR and between ddefd52 and 9067b96.

📒 Files selected for processing (81)
  • mobile/src/worktree/agent-row-display.test.ts
  • mobile/src/worktree/agent-row-display.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-recovered-turn-clock.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-stop-event-restart.test.ts
  • src/renderer/src/attention/agent-attention-acknowledgement.test.ts
  • src/renderer/src/attention/agent-attention-acknowledgement.ts
  • src/renderer/src/components/activity/ActivityPrototypePage-test-fixtures.ts
  • src/renderer/src/components/activity/ActivityPrototypePage.test.ts
  • src/renderer/src/components/activity/ActivityPrototypePage.thread-grouping.test.ts
  • src/renderer/src/components/activity/activity-clear-completed-hmr.test.ts
  • src/renderer/src/components/activity/activity-clear-completed-state-lifetime.test.ts
  • src/renderer/src/components/activity/activity-clear-completed.test.ts
  • src/renderer/src/components/activity/activity-event-builder.identity-reuse.test.ts
  • src/renderer/src/components/activity/activity-event-builder.live-cap.test.ts
  • src/renderer/src/components/activity/activity-scope-filter.test.ts
  • src/renderer/src/components/activity/activity-status-group-header.test.ts
  • src/renderer/src/components/activity/activity-thread-actions.test.ts
  • src/renderer/src/components/activity/activity-thread-builder.ts
  • src/renderer/src/components/activity/activity-thread-child-agent.test.ts
  • src/renderer/src/components/activity/activity-thread-grouping.search-cache.test.ts
  • src/renderer/src/components/activity/activity-thread-hover-card.test.tsx
  • src/renderer/src/components/activity/activity-thread-list-pane-collapsible.test.tsx
  • src/renderer/src/components/activity/activity-thread-list-pane.virtualization.test.tsx
  • src/renderer/src/components/activity/activity-thread-presentation.test.ts
  • src/renderer/src/components/activity/activity-thread-presentation.ts
  • src/renderer/src/components/activity/activity-thread-types.ts
  • src/renderer/src/components/activity/activity-thread-virtual-items.test.ts
  • src/renderer/src/components/activity/use-agent-pane-threads.ts
  • src/renderer/src/components/activity/useActivityUnreadCount.ts
  • src/renderer/src/components/agent-child-row-parity.test.tsx
  • src/renderer/src/components/cmd-j/palette-live-status.tsx
  • src/renderer/src/components/dashboard/DashboardAgentChildDisclosure.test.tsx
  • src/renderer/src/components/dashboard/DashboardAgentRow.test.tsx
  • src/renderer/src/components/dashboard/DashboardAgentRow.tsx
  • src/renderer/src/components/dashboard/agent-finished-timestamp.ts
  • src/renderer/src/components/dashboard/dashboard-row-bucket.ts
  • src/renderer/src/components/editor/ReviewNotesSendMenuContent.tsx
  • src/renderer/src/components/native-chat/structured-child-records-switch.test.tsx
  • src/renderer/src/components/sidebar/WorktreeCardAgents.acknowledgement-emphasis.test.tsx
  • src/renderer/src/components/sidebar/WorktreeCardAgents.test.tsx
  • src/renderer/src/components/sidebar/WorktreeCardAgents.tsx
  • src/renderer/src/components/sidebar/stale-agent-row-unverifiable.test.ts
  • src/renderer/src/components/sidebar/subagent-child-row-fallback-pixels.test.tsx
  • src/renderer/src/components/sidebar/use-worktree-activity-status.test.tsx
  • src/renderer/src/components/sidebar/use-worktree-activity-statuses.test.ts
  • src/renderer/src/components/sidebar/use-worktree-activity-statuses.ts
  • src/renderer/src/components/sidebar/useWorktreeAgentRows.acknowledgement.test.tsx
  • src/renderer/src/components/sidebar/useWorktreeAgentRows.ts
  • src/renderer/src/components/sidebar/worktree-agent-activity-summary.test.ts
  • src/renderer/src/components/sidebar/worktree-agent-activity-summary.ts
  • src/renderer/src/components/sidebar/worktree-card-agent-summary.test.ts
  • src/renderer/src/components/sidebar/worktree-card-agent-summary.ts
  • src/renderer/src/components/sidebar/worktree-card-compact-agent-row.stable-message.test.tsx
  • src/renderer/src/components/sidebar/worktree-card-compact-agent-row.tsx
  • src/renderer/src/components/sidebar/worktree-card-compact-agents.tsx
  • src/renderer/src/components/tab-bar/SortableTab.tsx
  • src/renderer/src/components/tab-bar/terminal-tab-activity-status.test.ts
  • src/renderer/src/components/tab-bar/terminal-tab-activity-status.ts
  • src/renderer/src/components/tab-bar/terminal-tab-spinner-launch-agent.test.ts
  • src/renderer/src/components/use-worktree-jump-palette-recent-tabs.ts
  • src/renderer/src/components/use-worktree-jump-palette-store-state.ts
  • src/renderer/src/components/worktree-jump-palette-recent-inclusion.test.ts
  • src/renderer/src/components/worktree-jump-palette-recent-inclusion.ts
  • src/renderer/src/components/worktree-jump-palette-status-inputs.test.ts
  • src/renderer/src/components/worktree-jump-palette-status-inputs.ts
  • src/renderer/src/lib/activity-thread-display.test.ts
  • src/renderer/src/lib/activity-thread-display.ts
  • src/renderer/src/lib/agent-entry-acknowledgement.test.ts
  • src/renderer/src/lib/agent-entry-acknowledgement.ts
  • src/renderer/src/lib/agent-pane-activity-flags.ts
  • src/renderer/src/lib/agent-row-dot-state.ts
  • src/renderer/src/lib/agent-verdict-status-line.ts
  • src/renderer/src/lib/recent-workspace-tab-rows.test.ts
  • src/renderer/src/lib/recent-workspace-tab-rows.ts
  • src/renderer/src/runtime/paired-agent-row-turn-outcome-arms.test.ts
  • src/renderer/src/store/slices/agent-status-observation-neutrality.test.ts
  • src/renderer/src/store/slices/ui-acknowledge-agents-clock-skew.test.ts
  • src/renderer/src/store/slices/ui/ui-slice-agent-notification-acknowledgement.ts
  • src/shared/agent-main-agent-verdict.test.ts
  • src/shared/agent-main-agent-verdict.ts
  • src/shared/agent-turn-acknowledgement.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

unreadTerminalTabs,
unreadAgentCompletionPanes
unreadAgentCompletionPanes,
acknowledgedAgentsByPaneKey

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- diff for target file ---'
git diff --unified=40 ddefd523e052cc83897937569d322a727f55dd78 9067b967bedf93ca4bcc84ce5c32fc993d095b4d -- src/renderer/src/components/use-worktree-jump-palette-store-state.ts
printf '%s\n' '--- target at reviewed head ---'
git show 9067b967bedf93ca4bcc84ce5c32fc993d095b4d:src/renderer/src/components/use-worktree-jump-palette-store-state.ts | nl -ba | sed -n '1,180p'
printf '%s\n' '--- relevant symbol references ---'
rg -n -C 3 'acknowledgedAgentsByPaneKey|paletteStatusInputsActive|useWorktreeJumpPaletteStoreState' src/renderer/src

Repository: stablyai/orca

Length of output: 45665


🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- selector implementation ---'
nl -ba src/renderer/src/components/worktree-jump-palette-status-inputs.ts | sed -n '1,240p'
printf '%s\n' '--- production acknowledgement references ---'
rg -n -C 2 'acknowledgedAgentsByPaneKey' src/renderer/src -g '!*.test.*'
printf '%s\n' '--- palette status resolver consumers ---'
rg -n -C 3 'resolve.*Recent|recent.*status|acknowledgedAgentsByPaneKey' src/renderer/src/components/worktree-jump-palette* src/renderer/src/components/use-worktree-jump-palette* -g '!*.test.*'
printf '%s\n' '--- palette lingering lifecycle references ---'
rg -n -C 3 'lingering|statusInputsLingering' src/renderer/src/components/worktree-jump-palette* src/renderer/src/components/use-worktree-jump-palette* -g '!*.test.*'

Repository: stablyai/orca

Length of output: 42252


🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- auto-ack hook ---'
nl -ba src/renderer/src/hooks/useAutoAckViewedAgent.ts | sed -n '1,180p'
printf '%s\n' '--- auto-ack input and action references ---'
nl -ba src/renderer/src/hooks/agent-auto-ack-surfaces.ts | sed -n '1,130p'
rg -n -C 3 'acknowledgeAgent|acknowledge.*Pane|acknowledgedAgentsByPaneKey:' src/renderer/src/store/slices src/renderer/src/hooks -g '!*.test.*'
printf '%s\n' '--- palette controller callers and lingering owner ---'
rg -n -C 4 'useWorktreeJumpPaletteController|setLingering|lingering' src/renderer/src/components/cmd-j src/renderer/src/components -g '*worktree-jump-palette*' -g '!*.test.*'
printf '%s\n' '--- recent-tab memo and source forwarding ---'
nl -ba src/renderer/src/components/use-worktree-jump-palette-recent-tabs.ts | sed -n '35,145p'
nl -ba src/renderer/src/components/worktree-jump-palette-recent-inclusion.ts | sed -n '25,65p'

Repository: stablyai/orca

Length of output: 42519


🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- acknowledgement action bindings ---'
rg -n -C 5 'acknowledgeAgents' src/renderer/src/store src/renderer/src -g '!*.test.*'
printf '%s\n' '--- all palette controller references ---'
rg -n -C 5 'useWorktreeJumpPaletteController|statusInputsLingering|lingering=' src/renderer/src -g '!*.test.*'

Repository: stablyai/orca

Length of output: 41586


🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- acknowledgeAgents implementation ---'
nl -ba src/renderer/src/store/slices/ui/ui-slice-activity-actions.ts | sed -n '21,102p'
printf '%s\n' '--- palette visible and lingering lifecycle ---'
nl -ba src/renderer/src/components/WorktreeJumpPalette.tsx | sed -n '1,55p'

Repository: stablyai/orca

Length of output: 6288


Refresh the palette snapshot when acknowledgements change.

If acknowledgedAgentsByPaneKey changes while visible || lingering is true and the tab maps stay unchanged, a later render reuses the old paletteIndexStatus snapshot. acknowledgeAgents can update the map without changing those tab maps, and recent-tab status resolution uses its acknowledgement timestamp. Subscribe to the map only while status inputs are active, and include it in the snapshot memo dependencies.

Suggested fix
   const paletteStatusInputsActive = visible || lingering
+  const acknowledgedAgentsByPaneKeyInput = useAppStore((state) =>
+    paletteStatusInputsActive ? state.acknowledgedAgentsByPaneKey : null
+  )
   const { ptyIdsByTabId, terminalLayoutsByTabId, tabsByWorktree } = useAppStore(
     useShallow((state) => selectPaletteStatusInputs(state, paletteStatusInputsActive))
   )
...
-    [paletteStatusInputsActive, tabsByWorktree, unifiedTabsByWorktree]
+    [
+      paletteStatusInputsActive,
+      tabsByWorktree,
+      unifiedTabsByWorktree,
+      acknowledgedAgentsByPaneKeyInput
+    ]

stateStartedAt,
acknowledgedAt
}: AgentTurnAcknowledgement): boolean {
return (acknowledgedAt ?? 0) >= stateStartedAt

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '1,100p' src/shared/agent-turn-acknowledgement.ts
sed -n '105,132p' mobile/src/worktree/agent-row-display.test.ts
sed -n '1,55p' src/renderer/src/components/sidebar/worktree-card-agent-ack-inputs.ts
rg -n 'stateStartedAt: 0|stateStartedAt \?\? 0|stateStartedAt \|\| 0' src/renderer/src src/shared mobile/src/worktree | head -75

Repository: stablyai/orca

Length of output: 5209


🏁 Script executed:

printf '%s\n' '--- symbol references ---'
rg -n 'agentVerdictDisplayMark|computeAgentAcknowledgementTargets|isAgentTurnAcknowledged|agentAttentionStartedAt|acknowledgedAgentsByPaneKey|stateStartedAt' src/renderer/src mobile/src/worktree src/shared
printf '%s\n' '--- source file inventory ---'
git ls-files 'src/renderer/src/*' 'mobile/src/worktree/*' 'src/shared/*' | rg 'agent|status|acknowledg|useDashboard|store/types' | head -100

Repository: stablyai/orca

Length of output: 41838


🏁 Script executed:

printf '%s\n' '--- shared verdict and acknowledgement ---'
sed -n '1,130p' src/shared/agent-main-agent-verdict.ts
sed -n '1,90p' src/shared/agent-turn-acknowledgement.ts
printf '%s\n' '--- acknowledgement target selector ---'
sed -n '1,115p' src/renderer/src/attention/agent-attention-acknowledgement.ts
printf '%s\n' '--- mobile row implementation and fixture ---'
sed -n '1,205p' mobile/src/worktree/agent-row-display.ts
sed -n '1,145p' mobile/src/worktree/agent-row-display.test.ts
printf '%s\n' '--- renderer row mark binding and acknowledgement projection ---'
rg -n 'agentVerdictDisplayMark|selectAcknowledgedAgentTimes|acknowledgedAt:' src/renderer/src/components/sidebar src/renderer/src/components/dashboard src/renderer/src/attention
printf '%s\n' '--- status entry builder and contract ---'
sed -n '80,235p' src/renderer/src/store/slices/agent-status-live-entry-builder.ts
sed -n '1,145p' src/renderer/src/store/slices/agent-status-contract.ts
printf '%s\n' '--- state start time zero/default handling ---'
rg -n -C 3 'stateStartedAt.*(Date\.now|now|0)|(?:Date\.now|now|0).*stateStartedAt|stateStartedAt\s*\?\?' src/renderer/src/store src/renderer/src/components/terminal-pane src/renderer/src/runtime mobile/src/worktree

Repository: stablyai/orca

Length of output: 45655


🏁 Script executed:

printf '%s\n' '--- status builder ---'
sed -n '90,225p' src/renderer/src/store/slices/agent-status-live-entry-builder.ts
printf '%s\n' '--- status contract ---'
sed -n '1,125p' src/renderer/src/store/slices/agent-status-contract.ts
printf '%s\n' '--- acknowledgement projection ---'
cat -n src/renderer/src/components/sidebar/worktree-card-agent-ack-inputs.ts
rg -n -C 5 'selectAcknowledgedAgentTimes|agentVerdictDisplayMark' src/renderer/src/components/sidebar src/renderer/src/components/dashboard
printf '%s\n' '--- acknowledgement state hydration/persistence ---'
rg -n -C 3 'acknowledgedAgentsByPaneKey|read.*Persisted|hydrate.*acknowledged|acknowledged.*hydrate' src/renderer/src/store src/renderer/src/app-shell
printf '%s\n' '--- runtime zero-valued production assignments ---'
rg -n 'stateStartedAt\s*:\s*(0|[^,;]*\?\?\s*0)|stateStartedAt\s*=\s*(0|[^;]*\?\?\s*0)' src/renderer/src --glob '!**/*.test.*' --glob '!**/*.spec.*' mobile/src --glob '!**/*.test.*' --glob '!**/*.spec.*'

Repository: stablyai/orca

Length of output: 41735


🏁 Script executed:

printf '%s\n' '--- production stateStartedAt references ---'
rg -n 'stateStartedAt' src/renderer/src --glob '!**/*.test.*' --glob '!**/*.spec.*' | head -100
printf '%s\n' '--- timing producers and agent status parser ---'
rg -n -C 2 'AgentStatusTiming|timing:.*stateStartedAt|stateStartedAt:' src/renderer/src src/main src/shared --glob '!**/*.test.*' --glob '!**/*.spec.*' | head -180
rg -n -C 3 'stateStartedAt|parseAgentStatus' src/shared/agent-status-types.ts
printf '%s\n' '--- acknowledgement display bindings ---'
rg -n 'selectAcknowledgedAgentTimes|acknowledgedAt|AcknowledgedAgentRow|agentVerdictDisplayMark' src/renderer/src/components/sidebar src/renderer/src/components/dashboard src/renderer/src/lib/agent-entry-acknowledgement.ts --glob '!**/*.test.*'
sed -n '1,140p' src/renderer/src/lib/agent-entry-acknowledgement.ts
sed -n '130,185p' src/renderer/src/components/dashboard/DashboardAgentRow.tsx

Repository: stablyai/orca

Length of output: 42297


🏁 Script executed:

printf '%s\n' '--- status event timestamp ingress ---'
sed -n '210,255p' src/renderer/src/hooks/ipc-events/agent-status-event-applicator.ts
sed -n '270,305p' src/shared/agent-status-types.ts
printf '%s\n' '--- card acknowledgement join ---'
sed -n '145,180p' src/renderer/src/components/sidebar/useWorktreeAgentRows.ts
sed -n '155,180p' src/renderer/src/components/sidebar/worktree-agent-activity-summary.ts
printf '%s\n' '--- acknowledgement storage contract ---'
sed -n '125,155p' src/renderer/src/store/slices/ui/ui-slice-contract-core.ts
sed -n '130,190p' src/renderer/src/store/slices/ui/ui-slice-hydration-values.ts
sed -n '1,75p' src/renderer/src/store/slices/ui/ui-slice-agent-notification-acknowledgement.ts
printf '%s\n' '--- status builder timestamp selection ---'
sed -n '100,120p' src/renderer/src/store/slices/agent-status-live-entry-builder.ts

Repository: stablyai/orca

Length of output: 11543


🏁 Script executed:

printf '%s\n' '--- production hook/status clock producers ---'
rg -n -C 2 'stateStartedAt' src/main/agent-hooks src/shared/agent-hook-listener --glob '!**/*.test.*' --glob '!**/*.spec.*' | head -180
printf '%s\n' '--- explicit source timestamp contracts ---'
rg -n -C 2 'stateStartedAt|statusStartedAt' src/main/agent-hooks src/shared/agent-hook-listener/main-agent-turn-state.ts src/shared/structured-agent-session-status-started-at.ts --glob '!**/*.test.*' --glob '!**/*.spec.*' | head -140

Repository: stablyai/orca

Length of output: 29228


🏁 Script executed:

printf '%s\n' '--- structured status timestamp contracts ---'
rg -n -C 3 'statusStartedAt|AgentSessionStatusSummary' src/shared src/main/agent-hooks --glob '!**/*.test.*' --glob '!**/*.spec.*' | head -170
sed -n '88,108p' src/main/agent-hooks/server/server-ingest-structured.ts
printf '%s\n' '--- acknowledgement hydration sanitizer ---'
rg -n -A 28 -B 3 'sanitizeAcknowledgedAgentsByPaneKey' src/renderer/src/store/slices/ui/ui-slice-hydration-values.ts

Repository: stablyai/orca

Length of output: 14590


🏁 Script executed:

printf '%s\n' '--- structured timestamp producer ---'
sed -n '1,105p' src/shared/structured-agent-session-status-started-at.ts
sed -n '226,291p' src/shared/structured-agent-session-projection.ts
printf '%s\n' '--- acknowledgement value sanitizer ---'
rg -n -C 8 'sanitizeAcknowledgedAgentsByPaneKey' src/renderer/src/store/slices/ui/ui-slice-hydration-sanitizers.ts

Repository: stablyai/orca

Length of output: 8237


🏁 Script executed:

printf '%s\n' '--- turn/journal timestamp contracts ---'
rg -n -C 4 'readAgentJournalTurn|requestedAt|settledAt|observedAt|submittedAt|Number\.isFinite|> 0' src/shared/agent-session-turn-record.ts src/shared/agent-session-journal-types.ts src/shared/agent-session-journal-producer.ts src/shared/agent-session-latest-request.ts

Repository: stablyai/orca

Length of output: 6792


🏁 Script executed:

printf '%s\n' '--- structured latest request clock ---'
rg -n -C 5 'settledAt|requestedAt|completedAt|Date\.now|Number\.isFinite|> 0' src/shared/structured-agent-session-latest-request.ts
printf '%s\n' '--- structured ingestion binding ---'
sed -n '35,110p' src/main/agent-hooks/server/server-ingest-structured.ts

Repository: stablyai/orca

Length of output: 5778


🏁 Script executed:

rg -n -C 6 'continueMainAgentStatus' src/shared/agent-hook-listener src/main/agent-hooks --glob '!**/*.test.*' --glob '!**/*.spec.*'

Repository: stablyai/orca

Length of output: 12179


🏁 Script executed:

rg -n -A 34 -B 5 'function continueMainAgentStatus|const continueMainAgentStatus' src/shared/agent-lead-status-fold.ts

Repository: stablyai/orca

Length of output: 1050


🏁 Script executed:

printf '%s\n' '--- production display-mark bindings ---'
rg -n -C 4 'agentVerdictDisplayMark|isAgentTurnAcknowledged' src/renderer/src/lib src/renderer/src/components/sidebar/worktree-card-agent-summary.ts src/renderer/src/components/sidebar/worktree-agent-activity-summary.ts
printf '%s\n' '--- acknowledgement target call sites ---'
rg -n -C 2 'computeAgentAcknowledgementTargets' src/renderer/src

Repository: stablyai/orca

Length of output: 15722


🏁 Script executed:

sed -n '1,105p' src/shared/structured-agent-session-agent-status.ts
printf '%s\n' '--- structured summary outcome field ---'
sed -n '210,263p' src/shared/agent-session-wire.ts

Repository: stablyai/orca

Length of output: 5653


Treat zero-sentinel acknowledgements as unseen.

When the effective attention clock is 0 and no acknowledgement exists, isAgentTurnAcknowledged returns true. computeAgentAcknowledgementTargets then omits the subject. Live structured status can preserve a zero clock. The sidebar selector also maps a missing acknowledgement to 0, so checking only for undefined would still suppress the Failed mark. The mobile parity test avoids this case by setting the desktop clock to 1.

Suggested fix
-  return (acknowledgedAt ?? 0) >= stateStartedAt
+  return acknowledgedAt !== undefined && acknowledgedAt > 0 && acknowledgedAt >= stateStartedAt
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (acknowledgedAt ?? 0) >= stateStartedAt
return acknowledgedAt !== undefined && acknowledgedAt > 0 && acknowledgedAt >= stateStartedAt

@brennanb2025
brennanb2025 marked this pull request as draft October 5, 2026 19:07
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Back to draft, to be closed: this is the wrong model. A chat interrupted by a crash, quit or restart is still interrupted after you open it, so its status shouldn't clear when you view it. It should read Interrupted until the chat's state actually changes (a new turn starts, or it moves to another state). A replacement PR will show an Interrupted status derived from the chat's latest turn, with no "seen" record. This PR will be closed when that one is up.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closed in favour of #25670: an interrupted chat reads Interrupted until its state actually changes. Viewing it changes nothing, and there is no seen record or timer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant