Gate agent completion on commits, pushes, and tests; capture real checkpoints

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
This commit is contained in:
Claude
2026-07-23 06:49:39 +00:00
parent f03b03ab5c
commit 9ad13b2488
8 changed files with 256 additions and 30 deletions
+8 -3
View File
@@ -342,9 +342,14 @@ All routes require `Authorization: Bearer <AUTH_TOKEN>`. `GET /health` is unauth
Wired into each agent as `python -m handler.hooks <event>`:
- **`Stop` / `SessionEnd`** — checkpoint. On `Stop`, run `mise run test`; on failure,
return `decision: "block"` with the output so the turn cannot end on red. Records
`tests_status` / `tested_at`; `status = 'done'` only ever accompanies a pass.
- **`Stop` / `SessionEnd`** — checkpoint + completion gate. On `Stop`, run
`mise run test` and check the tree deterministically: failing tests, uncommitted
changes, or commits that exist only locally each return `decision: "block"` with the
full blocker list, so a turn cannot end on red or walk away from unshipped work.
Records `tests_status` / `tested_at`; `status = 'done'` only ever accompanies a
passing gate (tests green, tree clean, everything pushed). The agent's final message
is captured from the session transcript onto the checkmark, so the dashboard always
shows a real checkpoint whether or not the agent thought to leave one.
- **`PreToolUse`** — two jobs. An `AskUserQuestion` is *deferred*: the question is
persisted, the checkmark set to `paused_for_input`, and the tool call denied so control
hands off to the async answer/resume flow. A `Bash` command running `git push` triggers