Repository navigation
Transcript: one live line that holds the latest step and keeps its clock - #29938
Conversation
…, no blank frame A per-frame Chromium trace of a running turn (Thinking, a call running, done, a thought, the next call, done): one line element and one shimmer band whose animation never restarts, words and the turn's clock on every frame, and the finished step named on the line between calls. Unit tests move to the new line: Ran between calls, the turn's time and tokens beside a call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
…lock
Leo (2026-11-01), on "Ran grep …" over a separate "Thinking 15s · ↓ out ↑ in":
"this should be replacing the thinking area and its shimmer across things",
"like the thinking or whatever it should just keep going", "and shouldn't
flicker".
- turns.ts codexLiveTurn: between calls the one live line names the latest
finished step ("Ran grep …", liftedStep) instead of going back to
"Thinking"; that step leaves its run so it reads once. Failed,
backgrounded, message, sub-session, render and edit calls keep their rows.
- Thinking.tsx: one clock for every step, the turn's from the prompt (it no
longer starts again at each call), and the turn's tokens beside a call too.
A done step reads "Ran …" and offers no ⌃B.
- The line stays one element with one shimmer band from send to end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a Passes: CI passes on CI passes on Written by red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: Shipped in:
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
There was a problem hiding this comment.
4 issues found across 9 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="webviews/test/transcript-live-line-continuous-browser.test.ts">
<violation number="1" location="webviews/test/transcript-live-line-continuous-browser.test.ts:164">
P2: This assertion does not prove that a shimmer band exists; a missing `.cv-shimmer__band` produces the same `-1` ID on every frame and passes. Assert the first band ID is positive (and its animation time is nonnegative) before checking continuity.</violation>
<violation number="2" location="webviews/test/transcript-live-line-continuous-browser.test.ts:176">
P2: The clock assertions allow a frozen clock to pass because they require only a lower bound and monotonicity. Require the trace to advance by at least one displayed second so a missing ticker is caught.</violation>
</file>
<file name="webviews/src/agent-session/acpmux/conversation/turns.ts">
<violation number="1" location="webviews/src/agent-session/acpmux/conversation/turns.ts:998">
P2: A completed latest step disappears when a todo list is present: `liftedStep` removes it, but the plan branch discards the populated `line` and renders a new `activityLine`. Preserve the lifted line's label and completion state in this branch, so the live line still says the completed step.</violation>
<violation number="2" location="webviews/src/agent-session/acpmux/conversation/turns.ts:1049">
P2: Completed `delete` and `move` calls are lifted out of the run even though they are file-change rows that must remain visible. Extend this exclusion to `delete` and `move` so their own rows are retained like other edit calls.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| expect(trace.length).toBeGreaterThan(60); | ||
| // One line element and one shimmer band from the first frame to the last: never remounted. | ||
| expect(new Set(trace.map((row) => row.line))).toEqual(new Set([trace[0]!.line])); | ||
| expect(new Set(trace.map((row) => row.band))).toEqual(new Set([trace[0]!.band])); |
There was a problem hiding this comment.
P2: This assertion does not prove that a shimmer band exists; a missing .cv-shimmer__band produces the same -1 ID on every frame and passes. Assert the first band ID is positive (and its animation time is nonnegative) before checking continuity.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/transcript-live-line-continuous-browser.test.ts, line 164:
<comment>This assertion does not prove that a shimmer band exists; a missing `.cv-shimmer__band` produces the same `-1` ID on every frame and passes. Assert the first band ID is positive (and its animation time is nonnegative) before checking continuity.</comment>
<file context>
@@ -0,0 +1,188 @@
+ expect(trace.length).toBeGreaterThan(60);
+ // One line element and one shimmer band from the first frame to the last: never remounted.
+ expect(new Set(trace.map((row) => row.line))).toEqual(new Set([trace[0]!.line]));
+ expect(new Set(trace.map((row) => row.band))).toEqual(new Set([trace[0]!.band]));
+ expect(trace[0]!.line).toBeGreaterThan(0);
+ // The shimmer never starts again: its animation's time only moves forward.
</file context>
| expect(new Set(trace.map((row) => row.band))).toEqual(new Set([trace[0]!.band])); | |
| expect(trace[0]!.band).toBeGreaterThan(0); | |
| expect(new Set(trace.map((row) => row.band))).toEqual(new Set([trace[0]!.band])); |
| } | ||
| // The turn's clock, never the call's: it never reads under the 15 s the turn has run, nor goes back. | ||
| const seconds = trace.map((row) => Number.parseInt(row.time, 10)); | ||
| expect(Math.min(...seconds)).toBeGreaterThanOrEqual(15); |
There was a problem hiding this comment.
P2: The clock assertions allow a frozen clock to pass because they require only a lower bound and monotonicity. Require the trace to advance by at least one displayed second so a missing ticker is caught.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/transcript-live-line-continuous-browser.test.ts, line 176:
<comment>The clock assertions allow a frozen clock to pass because they require only a lower bound and monotonicity. Require the trace to advance by at least one displayed second so a missing ticker is caught.</comment>
<file context>
@@ -0,0 +1,188 @@
+ }
+ // The turn's clock, never the call's: it never reads under the 15 s the turn has run, nor goes back.
+ const seconds = trace.map((row) => Number.parseInt(row.time, 10));
+ expect(Math.min(...seconds)).toBeGreaterThanOrEqual(15);
+ for (let index = 1; index < seconds.length; index += 1)
+ expect(seconds[index]!).toBeGreaterThanOrEqual(seconds[index - 1]!);
</file context>
| expect(Math.min(...seconds)).toBeGreaterThanOrEqual(15); | |
| expect(Math.min(...seconds)).toBeGreaterThanOrEqual(15); | |
| expect(Math.max(...seconds) - Math.min(...seconds)).toBeGreaterThanOrEqual(1); |
| isRunning(call) || | ||
| call.background !== undefined || | ||
| callFailed(call) || | ||
| isEditTool(call) || |
There was a problem hiding this comment.
P2: Completed delete and move calls are lifted out of the run even though they are file-change rows that must remain visible. Extend this exclusion to delete and move so their own rows are retained like other edit calls.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/conversation/turns.ts, line 1049:
<comment>Completed `delete` and `move` calls are lifted out of the run even though they are file-change rows that must remain visible. Extend this exclusion to `delete` and `move` so their own rows are retained like other edit calls.</comment>
<file context>
@@ -1008,6 +1023,49 @@ function codexLiveTurn(
+ isRunning(call) ||
+ call.background !== undefined ||
+ callFailed(call) ||
+ isEditTool(call) ||
+ renderCall(call) ||
+ subsessionCall(call) ||
</file context>
| isEditTool(call) || | |
| (isEditTool(call) || call.kind === "delete" || call.kind === "move") || |
| // just keep going"). The step leaves its run while the line names it, so it reads once. | ||
| const lifted = liftedStep(rows, last); | ||
| if (lifted) { | ||
| rows = lifted.rows; |
There was a problem hiding this comment.
P2: A completed latest step disappears when a todo list is present: liftedStep removes it, but the plan branch discards the populated line and renders a new activityLine. Preserve the lifted line's label and completion state in this branch, so the live line still says the completed step.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/conversation/turns.ts, line 998:
<comment>A completed latest step disappears when a todo list is present: `liftedStep` removes it, but the plan branch discards the populated `line` and renders a new `activityLine`. Preserve the lifted line's label and completion state in this branch, so the live line still says the completed step.</comment>
<file context>
@@ -986,6 +987,20 @@ function codexLiveTurn(
+ // just keep going"). The step leaves its run while the line names it, so it reads once.
+ const lifted = liftedStep(rows, last);
+ if (lifted) {
+ rows = lifted.rows;
+ line.doing = lifted.doing;
+ line.doingShell = lifted.shell;
</file context>
… list keeps its calls Review follow-ups on #29938: a run joined to an earlier one by a short note keeps that one's id, so the line looked it up by `joined` too; with a todo list the activity line is the live line, so no call leaves its run; the lifted run's version is a run state of its own (RUN_STATES 6, LIFTED 5). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="webviews/test/transcript-codex-live-line.test.tsx">
<violation number="1" location="webviews/test/transcript-codex-live-line.test.tsx:423">
P2: This test never verifies the behavior named in its title: the activity line could have the wrong kind or label and still pass. Assert that the final row is an `activityLine` with `text: "Step one"` (and `below: true`) before checking the calls remain in the run.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| new Set(), | ||
| { working: true, liveRuns: true, lineBelow: true, sessionStatus: true }, | ||
| ); | ||
| expect(callIds(shaped)).toEqual(["a", "b"]); |
There was a problem hiding this comment.
P2: This test never verifies the behavior named in its title: the activity line could have the wrong kind or label and still pass. Assert that the final row is an activityLine with text: "Step one" (and below: true) before checking the calls remain in the run.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/transcript-codex-live-line.test.tsx, line 423:
<comment>This test never verifies the behavior named in its title: the activity line could have the wrong kind or label and still pass. Assert that the final row is an `activityLine` with `text: "Step one"` (and `below: true`) before checking the calls remain in the run.</comment>
<file context>
@@ -398,3 +398,27 @@ test("a call that takes no input keeps its row in an ended turn", () => {
+ new Set(),
+ { working: true, liveRuns: true, lineBelow: true, sessionStatus: true },
+ );
+ expect(callIds(shaped)).toEqual(["a", "b"]);
+});
</file context>
| expect(callIds(shaped)).toEqual(["a", "b"]); | |
| expect(shaped.at(-1)).toMatchObject({ kind: "activityLine", text: "Step one", below: true }); | |
| expect(callIds(shaped)).toEqual(["a", "b"]); |
…on its own RUN_STATES 6 broke minimap-card-metadata's pinned edit-card version (review BLOCK at 58cefe8). The lifted run keeps version*2+1 as at 427e775. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="webviews/src/agent-session/acpmux/conversation/turns.ts">
<violation number="1" location="webviews/src/agent-session/acpmux/conversation/turns.ts:1065">
P2: This encoding can reuse a prior version on the live-to-ended transition, so React may skip the completed run when its activity version changes from 1 to 2 in the same snapshot. Use a distinct state offset for lifted runs.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| const doing = shell ? (call.command ? shortCommand(call.command) : "") : runningLabel(call, item!.text); | ||
| const kept = items.filter((_, position) => position !== index); | ||
| const next = rows.slice(); | ||
| if (kept.length) next[at] = { ...run, items: kept, version: run.version * LIFTED_STATES + 1 }; |
There was a problem hiding this comment.
P2: This encoding can reuse a prior version on the live-to-ended transition, so React may skip the completed run when its activity version changes from 1 to 2 in the same snapshot. Use a distinct state offset for lifted runs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/conversation/turns.ts, line 1065:
<comment>This encoding can reuse a prior version on the live-to-ended transition, so React may skip the completed run when its activity version changes from 1 to 2 in the same snapshot. Use a distinct state offset for lifted runs.</comment>
<file context>
@@ -1062,13 +1062,13 @@ function liftedStep(
const kept = items.filter((_, position) => position !== index);
const next = rows.slice();
- if (kept.length) next[at] = { ...run, items: kept, version: run.version * RUN_STATES + LIFTED };
+ if (kept.length) next[at] = { ...run, items: kept, version: run.version * LIFTED_STATES + 1 };
else next.splice(at, 1);
return { rows: next, doing, shell };
</file context>
| if (kept.length) next[at] = { ...run, items: kept, version: run.version * LIFTED_STATES + 1 }; | |
| if (kept.length) next[at] = { ...run, items: kept, version: run.version * RUN_STATES + RUN_STATES }; |
|
Review (subagent claude-opus-5-5 medium): SHIP at b1294f6 Three rounds:
At b1294f6, six focused files pass (67 tests): Follow-ups (non-blocking, in the PR body):
|
|
Found 5 test failures on Blacksmith runners: Failures
|
|
Base reds, not this PR's own. All three are reproduced on the base tip.
|
…inished step The webview verdict on #29938 named three: the peer chat's line still names its call once done, the question record's read becomes a row when the next call runs, and the compositor shimmer test times its line from the turn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
…may be under the reply Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
…e live line now Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
There was a problem hiding this comment.
2 issues found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="webviews/test/agent-pane-peer-chat.test.ts">
<violation number="1" location="webviews/test/agent-pane-peer-chat.test.ts:394">
P2: This no longer waits for any post-completion state: `.cv-worked__time` already exists while `Run make` is running, so the assertion can read the pre-completion DOM and miss a continuity regression. Wait for an observable completed state (for example, make the fixture render distinct `Running`/`Ran` labels and await `Ran`, or send a next call and assert the completed call becomes its row).</violation>
</file>
<file name="webviews/test/agent-pane-stream-jitter.test.ts">
<violation number="1" location="webviews/test/agent-pane-stream-jitter.test.ts:563">
P2: This allowance removes the only assertion that the tool-ending fixture actually contributes a tool row. A regression that omits or fails to fold that row can still satisfy the remaining geometry equality; assert the live/finished row content separately while allowing zero height change.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| // The call ends: the live line holds it until the next step and times the turn from the | ||
| // prompt ("Run make 5s"), moved onto this computer's clock, as it did while the call ran. | ||
| send(3, "tool_call_update", { toolCallId: "t1", status: "completed" }); | ||
| await row.first().waitFor({ state: "detached" }); |
There was a problem hiding this comment.
P2: This no longer waits for any post-completion state: .cv-worked__time already exists while Run make is running, so the assertion can read the pre-completion DOM and miss a continuity regression. Wait for an observable completed state (for example, make the fixture render distinct Running/Ran labels and await Ran, or send a next call and assert the completed call becomes its row).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/agent-pane-peer-chat.test.ts, line 394:
<comment>This no longer waits for any post-completion state: `.cv-worked__time` already exists while `Run make` is running, so the assertion can read the pre-completion DOM and miss a continuity regression. Wait for an observable completed state (for example, make the fixture render distinct `Running`/`Ran` labels and await `Ran`, or send a next call and assert the completed call becomes its row).</comment>
<file context>
@@ -388,10 +388,9 @@ await requireBrowserLane("agent-pane-peer-chat.test.ts", async () => {
+ // prompt ("Run make 5s"), moved onto this computer's clock, as it did while the call ran.
send(3, "tool_call_update", { toolCallId: "t1", status: "completed" });
- await row.first().waitFor({ state: "detached" });
const time = page.locator(".cv-worked.is-below .cv-worked__time");
await time.waitFor();
const label = (await time.textContent()) ?? "";
</file context>
| // drops by what went from under it, once, in the settle's frame, and rests there. | ||
| // on purpose): whatever ran after the reply leaves from under it, so the reply drops by | ||
| // what went from under it, once, in the settle's frame, and rests there. The live line | ||
| // holds the finished call until the turn ends (Leo, 2026-11-01: "it should just keep |
There was a problem hiding this comment.
P2: This allowance removes the only assertion that the tool-ending fixture actually contributes a tool row. A regression that omits or fails to fold that row can still satisfy the remaining geometry equality; assert the live/finished row content separately while allowing zero height change.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/agent-pane-stream-jitter.test.ts, line 563:
<comment>This allowance removes the only assertion that the tool-ending fixture actually contributes a tool row. A regression that omits or fails to fold that row can still satisfy the remaining geometry equality; assert the live/finished row content separately while allowing zero height change.</comment>
<file context>
@@ -558,10 +558,11 @@ await requireBrowserLane("agent-pane-stream-jitter.test.ts", async () => {
- // drops by what went from under it, once, in the settle's frame, and rests there.
+ // on purpose): whatever ran after the reply leaves from under it, so the reply drops by
+ // what went from under it, once, in the settle's frame, and rests there. The live line
+ // holds the finished call until the turn ends (Leo, 2026-11-01: "it should just keep
+ // going"), and the footer takes the line's place at its height, so that may be nothing.
const left = before!.under! - after!.under!;
</file context>
|
Review (subagent claude-opus-5-5 medium): SHIP at bd1ce43 Source unchanged since the SHIP at b1294f6; the delta b1294f6..bd1ce43 touches only five webview tests, each adapted to the intended behaviour (a finished last call stays on the live line instead of a row). No assertion weaker than the change requires. Follow-ups:
Verdict-named tests at bd1ce43 (cmux-ci run 41278616620): subagent-groups-open green; stream-jitter's only fail, "the reply only moves up as a tool call, the working line and the queue card come and go", fails the same way with base source (run of job 124762770402, reverse-patched), so it is a base red. |
…all a row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-20208ae4fb1bb2a05468
|
Review (subagent claude-opus-5-5 medium): SHIP at fbfd657 Delta since the SHIP at bd1ce43 (and cmux-bot's base merge ff392ce): one gallery fixture. Gallery health, Follow-ups:
|
…t step draws on the live line #29938 draws a running turn's latest step on its own live line, outside .cv-tool-run, so the fixture's last command left the run and the second note had no row below it. One more call keeps it between two run rows. transcript-interim-prose-gap.test.ts: red after the base merge (3 of 4 note gaps found), green with this (4 of 4 at 12px, light and dark). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-92fa78a91e29868c5eaa
* test(transcript): measure the room around a note between tool rows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-92fa78a91e29868c5eaa * fix(transcript): give a note between tool rows the plain gap above and below Leo, 2026-11-01: "the thoughts in between tool calls probably want a bit more padding" A short interim note folded into a run of calls (turns.ts mergeShortNotes) had 3px to the tool rows around it. It now has --cv-note-gap, the 12px plain status gap a longer reply between two runs has; model.ts estimates the note with the same numbers. Gallery variants compare 3, 8, 12 and 16px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-92fa78a91e29868c5eaa * test(transcript): open only the run in the note-gap gallery entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-92fa78a91e29868c5eaa * test(transcript): keep the second note between run rows now the latest step draws on the live line #29938 draws a running turn's latest step on its own live line, outside .cv-tool-run, so the fixture's last command left the run and the second note had no row below it. One more call keeps it between two run rows. transcript-interim-prose-gap.test.ts: red after the base merge (3 of 4 note gaps found), green with this (4 of 4 at 12px, light and dark). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-92fa78a91e29868c5eaa --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
…29938's live line (#33614) * base red: the spacing table finds the live line across its lead span (#33241) #33241 draws the live line's verb in its own span ("Sending" | " message to …"), so no single text node holds "Sending message to" and step-to-live / live-to-step measured nothing. The test now finds the live line by its row's text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-2f629b93d5a20c58e6f8 * base red: the codex live line test reads the live line's column class (#33157) #33157 gives the live line `cv-worked--live` (its words on the status rows' text column), so its quiet box renders "cv-worked cv-worked--live is-quiet". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-2f629b93d5a20c58e6f8 * base red: the working-counts tray lists its subagents newest first (#33364) #33364 orders the open running tray newest first: Tester (spawned after Reviewer) now leads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-2f629b93d5a20c58e6f8 * base red: the live permission tests move the turn past the approved call (#33408, #33439) In a running turn with nothing after it, the latest finished call is lifted onto the live line (turns.ts liftedStep, #29938), so its row and its Approved mark are not drawn. #33439 pushed only the call's completion; the tests now push the reply's first words after it, the state the passing resync test already reaches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-2f629b93d5a20c58e6f8 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, 2026-11-01, on a screenshot of "Ran grep -n -E 'environ|GH_QUOTA_[A-Z_]+|low_water' …" with "Thinking 15s · ↓ 284 out ↑ 342.6K in" on its own line under it:
What changed
A running turn now has one live line at the bottom, and that line is the current activity:
conversation/turns.tscodexLiveTurn/liftedStep: between calls the line names the latest finished step instead of going back to "Thinking". That step leaves its run so it reads once. Failed, backgrounded, message, sub-session, render and edit calls keep their own rows and do not go on the line.conversation/Thinking.tsx: one clock, the turn's time from the prompt, for every step, so the time no longer starts again at each call. The turn's tokens now show beside a call too. A finished step reads "Ran …" and offers no ⌃B.thinking-live) and the same element tree for every state. Only its text changes, so React never remounts it and the CSS shimmer never starts again.Spec: leo-workspace
design/chat-transcript.md, "The live line" (added alongside this PR).Proof: per-frame trace
webviews/test/transcript-live-line-continuous-browser.test.ts(real Chromium,cmuxAfterPaintreads every painted frame). A turn steps through Thinking → grep running → grep done → a thought → sed running → sed done, 12 frames each. Across every frame it asserts:The trace it prints is below.
Head (green), every painted frame:
72 frames
Base source (red), first and last frame of each step:
Tests
Base fails and head passes:
bun test test/transcript-codex-live-line.test.tsx test/transcript-running-live-line.test.tsx test/transcript-working-line.test.tsx: 46 pass on head. These tests encoded the 2026-10-20 spec ("Thinking -> Running -> Thinking", a clock per call) and now encode Leo's new one.Observed / Not run
vp fmtandvp lint(with type checks) are clean on the changed files.Follow-ups (from review)
10v+3) can equal the ended open fold's (5v'+3) if one snapshot ends the turn and doubles that run's version. It needs a run state of its own, and the minimap test's pinned versions have to move in the same PR.joined, no lift with a todo list).Changelog
risk:normal
Agent session: agent session
🤖 Generated with Claude Code