Skip to content

Agent chat: a steer is one continuous turn; only Stop or Esc reads as stopped - #22691

Merged
teamleaderleo merged 20 commits into
feat-cmux-nextfrom
leo/steer-continuous-turn
Oct 21, 2026
Merged

teamleaderleo merged 20 commits into
feat-cmux-nextfrom
leo/steer-continuous-turn

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

When Leo sends a message while a turn is running, and the agent can't take it mid-turn, acpmux ends the running turn and runs the message next. It ends the turn with a cancel, or, after acpmux #22808, Claude ends it with end_turn. The transcript drew this as a Stop: "You stopped after 55s" over the work, "Sent now · stopped the turn" under his message, and a fresh "Working for 18s". He never pressed Stop.

The turn really does end and restart under the hood. This PR keeps that data model and only merges how it is drawn. The "Worked for" and "You stopped after" lines below are the turn's fold label.

Change

  • direct.ts: telling a Send now from a real Stop.
    • acpmux sends a queue_order with next: true while a turn is open and the prompt is still queued. That arms interrupting.
    • The next turn_result that is not failed (cancelled or end_turn) marks its summary continued. A failure still shows.
    • The prompt's user_message row is marked continues. That means accepted and drawn inline, with no mark of its own: nothing reads "Steered".
    • A real Stop or Esc sends no queue_order next, so it is unchanged.
    • foldOlder saves, resets and restores interrupting, and patches a held sent-now row after an older page brings its cancel.
    • A Send now that never runs leaves the turn ended, but only on a record that says so: queue_removed (removed or withdrawn), prompt_refused (a local acpmux streams it now that the pane asks for it by name), another prompt's user_message, or the refusal answering this pane's own send. A transport error on that send (a closed socket fails it with native.timed_out) is not a refusal: acpmux still runs the prompt, so the turn stays continued. The summary then drops continued and shows its footer, "You stopped after" and its checkpoint. Between dequeued and the prompt's record, the chat reads idle while the prompt is about to run; the turn stays continued through that gap.
    • Stop clears a pending Send now, and a Send now inside Stop's grace period does not arm one.
  • New turnStart.ts. It holds continuesTurn (a steer, or a prompt that continues) and opensTurn. Everything that finds a turn by its prompt now walks past such a prompt:
    • turns.ts turns, with one Working and Worked clock from the first prompt. It also draws no "You stopped after" for a continued summary, including the moment before the prompt's record arrives.
    • diff.ts turnRows, and App's last edit turn.
    • The checkpoint pair, which spans from the first part's start to the summary that ends the turn (endsTurn).
    • rowMarks.ts, which marks only turn-ending summaries.
    • Find and shell reveal (turnFoldFor), and the hover footer (turnFooterFrom).
    • runningGroups and the minimap's turns.
  • Removed: the stopped steer state and its steer.stopped string. Native steers keep their existing marks; Agent chat: Claude's hover rows under every message (read receipts, Pin, Read aloud) #22773 removes those.

Screenshots

These show the first version, where the sent-now message read "Steered". It now carries no mark.

Before After (first version)
before after

Verification

Each fix lands as a tests-only commit followed by the fix commit. The tests are in webviews/test/transcript-steer-continuous.test.tsx. Each test commit fails on the fleet and its fix passes, running scripts/ci/run-webviews-tests.sh with cmux-ci run. They cover:

  • the inline steer and the clock;
  • a real Stop;
  • the checkpoint span;
  • Find and footer;
  • the live flash;
  • history paging;
  • the edit-then-Send-now checkpoint request;
  • both foldOlder leaks;
  • end_turn;
  • a failed turn;
  • a Send now removed before or after the turn's end, or refused with its record, each live and replayed;
  • a snapshot drawn between dequeued and the Send now's record (still continued, no checkpoint request);
  • a Send now whose request a dropped socket fails (native.timed_out): the turn stays continued;
  • Stop followed by Send now.

The suite's other failures are the same on each test commit and its fix: agent-pane-column-edges, apps-flow, composer-html-paste-edit, hover-row-sweep, stats-page, thread-minimap-feel, tooltip-surface, whats-new-text-selection.

Observed / Not run

  • The red-to-green proof is transcript-steer-continuous.test.tsx (bun + JSDOM, runs on the fleet). agent-pane-queue-steer.test.ts is a browser-lane file: requireBrowserLane skips it unless CI=true or CMUX_BROWSER_TESTS=1, so it never ran on the fleet and its update is checked only by GitHub CI.

  • The later commits were made in a sparse /tmp clone, because the shared repo's git was frozen on cosmic-gray. scripts/web-lint.sh there reported no errors in changed files, also after the base merge. Its other errors were in untouched pages (missing generated strings.json and out-of-clone fixtures), plus checks that need schemas/, Packages/ or the acpmux spec, which the clone lacks. CI's web lint lane is the check.

  • No macOS build or dogfood.

  • Known gaps:

    • A refused Send now stays drawn as continued until the next prompt when the pane never sees its prompt_refused: a remote acpmux, a pane reloaded after the refusal, or a pane other than the sender's. prompt_refused is a log record, not a transcript record, so only the sending pane's own refusal answer or a local live stream ends the turn.
    • A refusal can sit on an older-page boundary: when the attached page starts between the cancel and the prompt_refused, the refusal is read before the turn it ends, so loading the older page draws that turn as continued.
    • Stop is live-only. cancel() clears the pending Send now, but acpmux records no Stop, so replaying Stop followed by Send now draws a continued turn where the live pane drew it stopped. The durable fix is for acpmux to record a cancel_requested event; that is not built here.
  • Localization: one key removed, no new strings.

Agent session: cosmic-gray

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 20, 2026 17:26
A prompt sent now to an agent that cannot steer cancels the running turn and
runs next. The transcript must draw it inline in one turn timed from the first
prompt, without 'You stopped after' or 'stopped the turn'; a real Stop keeps
its line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
… stopped

A prompt sent now to an agent that cannot steer makes acpmux cancel the
running turn and run the prompt next. The pane drew that as a Stop: 'You
stopped after 55s', 'Sent now · stopped the turn', then a fresh 'Working
for'. Now the cancel a prompt sent now caused (queue_order next while the
turn ran, then turn_result cancelled) marks its summary continued, the
prompt reads Steered and draws inline in the turn, and the turn's Working
and Worked clocks run from the first prompt. A real Stop keeps its line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 5df2c79: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr22691 -f ref=leo/steer-continuous-turn -f platform=macos -f backend_mode=local

Pending: CI is running on 5df2c79278 (run); the result below was for 7d638f6e68 and no longer applies.

Last result: Unclear: web / react-apps-check (1) failed: no known signature; failed step: Fail on the gate checks.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); it is rewritten when this head's CI completes.

web and size checks: green

CI fast guards failed on 7d638f6e68 (https://github.com/manaflow-ai/cmux/actions/runs/39530939153). It does not block the merge; a red guard merged into main breaks it for every open PR.
The log named no failed step; see the run.

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally.

Tour of 5df2c79278 (run): no media. The tour did not run: only webview code changed (17 path(s)), which the gallery advisory run checks in Linux Chromium on the pull request (its artifact holds any captures); add the media-tour-mac label to tour it on a Mac.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-los7o created for this PR by the cmux coordinator.

@blacksmith-sh

blacksmith-sh Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Found 22 test failures on Blacksmith runners:

Failures

Test View Logs
test/agent-pane-adopted-tool-calls.test.ts/agent-pane-adopted-tool-calls.test View Logs
test/agent-pane-adopted-tool-calls.test.ts/agent-pane-adopted-tool-calls.test View Logs
test/agent-pane-column-edges.test.ts/agent-pane-column-edges.test View Logs
test/agent-pane-column-edges.test.ts/agent-pane-column-edges.test View Logs
test/agent-pane-queue-steer.test.ts/agent-pane-queue-steer.test View Logs
test/agent-pane-queue-steer.test.ts/agent-pane-queue-steer.test View Logs
test/agent-pane-soft-edges.test.ts/agent-pane-soft-edges.test View Logs
test/agent-pane-soft-edges.test.ts/agent-pane-soft-edges.test View Logs
test/agent-pane-stream-jitter.test.ts/agent-pane-stream-jitter.test View Logs
test/agent-pane-stream-jitter.test.ts/agent-pane-stream-jitter.test View Logs

...and 12 more test failures. View all on Blacksmith

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / defaults-snapshot is red on 21 of 24 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

teamleaderleo and others added 3 commits October 20, 2026 18:38
The edited-files checkpoint, Find and shell reveal, the hover footer,
running subagent groups and the minimap still split a turn at a steer; the
cancel a sent-now prompt caused flashes 'You stopped after' before the
steer's record, and comes back when an older page ends at the steer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
Review of #22691: code beyond the transcript still split a turn at each
prompt row. A leaf turnStart.ts holds continuesTurn and opensTurn, and:
- diff.ts turnRows and App's last edit turn run past steers; a turn's
  checkpoint pair spans its first part's start to the end of the part
  that ended it, and a turn has ended only on a summary that is not
  continued (endsTurn).
- Find and a shell's Bash row open the fold the turn draws
  (turns.ts turnFoldFor); the hover footer is the chain's (turnFooterFrom).
- runningGroups and the minimap's turns skip steers.
- A continued summary that is the last turn's last one draws nothing, so
  'You stopped after' does not flash before the steer's record.
- An older page that holds the cancel marks the held sent-now prompt a
  steer after foldOlder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Verification note: scripts/web-lint.sh ran in a sparse clone, because git in the local checkout was frozen by a pack-file repair. It found no errors in the files this PR changes. It still exits non-zero for two reasons, so the push used CMUX_WEB_LINT_SKIP_REASON:

  • type errors in settings, history and mobile-pairing pages and their tests, which this PR doesn't touch;
  • four checks (type-scale, status-glyphs, text-focus, acpmux-protocol:check) that crash because their inputs (Packages/ and the acpmux spec) are missing from the sparse clone.

CI's web lint lane is the real check for this head.

Fleet webviews suite:

  • transcript-steer-continuous fails on the test commit 19804f8 and passes on the head 4a22c0b.
  • 9 files fail on each run. 8 of them are the same on both runs (agent-pane-column-edges, apps-flow, composer-html-paste-edit, hover-row-sweep, stats-page, thread-minimap-feel, tooltip-surface, whats-new-text-selection), so they are not this PR's.
  • On the test commit, the 9th failure is the new test file itself.
  • react-compiler-skips-optimized-deps failed only on the head, with Unexpected while resolving package 'vite' from a probe script under the worker's /tmp. This PR touches no build or Vite config, so it looks like a worker environment problem, but it hasn't been re-run.

teamleaderleo and others added 4 commits October 20, 2026 19:03
…point nor leaks across a history fold

Review of #22691 at 4a22c0b: the cancel a Send now caused counted as a
turn end for the edits card's checkpoint, and foldOlder neither kept nor
reset the pending Send now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
…; foldOlder keeps a pending Send now

- rowMarks.ts marks a summary only when it ends its turn (endsTurn), so a
  Send now's cancel after an edit no longer asks for a pair that does not
  exist yet (readTurnFromRows threw, leaving the card without counts).
- foldOlder saves, resets and restores interrupting with the rest of the
  live state: an older page neither leaks its Send now into the live turn
  (a later Stop read as continued) nor clears the live one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
… end_turn Send now continues too

Coordinator ruling on #22691: nothing new reads "Steered" (#22773 removes the
steer marks). acpmux #22808 ends Claude's Send now with end_turn rather than a
cancel, which must also draw as one turn; a failed turn still shows its failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
…ed mark, whatever ended it

- The prompt row is marked `continues` (accepted, drawn inline) instead of
  steer "landed", so it shows no "Steered" line (coordinator ruling; #22773
  removes the steer marks). continuesTurn reads either.
- acpmux #22808 ends Claude's Send now with end_turn: any turn_result while a
  Send now waits marks the summary continued, except a failure, which shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

owner: 01a15ba3

teamleaderleo and others added 2 commits October 20, 2026 20:05
Review of #22691 at 46f55be: a Send now removed, withdrawn or refused
left the turn's summary continued for good (no footer, no checkpoint),
and Stop followed by Send now before the cancel read as continued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
…ns over a Send now

Review of #22691 at 46f55be:
- queue_removed or prompt_refused for the pending Send now clears it; after
  the turn ended, its summary is no longer continued, so the turn shows its
  footer, 'You stopped after' and its checkpoint.
- Another prompt opening a turn first does the same.
- Refused at dispatch (acpmux does not stream prompt_refused to the pane):
  once the chat is idle and the prompt is neither queued nor running, the
  summary reads ended; the prompt running after all puts it back.
- Stop clears a pending Send now, and a Send now within Stop's grace does not
  arm one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
@cursor

cursor Bot commented Oct 21, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot 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.

The never-run Send now cases expected 55s; the fold times a turn to its
answer (40s in), as the real Stop case does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

From the subsession-visibility lane: no overlap. My open PRs (#22827, #22879, #22895) don't touch direct.ts, turns.ts, turnStart.ts or rowMarks.ts.

Two things to keep:

teamleaderleo and others added 7 commits October 20, 2026 21:02
… keeps one turn

Review of #22691 at d5ace1c: acpmux ends the turn, reads ready and
dequeues the Send now before recording it; a snapshot in that gap ended the
turn ("You stopped after", and the checkpoint asked for the first part only).
A turn error for the Send now must still end the turn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
…ll not run

Review of #22691 at d5ace1c: the snapshot-time guess (idle, not queued)
fired in acpmux's normal gap between dequeued and the Send now's record,
ending the turn and asking for a one-part checkpoint. It is gone. The turn
stays continued until the prompt's own record, or a record that it will
not run: queue_removed, prompt_refused (now asked for by name on a local
acpmux), a turn_error, another prompt's record, or the refusal answering
this pane's own send.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
…inues

The transport error a closed socket gives the Send now's request (native.timed_out) is not
acpmux refusing the prompt: acpmux still runs it after the reattach. Also drops the turn_error
test, whose branch never runs (a turn_error comes only after the prompt's user_message).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
A closed socket fails the Send now's request with native.timed_out, but acpmux still runs the
prompt, so the turn it ended stays continued. Drops the turn_error branch too: a turn_error
comes only after the prompt's user_message, so awaiting is already clear by then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray
…r-continuous-turn

# Conflicts:
#	webviews/src/agent-session/acpmux/composer.gallery.ts
#	webviews/src/agent-session/acpmux/conversation/transcript.gallery.ts
#	webviews/src/agent-session/acpmux/conversation/turns.ts
#	webviews/src/agent-session/acpmux/direct.ts
#	webviews/src/agent-session/acpmux/model.ts
…r-continuous-turn

# Conflicts:
#	webviews/src/agent-session/acpmux/composer.gallery.ts
#	webviews/src/agent-session/acpmux/conversation/SteerMark.tsx
#	webviews/test/agent-pane-queue-steer.test.ts
@teamleaderleo
teamleaderleo merged commit e0a8dd4 into feat-cmux-next Oct 21, 2026
@teamleaderleo
teamleaderleo deleted the leo/steer-continuous-turn branch October 21, 2026 05:02
teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…age-hover-rows-rebased

Base's #22691 (a steer is one continuous turn) and #22910 (estimates) met this
PR's SteerMark removal: SteerMark stays deleted, the hover footer uses base's
turnFooterFrom except on a prompt (one action row at a time), and a steer
reserves a line only while it waits (PendingSteer); the queue-steer test keeps
the read-receipt assertions.

Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c08-3514-7430-9c71-6b7ba4fdac72&host=cosmic-gray
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

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