fix(orchestrator): Keep in-flight and stopped tool rows in the work log - #4762
fix(orchestrator): Keep in-flight and stopped tool rows in the work log#4762mwolson wants to merge 223 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Keeping these rows in the work log exposed that the row component had no treatment for them, so they borrowed the wrong one. A stopped tool rendered a green check with a "Completed" tooltip once the turn settled, because the settled-turn fallback treats any neutral row as success. A running tool rendered the "Empty" dash, which is meant for rows with no content. Both now render their own state: a spinner tooltipped "Running" while the turn is live, and a square tooltipped "Stopped" for an interrupted tool. An `inProgress` row that outlives its turn is also shown as stopped rather than completed, since the turn ended without the tool ever reporting a result. Reported by Macroscope on pingdotgg#4762, which caught the stopped-as-completed half.
Keeping these rows in the work log exposed that the row component had no treatment for them, so they borrowed the wrong one. A stopped tool rendered a green check with a "Completed" tooltip once the turn settled, because the settled-turn fallback treats any neutral row as success. A running tool rendered the "Empty" dash, which is meant for rows with no content. Both now render their own state: a spinner tooltipped "Running" while the turn is live, and a square tooltipped "Stopped" for an interrupted tool. An `inProgress` row that outlives its turn is also shown as stopped rather than completed, since the turn ended without the tool ever reporting a result. Reported by Macroscope on pingdotgg#4762, which caught the stopped-as-completed half.
caf6f38 to
d30b4a7
Compare
Codex review of pingdotgg#4762 caught that inferring "stopped" from a settled turn is wrong. `activeTurnInProgress` is thread-wide, and background work legitimately outlives the root run: `RunExecutionService` explicitly keeps ingesting a late background command completion after root terminal, in the order running -> root-finalized -> completed. The inference would have labelled that live work "Stopped" and then jumped it to "Completed". Both indicators now read the item's own lifecycle and nothing else. A genuine interrupt already terminalizes the item at the projection layer, which is what made the inference look unnecessary rather than harmful. Also from that review: - The work group's filter is now `workEntryShouldRenderInWorkLog`, a production helper, so the predicate tests exercise the shipped policy instead of a copy of the expression that could drift from it. - The lifecycle joins the row's accessible name ("Ran command …, running"), since the indicator sits inside the row button and did not contribute to it. The spinner is decorative rather than a nested role="status" announcing a generic "Loading". - Component tests cover running during an active turn, running after settle, stopped in both states, and a signal-less row staying hidden. Tooltip text is absent from static markup, so they assert on the icon and accessible name, as the existing failed-indicator test does.
Keeping these rows in the work log exposed that the row component had no treatment for them, so they borrowed the wrong one. A stopped tool rendered a green check with a "Completed" tooltip once the turn settled, because the settled-turn fallback treats any neutral row as success. A running tool rendered the "Empty" dash, which is meant for rows with no content. Both now render their own state: a spinner tooltipped "Running" while the turn is live, and a square tooltipped "Stopped" for an interrupted tool. An `inProgress` row that outlives its turn is also shown as stopped rather than completed, since the turn ended without the tool ever reporting a result. Reported by Macroscope on pingdotgg#4762, which caught the stopped-as-completed half.
Codex review of pingdotgg#4762 caught that inferring "stopped" from a settled turn is wrong. `activeTurnInProgress` is thread-wide, and background work legitimately outlives the root run: `RunExecutionService` explicitly keeps ingesting a late background command completion after root terminal, in the order running -> root-finalized -> completed. The inference would have labelled that live work "Stopped" and then jumped it to "Completed". Both indicators now read the item's own lifecycle and nothing else. A genuine interrupt already terminalizes the item at the projection layer, which is what made the inference look unnecessary rather than harmful. Also from that review: - The work group's filter is now `workEntryShouldRenderInWorkLog`, a production helper, so the predicate tests exercise the shipped policy instead of a copy of the expression that could drift from it. - The lifecycle joins the row's accessible name ("Ran command …, running"), since the indicator sits inside the row button and did not contribute to it. The spinner is decorative rather than a nested role="status" announcing a generic "Loading". - Component tests cover running during an active turn, running after settle, stopped in both states, and a signal-less row staying hidden. Tooltip text is absent from static markup, so they assert on the icon and accessible name, as the existing failed-indicator test does.
…eachable Codex round 2 on pingdotgg#4762. Once `workEntryShouldRenderInWorkLog` admits neutral rows only when they carry a reported lifecycle, the row's neutral handling can never fire: `showNeutralIndicator` and the settled-turn success fallback both require the opposite. So `turnSettled`, `hasReportedLifecycle`, `showNeutralIndicator`, the "Empty" dash branch, and the compound success expression were all dead. Success is now just `workEntryIndicatesToolSuccess`, and the row no longer reads thread-wide turn state at all. Also from that round: - Tests pin "no stopped indicator" properly. A stopped row could have regressed to the dash while every assertion still passed; there is now a negative `lucide-minus` check, and the running case asserts the spinner carries neither `role="status"` nor `aria-label="Loading"`, so the accessibility fix cannot silently revert. - The comment claiming both states get their own indicator is gone, as is the overstatement that a "Run interrupted" divider always sits beneath a stopped row: `cancelled` maps to stopped too and has no such row.
…eachable Codex round 2 on pingdotgg#4762. Once `workEntryShouldRenderInWorkLog` admits neutral rows only when they carry a reported lifecycle, the row's neutral handling can never fire: `showNeutralIndicator` and the settled-turn success fallback both require the opposite. So `turnSettled`, `hasReportedLifecycle`, `showNeutralIndicator`, the "Empty" dash branch, and the compound success expression were all dead. Success is now just `workEntryIndicatesToolSuccess`, and the row no longer reads thread-wide turn state at all. The running test also asserts the spinner carries neither `role="status"` nor `aria-label="Loading"`, so the accessibility fix cannot silently revert. The comment claiming both states get their own indicator is gone, as is the overstatement that a "Run interrupted" divider always sits beneath a stopped row: `cancelled` maps to stopped too and has no such row.
5a7eae3 to
257f810
Compare
…eachable Codex round 2 on pingdotgg#4762. Once `workEntryShouldRenderInWorkLog` admits neutral rows only when they carry a reported lifecycle, the row's neutral handling can never fire: `showNeutralIndicator` and the settled-turn success fallback both require the opposite. So `turnSettled`, `hasReportedLifecycle`, `showNeutralIndicator`, the "Empty" dash branch, and the compound success expression were all dead. Success is now just `workEntryIndicatesToolSuccess`, and the row no longer reads thread-wide turn state at all. The running test also asserts the spinner carries neither `role="status"` nor `aria-label="Loading"`, so the accessibility fix cannot silently revert. The comment claiming both states get their own indicator is gone, as is the overstatement that a "Run interrupted" divider always sits beneath a stopped row: `cancelled` maps to stopped too and has no such row.
257f810 to
365949a
Compare
ApprovabilityVerdict: Approved 4b122f7 This is a self-contained UI bug fix that keeps running and stopped tool entries visible in the work log instead of incorrectly hiding them. The changes add proper filtering logic with extensive test coverage, limited to display behavior with no security or infrastructure impact. You can customize Macroscope's approvability policy. Learn more. |
Macroscope on pingdotgg#4762. `projectedWorkEntryStatus` collapses `pending`, `running`, and `waiting` into `inProgress`, so keying the spinner off `toolLifecycleStatus` made a queued tool and an approval request blocked on the user both claim to be running, with a "running" tooltip and accessible suffix to match. That was a regression from this PR: before it, those rows reached the indicator chain with no branch that applied and rendered nothing. `workEntryIsExecuting` reads the projected item's real status instead, so only a genuinely running tool spins. `pending` and `waiting` rows still render, since showing in-flight work is the point of the PR, but they claim nothing. The test helper now derives the projected status from the collapsed lifecycle value, so a fixture cannot assert on a pairing the projection never emits; the previous version let a `stopped` row carry a `running` item.
Dismissing prior approval to re-evaluate 7d5e55c
…eachable Codex round 2 on pingdotgg#4762. Once `workEntryShouldRenderInWorkLog` admits neutral rows only when they carry a reported lifecycle, the row's neutral handling can never fire: `showNeutralIndicator` and the settled-turn success fallback both require the opposite. So `turnSettled`, `hasReportedLifecycle`, `showNeutralIndicator`, the "Empty" dash branch, and the compound success expression were all dead. Success is now just `workEntryIndicatesToolSuccess`, and the row no longer reads thread-wide turn state at all. The running test also asserts the spinner carries neither `role="status"` nor `aria-label="Loading"`, so the accessibility fix cannot silently revert. The comment claiming both states get their own indicator is gone, as is the overstatement that a "Run interrupted" divider always sits beneath a stopped row: `cancelled` maps to stopped too and has no such row.
Macroscope on pingdotgg#4762. `projectedWorkEntryStatus` collapses `pending`, `running`, and `waiting` into `inProgress`, so keying the spinner off `toolLifecycleStatus` made a queued tool and an approval request blocked on the user both claim to be running, with a "running" tooltip and accessible suffix to match. That was a regression from this PR: before it, those rows reached the indicator chain with no branch that applied and rendered nothing. `workEntryIsExecuting` reads the projected item's real status instead, so only a genuinely running tool spins. `pending` and `waiting` rows still render, since showing in-flight work is the point of the PR, but they claim nothing. The test helper now derives the projected status from the collapsed lifecycle value, so a fixture cannot assert on a pairing the projection never emits; the previous version let a `stopped` row carry a `running` item.
Hoist inline Schema compiles to module scope, drop unused imports/vars, stabilize react-markdown component identities via a module-scope factory, remove useless spreads, and use data-derived keys for release-note bullets. No behavior changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The codex resume test was the only replay scenario without a runtimePolicyOverride, so its checkpoint scope cwd fell back to process.cwd() and baseline capture ran real git over the entire checkout. Locally the capture short-circuits on checkpoint refs left behind by earlier runs; on a fresh CI checkout it is a cold multi-second capture that outlives the scenario wait budget, failing await_thread_idle while the run is still mid-checkpoint. Point the fixture's turn/start frames at the <workspace> placeholder and checkpoint a throwaway git workspace like every other replay test. Scenario waits are also wall-clock-bounded now: the iteration budget counts event-loop turns, which burn at full speed while git/fixture IO is in flight, so exhaustion additionally requires a 60s real-time deadline to pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r's scope (pingdotgg#5406) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
- Keep the git action control disabled when the branch is up to date - Omit open PR menu entries and remove their link-opening behavior - Update logic tests for the new states
…sPinned Main owns migration numbering: 036_ProjectionThreadsPinned landed on main, so the v2 migrations shift from 036-044 to 037-045. Release path runs all of main's migrations first, then the v2 stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline: thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the v2 thread state and projected shells, promotion semantics (pin clears settle/snooze, settle clears pin) matching the v1 decider, and client pin/unpin operations in the v2 dispatch style. - Port the regenerated-title context anchoring (pingdotgg#5365) into ThreadTitleRegenerationService: pin the first user message ahead of the retained tail when the digest is truncated. - Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2 ChatView title bar controls. - Repair merge artifacts: committed conflict markers in BranchToolbar, duplicate capability keys, duplicate CommandPalette import, v1 turn naming in DiffPanel's focus-refresh effect, onSend signature merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested conflict markers in several files. Restore the branch-intended v2 shapes and re-graft main's compatible additions (pending-card opacity comments, theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan: - getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc group, ws handler, auth scope, client atom). - AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime maps orchestration-v2 subagent entities into the panel model; deriveAgentPanelModel's v2Projection leg is now live and the v1 fold never runs. Agents surface wired into ChatView + RightPanelTabs. Other ports and reconciliations: - Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync (same-session resubscribes resume from the in-memory cursor), with the cursor-resume regression test adapted to v2 fixtures. - Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed. - Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port (unknown system subtypes are already ignored). - Plan sidebar removed from the v2 ChatView/ChatComposer per main's plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's surface set. - SettingsPanels rebuilt as main's refactored version plus the branch's composer-context setting; sidebar snooze respects the time format (pingdotgg#4438 follow-through). - v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2 rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client side; the 037 keyset migration is kept — server-side v2 windowing is a follow-up). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView still carried the branch's blunt any-gesture-breaks-follow listeners. Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture: - resolveTimelineIsAtEnd measures the 40px follow re-arm band from real geometry (contentLength/scroll/scrollLength minus the composer inset), keeping the isNearEnd fallback for older state shapes. - Follow now breaks only on gestures that can actually leave the live edge: upward wheel with overflowing content, touch drags that exited the end band, scrollbar drags vs content clicks, and keyboard navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never broke follow and the next stream chunk yanked the view back down. - Listener attach retries across frames so a thread switch cannot mount the list without its opt-out listeners. Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps maintainScrollAtEnd={false} with its own follow scrolls and anchor system; flipping that core is a separate change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449) Complete the pingdotgg#5449 architecture on the v2 timeline, following the LegendList author's direction to lean on the list's native mechanisms instead of app-side scroll layers: - maintainScrollAtEnd is enabled and owned by LegendList, gated off only while the user reads history (liveFollowEnabled), while a sent turn anchors near the top (anchoredEndSpace), or during the two-frame settle of a fold toggle. - maintainVisibleContentPosition compensates size changes natively ({data, size, shouldRestorePosition}); fold toggles anchor compensation to the toggled row via a disclosure anchor key, so the trigger stays under the pointer instead of the viewport chasing the end. - ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on every data change) is gone; the app now only owns streaming adjustments during anchored-end-space mode, mirroring main. - timelineLiveFollowEnabled state mirrors the follow refs so the render-visible gate switches native follow off when a gesture breaks follow and back on when the viewport returns to the end band. Timeline tests updated to assert the native-ownership invariants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds - Move the running elapsed timer into the panel menu slot
…s with v2 Post-rebase reconciliation sweep: - Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells (latestRun/runtime naming, waiting status instead of monitoring), with subagent-thread filtering and main's pinned-reorder helpers re-exported - Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey + thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold, ProjectionStore/Maintenance, client-runtime commands and shell mapping - Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2 contracts (OrchestrationProjectShell, application event payloads) - ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces, liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout - enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to v2 RunExecutionService and replay testkit - Removed v1 zombies resurrected by the rebase (provider service/reaper/ ingestion + v1 layer tests, server.test.ts, integration harness) - routeTree: main's tree + branch's /settings/scheduled-tasks route - Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts, rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040 Main released ProjectionThreadsPinOrderKey (038), ProjectionProjectsDefaultThreadEnvMode (039) and ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts up by three. Registry ids were already 41-49; this renames the files and identifiers to match and updates the ledger expectations and through-id boundaries in the migration tests (released boundary 37 -> 40). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the attachments dir alongside cwd via additionalDirectories and appends '[Attached ... is saved at: path]' lines to the turn text so tools can dereference pasted images (pixels alone are not tool-readable). - 5bb8c03 (settle leaves monitors running): thread.settle now joins archive/delete in the provider-session detach set, so PR monitors, dev servers and subagent fleets stop when the user parks the thread. The settle guard already rejects active runs, and serialized dispatch closes the re-engage race the v1 fix handled with onlyIfSettled. - e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live background subagents) are already covered structurally in v2: results are turn-scoped with explicit zero-turn handshake drops, and idle release is pinned while background work is pending. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge The keep-both merge nested main's plan-surface migration test inside a branch popover test and dropped the threadPanelVisibilityByThreadKey key from the migration results. Restore main's test body and include the branch's (empty) visibility map in the expected persisted shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text Follow-up to the pingdotgg#5757 port: start and steer turns now append the '[Attached ... is saved at: path]' line, so the adapter fixtures assert it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
22bd872 to
a27c1cc
Compare
The round-3 reconciliation took main's ChatHeader wholesale and wired its full prop set, resurrecting the scripts/open-in/git-actions cluster the branch had deliberately relocated into the thread panel. Restore the 79-line slim header (project favicon + name + thread title) and its minimal ChatView call. pingdotgg#5592's header actions stay a documented v2 follow-up, as decided in round 2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add ready, merge, and conflict-resolution actions to the PR row - Share pull request action and handoff logic with the detail panel - Fix thread details scrolling and row alignment
8b32d74 to
b387e85
Compare
a186d64 to
5b1a115
Compare
Summary
Keep explicitly running and stopped tool rows visible in the web work log.
Running rows show a spinner, while stopped rows remain visible without being
misrepresented as successful.
This branch is one focused commit on its approved
t3code/codex-turn-mappingsnapshot.Problem and Fix
inProgressorstoppedlifecycle the same as rows with no lifecycle signal. That hid the only visible record of running and interrupted tool work.Validation
session-logic.test.tsandMessagesTimeline.test.tsx, 49 tests passed.Cursor Bugbot, and all Macroscope checks pass.
newer
t3code/codex-turn-mappingmerge result. This PR changes only webtimeline presentation.
Checklist
Note
Low Risk
UI-only chat timeline presentation with focused helpers and broad test coverage; no auth, data, or API changes.
Overview
Fixes work-log visibility and per-row tool lifecycle UI so running and interrupted tools are not hidden or mislabeled when the thread turn settles.
The work group now keeps neutral tool rows when the provider reported
inProgressorstoppedlifecycle (workEntryShouldRenderInWorkLog), while still dropping signal-less noise. Collapsed groups useselectCollapsedWorkLogEntriesto pin genuinely running and stopped tools (not pending/waiting) alongside the latest ordinary row instead of only showingslice(-1).SimpleWorkEntryRowderives indicators from each entry’s status: spinner only whenstructuredPayload.status === "running", no success check for stopped tools, and accessible names include, running/, stopped/, failed. The thread-wideactiveTurnInProgressprop is removed fromMessagesTimelineandChatViewso background commands are not treated as stopped when the root run completes.Reviewed by Cursor Bugbot for commit b387e85. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Keep in-flight and stopped tool rows visible in the work log
workEntryIsExecuting,workEntryHasReportedLifecycle, andworkEntryShouldRenderInWorkLoghelpers insession-logic.tsto accurately classify tool entry states.selectCollapsedWorkLogEntriesinMessagesTimeline.logic.tsthat pins executing and stopped rows when collapsing a work group, filling remaining slots with the latest ordinary entries.WorkGroupSectionandSimpleWorkEntryRowinMessagesTimeline.tsxto show a spinner for running tools, append lifecycle suffixes (, running,, stopped,, failed) to accessible names, and hide neutral rows that carry no lifecycle signal.activeTurnInProgressprop fromMessagesTimeline; per-row status is now derived from each entry's own lifecycle data.Macroscope summarized b387e85.