Skip to content

spec, master: headless roles must not background gates; tell a stalled revise from a declined one - #457

Merged
mkovero merged 1 commit into
mainfrom
fix-headless-agent-gates
Sep 15, 2026
Merged

mkovero merged 1 commit into
mainfrom
fix-headless-agent-gates

Conversation

@mkovero

@mkovero mkovero commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Problem

Pipeline roles run headless (bin/*.shclaude -p / codex exec). On 2026-09-15 three rounds on PR #437 were lost the same way. The role started its cargo gate as a background command and ended its turn to wait for a completion notification that never comes in -p mode, so the process exited with nothing posted:

role time (UTC) last words result
qa 10:04 "Holding here — clippy compiling full workspace in a fresh isolated target dir … Will resume automatically once it finishes." no review
developer 12:28 "Standing by for the cargo test --workspace background run to finish before posting the PR comment." fix written, never committed or pushed
qa 12:39 "Waiting on background gate run — will continue the review once notified." no review

master.sh reported the developer case as a decline: "revise pushed nothing … the block is one only you can clear: a rig measurement, acceptance of an assumed criterion, a design call." The developer had declined nothing. The fix was in the worktree, uncommitted.

Changes (rebased onto main after #458)

.agents/AGENTS.md: new every-role section headless sessions, placed after #458's bounded reading discipline:

  • no later turn exists, so never background a command whose result you need
  • gate steps run in the foreground, one call each, with the tool timeout at maximum; still foreground when a fresh target dir makes them take minutes
  • the deliverable (review comment / push / PR comment / labels) must exist before the turn ends
  • a step that can't finish in one call is stated in the comment, not parked
  • the three excerpts above are quoted as the concrete bad example

.agents/qa.md, developer.md, codex-qa.md: a pointer where each role runs commands.

bin/master.sh (qa_loop, needs-work → revise), fitted to #458's version of that block:

Verification (on the rebased commit)

  • bash -n bin/master.sh: clean. shellcheck -x -S warning bin/master.sh: 0 warnings, same as main.
  • bin/ has no test harness, so I used a throwaway one. It sources the rebased master.sh with the top-level dispatch removed, and refuses to source it if that removal did not match. It stubs gh_retry and revise.sh (exit 0), and uses a real git repo as $WT_BASE/<branch>:
    • (a) commented → "the developer commented…", STATE=needs-human
    • (b) no comment, dirty worktree → "this is NOT a decline" + "1 uncommitted tracked file(s)", STATE=needs-human
    • (c) comment count unreadable → "cannot count PR comments…", STATE=needs-human
    • (d) tip moved → labels swapped, no pushed-nothing message
    • (a) does not print the stall text, and (b) prints the uncommitted-file line.
  • The spec rule is not mechanically enforceable. claude --help (2.1.272) has no flag that disables background Bash, and --disallowedTools works per tool, not per parameter.

Not in this PR

🤖 Generated with Claude Code

https://claude.ai/code/session_01FuCG3RuLHArnC8snRZVDYG

…d revise from a declined one

Three pipeline rounds on PR #437 were lost on 2026-09-15 the same way:
a headless role (qa twice, developer once) started its cargo gate as a
background command, ended its turn "waiting for the notification", and
the claude -p process exited with nothing posted. The developer's fix sat
uncommitted in the worktree.

.agents/AGENTS.md gains an every-role "headless sessions" section: no
later turn exists, so never background a command whose result you need,
run gate steps in the foreground one call each, and have the deliverable
(comment, push, labels) in place before the turn ends. The three session
excerpts are quoted as the concrete bad example. qa.md, developer.md and
codex-qa.md point to it where each role runs commands.

bin/master.sh: "revise pushed nothing" always told the operator the
developer had declined ("the block is one only you can clear"). It now
counts PR comments around revise.sh. A comment keeps that message. No
comment reports that the session did not finish, not a decline, and names
any uncommitted tracked files left in the branch worktree plus the session
log. needs-work and STATE=needs-human stay in both cases.

Checked with a throwaway harness sourcing master.sh with gh_retry and
revise.sh stubbed: commented, stalled with a dirty worktree, unreadable
comment count and moved tip each take their own path, and the three
unchanged-tip cases end in STATE=needs-human.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FuCG3RuLHArnC8snRZVDYG
@mkovero
mkovero force-pushed the fix-headless-agent-gates branch from b1c884f to 79144ae Compare September 15, 2026 13:56
@mkovero
mkovero merged commit 39aebec into main Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant