Agents could be marked done while leaving work uncommitted or unpushed:
the Stop gate only ran the test suite, and the headless supervisor's
fallback marked any still-working agent done on a clean process exit
even when the Stop gate never recorded a verdict. Checkmarks also only
ever carried hook-written boilerplate, so the webui had no real
checkpoint to show.
The Stop gate now blocks the turn on any of: failing tests, uncommitted
changes, or commits no origin/* ref contains (rev-list --not
--remotes=origin, so it works for the --no-track worktree branches).
All blockers are reported at once; status 'done' only ever accompanies
a fully passing gate. Working dirs that aren't git checkouts, and repos
without an origin remote, skip the git half so local-only projects
can't deadlock. The supervisor's clean-exit fallback now reconciles a
still-working agent to blocked instead of done — done is a gate
verdict, not an exit code (operator cancels still settle as done).
The agent's final message is captured deterministically from the
session transcript onto the checkmark's where_it_stopped, so the
dashboard always shows a real checkpoint regardless of whether the
agent thought to leave one; a blocked checkmark shows the blockers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7mF6qeryi9nJthaxYkfPm
Agent runs are now always worker-owned 'claude -p' subprocesses; tmux
survives only for the interactive /login flow.
- deleted: worker.capture_agent_output/_pane_tail + the capture loop
arm (the empty-/log bug's home), spawn's tmux launch/_claude_command,
the tmux resume/kill branches (the silent-send-keys bug's home),
tmux.session_name/list_sessions, the CLI attach subcommand, the
'runner' setting
- spawn: task is now a hard requirement (headless has no idle REPL) -
enforced in spawn (SpawnError) and the API (400); onboarding seeding
dropped (-p skips the trust dialog)
- resume: single headless path; pre-headless agent rows (no session_id)
degrade to the context-re-injection fresh run
- settings_gen: permissions allowlist is always emitted
- credsync: change-triggered uploads key on .claude/.credentials.json
only (claude touches ~/.claude.json every run - keying on it would
ping-pong uploads between workers); logins still publish explicitly
- cli list: liveness from agent_runs in the DB, not tmux
- tests: spawn/kill/resume re-pointed at the fake_launch seam
(conftest); integration test now drives API -> worker -> real fake
claude subprocess -> events endpoint; README documents the headless
model + multi-worker deployment invariants
Suite 295 green; frontend unchanged since phase 3.
Wires the phase-1 headless machinery behind runner=headless (default
stays tmux; legacy agents, session_id null, keep the tmux paths):
- spawn: branches tmux vs headless.launch; extracts _agent_env (shared
with resume - a headless resume is a new process needing identity/
credential env); headless spawns require a task (no idle-REPL mode),
enforced at spawn and as a 400 in the API
- resume: headless path materializes the session archive from the DB
onto whichever worker claimed the command, then claude -p --resume;
falls back to a fresh session with DB-re-injected context (visible
worker event) when no transcript survives anywhere; refuses while a
run is live. Undeliverable resumes now raise -> command FAILED,
fixing silent input loss on both runners
- kill: headless path flags cancel_requested; the owning supervisor
SIGTERMs its own child (cross-worker safe)
- worker: stable per-container ids, DB-driven run slots (full workers
skip claiming spawn/resume/mise_init, leaving them for less-loaded
workers), credsync refresh in the main loop
- settings_gen: permissions block (defaultMode + allowlist) for
headless runs - -p auto-denies anything that would prompt; hooks
remain the hard gate
- credsync + migration 0009 (runtime_secrets): login publishes the
Fernet-encrypted claude credential bundle; every worker materializes
it (merge-safe for local trust state); login_submit pinned to the
login_start worker via commands.target_worker
Suite 270 -> 290 green, including the cross-worker resume linchpin
(clean-HOME materialize + --resume against the fake binary).