Skip to content

harness: Stop/Esc must not drain the queue; idle ▶ promotes when the composer is empty #757

Description

@btipling

Summary

Stop (■) and Esc cancel the in-flight turn and today look like a finished turn: Wasm sees busy → ready and tryPromoteQueued starts the next queued prompt. The operator asked to stop; the harness keeps going.

Stop must only stop. The queue stays put. Consuming the next item is an explicit Play: idle with items in the queue, even when the composer is empty.

Amends #756: that issue’s “Stop still drains after Ready” is wrong. Stop never promotes. Play does.

Current baseline (live code)

Claim Path / symbol Notes
Promote on any terminal native/harness/src/ui.zig ~227–235 terminal = ready or err; busy → terminaltryPromoteQueued (success, error, and Stop all go Ready)
Docs docs/harness-limits.md Submit queue When it runs: Ready or error. Stop: “queue stays and drains after Ready” — this is the defect
Idle ▶ ui/composer_chrome.zig if (res.typed.len > 0) on_send — empty composer is a no-op
Ctrl/Cmd+Enter idle composer.submitOrEnqueue / keymap submit Blank normalize → clearPrompt and return; does not promote
Stop / Esc on_stopqueueCancelFromUi; Esc cancel_turn while Busy Host abort → Ready → same promote path as success

Goals

# Goal Success signal
1 ■ Stop and Esc (Busy, not queue-edit / help) do not promote Queue depth unchanged; no new user row from the old head; composer idle; ■ gone, ▶ back
2 Idle with queue non-empty and empty composer promotes the head One click starts that prompt as a normal user turn (pending_submit / queueSubmitFromUi); head popped; Busy
3 Idle with non-empty composer still sends the composer text (today) Queue untouched until that turn’s successful Ready (see #756)
4 Idle with empty composer and empty queue stays a no-op No blank user row (existing empty-send guard)

Non-goals

  • Do not auto-drain the queue after Stop, error, or cancel
  • Do not change Busy ▶ (still enqueue when the field has text)
  • Do not consume the queue on error — that’s #756 (retry / Continue-at-head)
  • Do not add a second “Play queue” icon; the existing idle ▶ is the control
  • Forbidden wiring: dual DOM chat · secrets in Wasm

Design

Stop / Esc — promote only after successful Ready, never after cancel

tryPromoteQueued on busy → ready must not run when the turn ended because of Stop. Same gate #756 needs for error.

Practical split:

Host already has classifyTurnFailure (stop vs error vs timeout). Wasm today cannot tell success Ready from Stop Ready — both are .ready. Options (pick one in implementation, lock in the PR):

  1. Host sets a one-frame “do not promote” flag / distinct lifecycle (.err already exists; Stop could stay Ready with a inv_* inhibit, or use Error only for failures and a boolean promote_ok)
  2. Promote moves off the lifecycle edge and onto an explicit Play only (goal 2). Then success would also not auto-drain — operator hits ▶ to continue the queue after every turn.

Operator intent for this issue: Stop must not play the next item. After a successful turn, auto-promote may remain (today / #756 goal 1). After Stop, only ▶ starts the next item.

Idle ▶ — empty field + queue → promote

In paintComposerChrome idle branch (and the request_submit / submitOrEnqueue blank path):

if composer empty and idle and queuedCount > 0:
  tryPromoteQueued(editing)
else if composer non-empty:
  submitOrEnqueue(text)   // today
else:
  no-op

▶ stays enabled when the field is empty iff the queue is non-empty (visual: don’t grey the icon in that state). Ctrl/Cmd+Enter should do the same (it’s the same submit action) so keyboard and Play match.

submit_queue.canPromote already requires !busy && count > 0 && !editing && !pending_submit. Reuse it.

Living docs

Surface Change
docs/harness-limits.md Submit queue Stop Stop cancels only the in-flight turn. Queue does not drain. Idle ▶ (empty composer) starts the head
Same table When it runs Auto-promote on successful Ready only (not Stop, not error). Manual promote: idle ▶ / Ctrl+Enter with empty field + non-empty queue
Keyboard & focus Esc Busy = stop, no queue consume
AGENTS.md / README / SECURITY N/A unless a new cap/export is added

Testing

# Case Expected
1 Busy + 2 queued + ■ Stop Ready; queue still 2; no new user line from head
2 Same + Esc Same as 1
3 After 1, idle empty composer, click ▶ Head submits; queue 1; Busy
4 After 1, type text, click ▶ Sends the typed prompt (enqueue-not, idle send); queue still 2
5 Idle, empty composer, empty queue, ▶ No-op
6 Queue-row editor open + Esc Still dismisses the editor, not the turn / not promote (existing precedence)

submit_queue unit tests + composer/keymap host tests for empty-submit-promotes. zig build test-rich · vitest if the host must pass a Stop-vs-success promote inhibit.

Cloud ops

N/A — Wasm queue + host abort; no Production mutate.

Refs #756 (errors / retry) · submit-queue #664 · Esc cancel_turn #741

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingharnessHarness / agent UIui

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions