Skip to content

daemon: playbook run is never reaped when the owning session never reports Running — including after it errors #1090

Description

@edwin-zvs

Every stop signal for a Playbook run is gated on the run having been seen running:

SessionState::Done | SessionState::Errored => {
    if run.seen_running {
        clear = true;
    }
}
SessionState::AwaitingInput => {
    if run.seen_running && (!run.agent_managed || (…pending empty…)) {
        clear = true;
    }
}

(crates/daemon/src/session/playbook_run.rs ~lines 541–568 @ ad47152)

seen_running is only set from a SessionState::Running transition. A turn that ends before the busy detector ever reports Running never sets it, so no stop signal can fire and the run survives to the PLAYBOOK_RUN_MAX_MS backstop — 10 minutes of the whole document shimmering (crates/daemon/src/session.rs ~line 122).

The Done | Errored arm is the worse half: a terminal session can never settle anything, so there is nothing to protect by waiting.

Repro

A — fast turn, no Running transition. Run a Playbook on a shell session whose delivered prompt fails immediately (e.g. bash syntax error). Subscribed to subscribe.events for 20s afterwards: one playbook/state with seen_running: false, and no session/state status event at all. The session sits at awaiting input while all 9 blocks report shimmer: true, and stay that way.

B — session dies mid-run. Create a session, set a Playbook, playbook.execute, then kill the session:

=== after kill ===
● ✗ se01e3332a  shell    errored
  b0:0  shimmer=True  '# T'
  b1:0  shimmer=True  '- alpha'
  b2:0  shimmer=True  '- beta'

The owning agent is gone and the Playbook keeps shimmering.

Both reproduce with a shell harness, but the shape is general: any harness whose turn fails fast (crashed CLI, auth error, rejected prompt) lands in case A.

Expected

  • Done / Errored: clear the run unconditionally. Nothing can settle those blocks.
  • AwaitingInput with no Running ever observed: use a short post-dispatch grace (~10–20s) rather than the 10-minute backstop, so a failed dispatch stops shimmering promptly.

Per spec 0042, narrowing/stop remains best-effort and system-owned; this is about the backstop being the only signal in these cases.

Acceptance criteria

  • A session reaching Done/Errored clears its active Playbook run regardless of seen_running.
  • A run whose session goes idle without ever reporting Running clears within seconds, not 10 minutes.
  • Tests cover both: terminate-before-Running, and idle-without-Running.

Found during a hands-on UX audit of the Playbook in the TUI and web UI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions