Skip to content

fix(orchestrator): Keep lifecycle rows visible in the work log - #4762

Open
mwolson wants to merge 5 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/web-timeline-inflight-tool-rows
Open

fix(orchestrator): Keep lifecycle rows visible in the work log#4762
mwolson wants to merge 5 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/web-timeline-inflight-tool-rows

Conversation

@mwolson

@mwolson mwolson commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Keep important lifecycle outcomes visible in the web timeline:

  • Running and stopped tool rows survive work-log filtering and compact views.
  • A terminal provider error remains visible when a failed turn produces no
    assistant message.

These are provider-agnostic presentation fixes. In each case, the server
projection was correct and the web timeline hid the only useful row.

Problem and Fix

Problem and Why it Happened Fix
Work-log filtering treated explicit inProgress and stopped lifecycle rows like rows with no lifecycle signal. Keep neutral rows when the provider reported an explicit running or stopped lifecycle, while continuing to hide signal-less noise.
Running and stopped rows had no accurate indicator treatment because they previously never reached the row component. Show a spinner only for an executing tool. Keep stopped tools visible without giving them a success indicator.
Compact work logs could drop active or stopped rows when newer ordinary entries consumed the visible-row budget. Pin executing and stopped rows, then fill the remaining budget with the latest ordinary rows.
Settled-turn folding retained a terminal assistant message, but a provider failure can end with only a failed error work item. The error then appeared only in the sidebar. Exclude failed provider-error items from the fold's hidden-entry set. Recovered retry rows remain ordinary foldable work.

Defensive Fixes

Problem and Why it Happened Fix
The CTM test fixture supplied a fallible ServerSettings layer where the provider-session test runtime requires an infallible layer. Convert that test-only layer with Layer.orDie. Production runtime behavior is unchanged.

Validation

  • vp check: passed
  • Full workspace and mobile typechecks: passed
  • session-logic.test.ts, MessagesTimeline.logic.test.ts, and
    MessagesTimeline.test.tsx: 105 tests passed after the terminal-error append
  • Independent GPT-5.6-Sol high review of the terminal-error change: no findings
  • Isolated renderer test with a real Codex turn:
    • A foreground command appeared with a running spinner.
    • Stop interrupted the turn.
    • The command row remained visible without a spinner or success check.
  • A live failed provider turn confirmed that retries and persistence were
    correct while settled-turn folding hid the final capacity error.

Notes for review

A stopped row intentionally has no lifecycle glyph. The component already uses
XIcon for failure, while the red stop marker belongs to the separate interrupt
lifecycle row. Inventing another glyph would turn a visibility fix into a design
change.

The terminal-error exception is narrow: it requires an error work item with a
failed lifecycle. A recovered provider-retry row remains foldable.

The original neutral-row behavior arrived in #3022 ui/messages-timeline. Mobile
does not share the affected web filtering or settled-turn folding paths.


Note

Low Risk
UI-only timeline presentation and tests. No auth, data, or orchestration behavior changes beyond a tiny test-layer .pipe(Layer.orDie) tweak.

Overview
Keeps in-flight and interrupted tool rows visible in the chat work log, and stops inferring their status from the whole turn.

Neutral rows with a reported inProgress or stopped lifecycle now render; only rows with no lifecycle signal stay hidden. Collapsed groups pin executing and stopped tools so a later completed row cannot bury them. Pending/waiting tools stay unpinned and show no glyph (the old minus/"Empty" indicator is gone).

Row indicators now follow the item’s own status: a spinner only when structuredPayload.status === "running", no success check for stopped tools, and aria-label suffixes for running/stopped/failed. activeTurnInProgress is removed so a settled turn cannot mark live background work as complete.

Failed provider-error work entries also stay unfolded when there is no assistant message.

Reviewed by Cursor Bugbot for commit 38109f8. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Keep in-flight and stopped tool rows visible in collapsed work log groups

  • Adds selectCollapsedWorkLogEntries in MessagesTimeline.logic.ts to pin executing and stopped entries while showing only the most recent ordinary entries within MAX_VISIBLE_WORK_LOG_ENTRIES
  • Adds lifecycle helpers workEntryHasReportedLifecycle, workEntryIsExecuting, and workEntryShouldRenderInWorkLog in session-logic.ts to distinguish genuinely running tools from neutral rows with no lifecycle signal
  • Reworks SimpleWorkEntryRow in MessagesTimeline.tsx to show a spinner only for running tools, a checkmark for success, an X for failure, and no icon for pending/waiting; stopped state is conveyed via accessible name suffix
  • Removes the activeTurnInProgress prop from MessagesTimeline and ChatView; lifecycle indicators are now driven solely by entry data
  • deriveTurnFolds keeps terminal provider error entries visible in settled turns instead of folding them away
  • Risk: workEntryShouldRenderInWorkLog now hides neutral work log rows that lack any lifecycle signal — any caller expecting those rows to appear in collapsed groups will no longer see them

Macroscope summarized 38109f8.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d63aaf22-b302-4873-a096-e3875d635abe

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Jul 28, 2026
Comment thread apps/web/src/components/chat/MessagesTimeline.tsx
@mwolson mwolson changed the title fix(web): Keep in-flight and stopped tool rows in the work log fix(orchestrator): Keep in-flight and stopped tool rows in the work log Jul 28, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
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.
@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels Jul 28, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
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.
@mwolson
mwolson force-pushed the fix/web-timeline-inflight-tool-rows branch from caf6f38 to d30b4a7 Compare July 28, 2026 17:55
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
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.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
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.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
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.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
…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.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
…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.
@mwolson
mwolson force-pushed the fix/web-timeline-inflight-tool-rows branch from 5a7eae3 to 257f810 Compare July 28, 2026 18:36
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
…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.
@mwolson
mwolson force-pushed the fix/web-timeline-inflight-tool-rows branch from 257f810 to 365949a Compare July 28, 2026 18:55
@mwolson
mwolson marked this pull request as ready for review July 28, 2026 18:55
Comment thread apps/web/src/components/chat/MessagesTimeline.tsx Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 38109f8

Macroscope's review found this PR approvable — UI bug fix with well-scoped changes to work log display logic. Adds helper functions and smarter collapsing to keep running/stopped tools visible. Includes comprehensive test coverage. No backend or API changes.

You can add or adjust custom eligibility rules. Learn more.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
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.
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jul 28, 2026
@macroscopeapp
macroscopeapp Bot dismissed their stale review July 28, 2026 19:18

Dismissing prior approval to re-evaluate 7d5e55c

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Jul 28, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jul 28, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
…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.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
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.
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 5, 2026
@juliusmarminge
juliusmarminge force-pushed the t3code/codex-turn-mapping branch from 25de21d to 0af2a6e Compare August 7, 2026 12:10
@mwolson
mwolson force-pushed the fix/web-timeline-inflight-tool-rows branch from 4b122f7 to 8b32d74 Compare August 8, 2026 18:41

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8b32d74. Configure here.

Comment thread apps/web/src/components/chat/MessagesTimeline.logic.ts
@juliusmarminge
juliusmarminge force-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1cc Compare August 10, 2026 17:05
@mwolson
mwolson force-pushed the fix/web-timeline-inflight-tool-rows branch from 8b32d74 to b387e85 Compare August 11, 2026 13:25
@juliusmarminge
juliusmarminge force-pushed the t3code/codex-turn-mapping branch 2 times, most recently from a186d64 to 5b1a115 Compare August 12, 2026 23:19
@juliusmarminge
juliusmarminge force-pushed the t3code/codex-turn-mapping branch 4 times, most recently from 519c42a to 4c55679 Compare August 17, 2026 10:28
@mwolson
mwolson force-pushed the fix/web-timeline-inflight-tool-rows branch from b387e85 to ede1305 Compare August 19, 2026 15:37

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

One issue found: the removal of the activeTurnInProgress prop leaves a stale call site in this file's own test, which no longer typechecks. Everything else in the diff (the Spinner running indicator, the , running/, stopped accessible-name suffixes, and the pinned collapsed work-group selection) is consistent with the existing indicator branches in SimpleWorkEntryRow and the shared UI primitives.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/chat/MessagesTimeline.test.tsx
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 19, 2026 16:56

Dismissing prior approval to re-evaluate dd1f712

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
CI Check fails on current CTM because layerTest now surfaces
ServerSettingsError while the ProviderSessionManager test layer requires
never. orDie matches the other test layers.
@mwolson
mwolson force-pushed the fix/web-timeline-inflight-tool-rows branch from dd1f712 to 19d3389 Compare August 19, 2026 18:05
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 19, 2026 18:05

Dismissing prior approval to re-evaluate 19d3389

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 20, 2026 17:42

Dismissing prior approval to re-evaluate 38109f8

@mwolson mwolson changed the title fix(orchestrator): Keep in-flight and stopped tool rows in the work log fix(orchestrator): Keep lifecycle rows visible in the work log Aug 20, 2026
@t3dotgg t3dotgg added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. and removed vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants