Skip to content

Commit 6de8863

Browse files
committed
docs(tui): document ctrl+c as a pause that stays idle
1 parent c485d25 commit 6de8863

2 files changed

Lines changed: 30 additions & 30 deletions

File tree

‎docs/PRODUCT.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The evidence is in how the product fails today: the personas already produce exc
4141
5. **Resume capability** — Runs persist to a git-backed store and resume from the last point after interruption.
4242
6. **Legible loop** — A live event log, working-tree diff panel, plan tracker, and real-time cost meter show what happened, when, and why.
4343
7. **Operator-in-the-loop** — The agent can call `ask_operator` to pause and ask a clarifying question; the operator answers from a modal (TUI). Headless `corbits exec` unmounts `ask_operator` when stdin/stdout are not TTYs. TTY exec still reads a single line from stdin.
44-
8. **Mid-run steering** — Two modes while the agent is running, keyed to **whose** idle. **Parent-idle** is when the primary dispatch turn is not inside an in-flight parent tool; **session-idle** is parent-idle **and** no live fleet lanes. **Enter** soft-steers while the parent is busy — delivers at the next **parent** `tool.boundary` without stopping the current run; a long parent `run_shell` is parent-busy, so Enter is a queued steer, not a new turn. A queued steer delivers at the next parent `tool.boundary` so occupancy can pick it up. Idle-with-fleet is shipped: after a non-blocking `spawn_agent` dispatch the parent goes idle while workers keep running, mailbox mail arrives as inbound when a worker finishes or fails, and mid-hold Enter starts a new primary turn instead of queueing a steer. **Alt+Enter** queues a follow-up delivered only on session-idle (`run` goes idle; does not interrupt). Session-idle Alt+Enter is a no-op. **Ctrl+C** stops the run outright. The notice row shows distinct `steer N` / `follow-up M` badges, and held messages list in a pending column stacked on the prompt box — `↑`/`↓` select, `Enter` force-pushes one now, `Ctrl+X` drops it — instead of echoing labelled transcript rows; when steers are pending and a parent tool has been in flight a few seconds, the notice names that command. Shortcuts are listed in `/help` (`Enter` soft-steer · `Alt+Enter` follow-up · `Ctrl+C` stop).
44+
8. **Mid-run steering** — Two modes while the agent is running, keyed to **whose** idle. **Parent-idle** is when the primary dispatch turn is not inside an in-flight parent tool; **session-idle** is parent-idle **and** no live fleet lanes. **Enter** soft-steers while the parent is busy — delivers at the next **parent** `tool.boundary` without stopping the current run; a long parent `run_shell` is parent-busy, so Enter is a queued steer, not a new turn. A queued steer delivers at the next parent `tool.boundary` so occupancy can pick it up. Idle-with-fleet is shipped: after a non-blocking `spawn_agent` dispatch the parent goes idle while workers keep running, mailbox mail arrives as inbound when a worker finishes or fails, and mid-hold Enter starts a new primary turn instead of queueing a steer. **Alt+Enter** queues a follow-up delivered only on session-idle (`run` goes idle; does not interrupt). Session-idle Alt+Enter is a no-op. **Ctrl+C** pauses: stop the primary, hold the queue, stay idle until an explicit operator send. The notice row shows distinct `steer N` / `follow-up M` badges, and held messages list in a pending column stacked on the prompt box — `↑`/`↓` select, `Enter` force-pushes one now, `Ctrl+X` drops it — instead of echoing labelled transcript rows; when steers are pending and a parent tool has been in flight a few seconds, the notice names that command. Shortcuts are listed in `/help` (`Enter` soft-steer · `Alt+Enter` follow-up · `Ctrl+C` pause).
4545
9. **Orchestrator-only (TUI + exec)** — The primary session is always the orchestrator: it can act directly and delegates via `spawn_agent` (then idle; mailbox mail inbound) / `search_agents`. Nested orchestrators collect through mailbox mail the same way. Long jobs belong on workers — a parent that runs them itself stays parent-busy and holds Enter steers. Single-agent session mode, the first-run mode picker, and Settings → Session are gone (CL-5814). Legacy `sessionMode` values on disk are ignored.
4646

4747
## User Experience

‎docs/TUI.md‎

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,9 @@ nearest the prompt, first selected — stay visible, and a guidance row naming
693693
its keys (`pending-column.ts`, painted by `syncPendingRows` in `chrome.ts`).
694694
The transcript only ever sees the item that actually delivers, as an ordinary
695695
user row — pending/delivery labels (`[will steer next]`, `[steering]`,
696-
`[following up]`) are gone on purpose.
696+
`[following up]`) are gone on purpose. After a first Ctrl+C pause the column
697+
is the durable surface: held items stay pending and deliver only on the next
698+
explicit operator send.
697699

698700
While the column has items, `↑` at the prompt buffer's top edge selects the
699701
newest held item and `↑`/`↓` walk the rows; `↓` past the last row hands the
@@ -736,26 +738,26 @@ as id/status/description only. The mail and that fleet-dry continuation
736738
are runtime-to-agent traffic — the fleet board owns worker status — so
737739
neither paints a transcript row, and neither rehydrates as one.
738740

739-
Interrupting (Ctrl+C) never discards a queued or steered message. It used to
740-
— the transcript literally said `interrupt — discarded N pending`, and an
741-
operator who queued an instruction and then lost patience destroyed the very
742-
thing they were trying to deliver. It now reports `interrupt — N pending
743-
kept`: the run stops, the queue survives, and those messages are handed over
744-
at the interrupt itself (`doInterrupt` drains after `port.interrupt()`), not
745-
left waiting on an idle event the stop may never produce (`interrupt` in
746-
`delivery-queue.ts` no longer clears `items`).
741+
Interrupting (Ctrl+C) never discards a queued or steered message. The first
742+
press is a pause: it stops the primary, holds the queue, and reports
743+
`N pending kept` (`interrupt` in `delivery-queue.ts` does not clear `items`).
744+
Held items stay in the pending column and deliver only on the next explicit
745+
operator send. Stall and expire aborts do not pause, so they still drain.
746+
Occupancy and ask-wake do not start a new primary while paused. A compaction
747+
continuation is dropped on operator pause (`abortCompaction`); it is not held
748+
and does not auto-resume.
747749

748750
**Fleet agent lanes on redirect.** Soft steer (Enter mid-run) and follow-up
749751
(queued drain) leave running workers alone — they never call
750752
`runner.ts`'s `interrupt()`, so the parent's operation signal stays live and
751-
spawned workers keep running. Ctrl+C is the explicit fleet teardown:
752-
`doInterrupt` → `port.interrupt()` → `currentAgent.close()` aborts the shared
753-
operation signal and routes cancellation through the fleet/session-store
754-
teardown path, so in-flight fleet-agent dispatch reports back as cancelled by
755-
the operator rather than being left to finish silently detached. `/clear` and
753+
spawned workers keep running. The first Ctrl+C pauses the parent and does not
754+
tear down the fleet. A second press while workers are live stops those workers
755+
(`cancelLiveWorkers`); the app stays up and the held queue is kept. A third
756+
press quits. With no live workers, the second press still quits. `/clear` and
756757
session exit still call `subAgentSessions.cancelAll` for an explicit
757758
session-wide cancel; that path is separate from interrupt and must stay off
758-
the soft-steer / follow-up gestures.
759+
the soft-steer / follow-up gestures. Quit still full-teardowns, including
760+
wiping the delivery queue.
759761

760762
Up/Down are caret motion first inside a multi-line buffer — except while the
761763
pending column is engaged, when ↑ at the buffer's top edge selects a held item
@@ -813,21 +815,19 @@ The prompt repaints on every keystroke (`onFrame` in `src/tui/shell/index.ts` ca
813815
not on a debounce) — anything added to the prompt's paint path must stay
814816
cheap, because it runs at typing speed.
815817

816-
Ctrl+C interrupts a busy run, or clears idle prompt text and pending
818+
Ctrl+C pauses a busy run (stop the primary, hold the queue, stay idle until
819+
an explicit operator send), or clears idle prompt text and pending
817820
attachments. Clearing prompt text arms a 2-second quit window
818-
(`CTRL_C_EXIT_WINDOW_MS`); clearing attachments alone does not. A second
819-
Ctrl+C while the window is open quits — this
820-
replaced an Ink-era yes/no exit-confirm modal with the same intent (an
821-
explicit second confirmation) without adding a modal (`handleCtrlC`,
822-
`src/tui/shell/prompt.ts`). See "Soft steer vs. follow-up" above for the two
823-
mid-run gestures and what interrupting does to fleet-agent lanes. The interrupt
824-
keeps whatever is sitting in the queue rather than discarding it — the
825-
operator typed those messages meaning them delivered, not meaning "cancel
826-
this run and also throw away what I typed"; the transcript row says so
827-
(`"N pending kept"`). Kept items are handed over at the
828-
interrupt itself (`doInterrupt` in `runtime-bridge.ts` drains after
829-
`port.interrupt()`), serialized behind the agent rebuild the stop starts —
830-
a stop does not reliably produce an idle event to drain against later.
821+
(`CTRL_C_EXIT_WINDOW_MS`); clearing attachments alone does not. With live
822+
sub-agents, a second Ctrl+C in the window stops the workers and keeps the
823+
app up (queue kept); a third quits. With no live workers, the second press
824+
still quits. The extra press is the confirmation, with no modal
825+
(`handleCtrlC`, `src/tui/shell/keys.ts`). See "Soft steer vs. follow-up"
826+
above for the two mid-run gestures and what interrupting does to
827+
fleet-agent lanes. The pause keeps whatever is sitting in the queue rather
828+
than discarding it — the transcript row says so (`"N pending kept"`). Held
829+
items deliver only on the next explicit send; occupancy and ask-wake do
830+
not start a new primary while paused.
831831

832832
## Overflows, scrolling, and key macros
833833

0 commit comments

Comments
 (0)