Runs one ticket end to end and opens a PR only when every gate is green. Preflight is shell run here, in your session, with no model in the loop; the stages run inside the gauntlet-run workflow. The runner is "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gauntlet/run.py"; its README (gauntlet/README.md beside it) is the per-repo bootstrap.
Procedure
-
Parse the arguments. The first token is the ticket reference; an optional
--from <stage>names the stage to enter at (defaultspecify). Valid stages:specify,coder,cleaner,qa,ship. Anything else: stop and list them. -
Resolve a Linear reference. If the reference matches
[A-Z]+-\d+, fetch the issue through the Linear MCP tools, write its title as an H1 and its description verbatim below it to.gauntlet/ticket-<id>.mdin the repo root, and use that path as the reference. A GitHub number or a file path needs nothing —run.pyreads those itself. -
Enter the run's worktree. The Stages are background subagents that Write/Edit the repo; on the shared checkout the platform's bg-isolation guard blocks them, forcing heredoc smuggling (ADR-0001). Isolate the run in a worktree this session owns:
git rev-parse --git-dirvsgit rev-parse --git-common-dir. Equal → you are on the main checkout: EnterWorktree (name it after the ticket, e.g.gauntlet-<ref>) to create and enter one. Unequal → you are already in a linked worktree; reuse it, never nest.- Run preflight and the workflow from inside that worktree.
-
Preflight. From the worktree root run, via Bash:
python3 "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gauntlet/run.py" preflight <ref> [--from <stage>]
It mints the run secret, writes .gauntlet/ticket.json (copying the git-ignored operator config.json into the worktree first), refuses a body without - [ ] Given …, when …, then … criteria, and runs clean-tree, install, setup, build and coverage (plus acceptance green when re-entering past the coder). It prints one JSON line.
5. Branch on ok.
-
false→ stop. Showfailedandtailverbatim. Do not fix anything, do not retry, do not invoke the workflow. A missing config means the repo has not been bootstrapped (see the README); a ticket with no criteria goes back through/to-tickets; a red guard is the repo's problem to solve in this session. -
true→ invoke the workflow immediately, passing the JSON object asargs(not a string):Workflow({ name: "gauntlet-run", args: <the parsed JSON> })
- Report the outcome when the workflow returns: the PR URL on
ship; onescalate, the reason, the stage, and thereentercommand verbatim — that command is how the run resumes once the cause is fixed here.
Rules
- Never write acceptance criteria. A free-text idea is not a ticket; it goes through
/grilling→/to-ticketsfirst. - Never run a stage in this session. Specify, coder, cleaner and QA are fresh-context agents inside the workflow by design; the operator's window stays lean.
- Never invoke the workflow with a hand-built
args— only preflight's output. The secret and HEAD it carries are what the receipts are hashed over.
Related
to-tickets — writes the criteria this consumes · gauntlet-run — the workflow this invokes · implement — the manual alternative for work that is not gauntlet-shaped