double-check
Purpose
Standalone second-pass PR review, stage-partitioned into an ICM procedure. Fetch the PR + the
first review + the diff and check it out (setup), then run ONE cohesive critical-judgement
review in a clean context (verify the first review's claims, find missed edge cases, and check
tests/docs together → a single consolidated verdict), then apply fixes and re-run tests if
needed (fix), then post the curated comment, apply the double-checked label, and write the
report (post). Behaviorally equivalent to the original double-check skill, just isolated so the
judgement step is not polluted by the orchestrator's history.
Arguments
| Param |
Required |
Default |
Notes |
pr |
yes |
— |
PR number, e.g. 742 |
repo |
yes |
— |
org/repo, e.g. fellowship-dev/booster-pack |
Parse from $ARGUMENTS: first token is pr, second is repo.
GitHub auth is ambient — no token env var. The pod's git-credential-pylot helper and the gh
shim mint short-lived App installation tokens per operation, so git URLs must stay plain
(https://github.com/<org>/<repo>.git); inline credentials bypass the helper and expire mid-run.
What it does
4-stage SEQUENTIAL ICM procedure (no parallel stages):
| Stage |
Mode |
Description |
| 01-setup |
subagent |
Fetch PR metadata including current HEAD, classify the incoming first-review receipt (its Head reviewed line) as current/stale/absent, capture comments + full diff, and checkout PR branch + merge base |
| 02-review |
subagent |
ONE cohesive critical review in clean context: reconcile the PR's claims against the diff, verify first review's claims, find missed edge cases, check tests/docs → consolidated verdict + curated findings |
| 03-fix |
subagent |
Apply MUST-FIX (and worthwhile NICE-TO-HAVE) fixes, re-run tests, push — only if fixes are needed |
| 04-post |
inline |
Re-fetch the live head, promote only when it equals the exact 40-hex head stage 02 reviewed, or perform one clean restart; then post curated review comment and apply labels only for the matching head |
Handoff locations
All handoffs live in the repo working directory:
.procedure-output/double-check/{stage}/handoff.md
The setup stage records the local checkout dir (REPO_DIR) in its handoff so the fix stage
operates on the same working tree. Each subagent stage receives only the handoffs it needs —
never the full orchestrator context.
Execution
Exact-head cycles (sequential subagents)
Run one Task per stage, one after another. Do NOT launch any stages in parallel. Do not start the
next stage until the current one completes. Start with restart_count=0. If stage 04 sees a
different full remote SHA, it stops without posting a verdict or touching labels, and the
orchestrator runs a complete conditional 01 → 02 → 03 → 04 cycle at the observed SHA with
restart_count=1. Stage 02 still receives only its new setup handoff: never orchestration
history. A second transition, or any unreadable live head, stops blocked.
Each Task prompt must be self-contained:
- Include only the stage's input handoff paths
- Include the path to the stage's CONTEXT.md
- Pass
pr and repo values
- Do NOT pass orchestrator history or prior reasoning
Task prompt template:
You are running stage {NN}-{name} of the double-check procedure.
PR: {pr} REPO: {repo}
Read your stage instructions:
skills/double-check/stages/{NN}-{name}/CONTEXT.md
Your inputs:
{list only the input handoff paths from that stage's CONTEXT.md}
Write your output to:
.procedure-output/double-check/{NN}-{name}/handoff.md
Execute all steps in CONTEXT.md. Write handoff.md before exiting.
Stage gating:
- Stage 02 is the isolated critical-judgement step. Its prompt MUST carry only the setup handoff
(PR + first review + diff) — nothing else. This is the clean-context window the whole proc exists for.
- After stage 02, read its handoff. If
fixes_needed: false, SKIP stage 03 (no fixes to apply)
and go straight to stage 04. Otherwise run stage 03.
Stage 04 (inline)
Run stage 04 yourself in the orchestrator context — do NOT spawn a Task. Read CONTEXT.md:
skills/double-check/stages/04-post/CONTEXT.md
Run the live claims-vs-diff and exact-head gates (gh pr view), then only for a matching head
post the comment and apply the label. Verify labels/comment actually landed, write the report
file, and emit the [pylot] outcome=... marker from the orchestrator (never from a subagent).
If stage 04 exits 3, set RESTART_COUNT=1 and run a new complete conditional
Stage 01 → 02 → 03 → 04 cycle at the current live head; do not pass the old
Stage 02 or Stage 03 handoff. If it exits 2, it is terminal blocked: do not run a promotion path.
Stage handoff chain
01-setup ─► 02-review ─► 03-fix ─► 04-post (inline, reads 01+02+03)
│ ▲
└── fixes_needed:false ┘ (skip 03)
Exit paths
- First-check fail closed (negative verdict or claims mismatch): stage 04 posts a
<!-- pylot:first-check-fail-closed --> comment, removes/withholds double-checked, adds or
retains needs-work, and creates no positive follow-on. It emits:
[pylot] outcome="double-check {repo}#{pr} — verdict {verdict}, double-checked withheld, needs-work retained" status=success
- Re-check PASS (PR had
needs-work, verdict=ready): stage 04 removes needs-work, re-toggles
double-checked (remove + re-add), and emits:
[pylot] outcome="double-checked re-check PASS {repo}#{pr} — loop closed, cto-review re-fired" status=success
- Re-check FAIL (PR had
needs-work, verdict=needs-work): stage 04 leaves needs-work in place,
does NOT re-toggle double-checked, posts a structured verdict comment with a
<!-- pylot:recheck-fail --> marker (idempotent — skipped if marker already present), and emits:
[pylot] outcome="double-checked re-check FAIL {repo}#{pr} — needs-work retained" status=success
- First-check success: only an explicit
ready verdict at the exact live 40-hex head may
apply double-checked; it emits:
[pylot] outcome="double-checked {repo}#{pr} — verdict ready, {N} findings curated, {N} fixes pushed" status=success
- Failure: failing stage emits
[pylot] outcome="double-check failed at stage NN: {reason}" status=failed
- Blocked: setup cannot fetch/checkout the PR (e.g. merge conflict, missing PR), the live
head cannot be read, or a second head transition occurs →
[pylot] outcome="double-check blocked: {reason}" status=blocked (a deliberate stop — blocked
is its own terminal state, not a failure)
Rework Follow-up Mode
Whether — and how — a "produce the fix" follow-up dispatch exists at all is
repo policy, not protocol: some orgs wire a CTO-rework automation on top
of this skill, some don't. Read the repo playbook (GET /admin/playbooks/<org>/<repo>) for a rework-dispatch section; if it names a
contract, it looks like this:
A gate rule dispatches back into this skill when a prior review left
needs-work, with a task string identifying it as a PRODUCE follow-up
(not a re-judge). The playbook names the exact trigger string/label to
match and the automation's identity — treat those as resolved values below,
not literal text to hardcode here.
When dispatched under that contract, do the following BEFORE running the
stages above (the playbook is the source of truth for the trigger and rule
name — keep this protocol in sync with it, don't let the two drift):
- Scan the PR's comments first. If a prior attempt already posted a
⚠️ NEEDS HUMAN marker, or a previous rework already tried and failed on
this SAME gate, do NOT loop — leave it for a human and stop. Never re-check
and re-apply needs-work without producing anything.
- Read the latest CTO review comment for its specific action items.
- Actually ADDRESS them. Code/test/doc fixes: make the changes and push.
Staging evidence requested: deploy the branch to staging, run the required
procedures, and update the PR body (not a comment) with the evidence
plus a
deployed_sha: <sha> line — the CTO gate scans the body. If the ask
genuinely exceeds this skill's scope (needs a specialized runner, a human
decision, or credentials you lack): do NOT silently re-apply needs-work —
post a comment beginning ⚠️ NEEDS HUMAN stating exactly what's blocking
and what would unblock it, then STOP.
- Only after the items are actually addressed:
gh pr edit <number> --repo <repo> --remove-label "double-checked,needs-work", then run the stages
above fresh so the label chain re-fires.
No rework-dispatch section in the playbook? This mode does not apply —
run the stages above as a normal review/fix/post pass.
Hard Rules
- SEQUENTIAL ONLY — one Task per stage, run one after another. NO parallel Task launches, ever.
- The review is ONE cohesive stage — do NOT split stage 02 into per-file or per-dimension
subagents. Correctness, edge cases, tests, docs, deps, and security are judged together in a
single verdict.
- Stage 02 gets a clean context — only the setup handoff (PR + first review + diff). Never
pass orchestrator history into it.
- Stage 04 runs inline — the
[pylot] outcome=... marker MUST come from the orchestrator.
- Never pass full orchestrator context into subagent Task prompts — inputs only.
- Each stage writes handoff.md before the next stage reads it.
- Do not skip stages except stage 03 when
fixes_needed: false (an explicit, allowed skip).
- NO Quest. Reporting is the local report file only — no Quest POST, no
127.0.0.1:4242,
no quest.fellowship.dev, no QUEST_TOKEN.
- Apply labels only after the comment posts successfully (stage 04). On re-check PASS,
remove
needs-work BEFORE re-adding double-checked — this is the structural loop-break.
On re-check FAIL, do NOT touch labels or re-toggle double-checked.
- The diff is the only evidence; the PR body is a claim. Stage 02 reconciles every concrete
claim in the title/body against the changed files, and stage 04 re-checks it against the live
PR. A claim with no code behind it and no pointer to where it landed is
needs-work —
"intentional", "the commit message explains it", and a LOW risk tier are NOT waivers.
double-checked is withheld until the body matches the diff. (pylot#2649, PR pylot#2782.)
- Stage 04 verifies its own side effects — after labelling,
gh pr view the PR and confirm
the expected labels/comment are actually there. Reporting success on unverified side effects
is the failure this skill exists to catch in others.
- Curate the first review, never re-derive it blind — setup captures the first review's
comments verbatim and its
Head reviewed receipt line; stage 02 curates those findings at
tier-scaled depth (escalate-only). No first review found → full-depth fresh review.
- A stale first review is historical evidence, not a blocker or current coverage — compare
its
head_sha with the post-rebase PR HEAD. Continue the cohesive review against the complete
current diff, re-check prior findings, and post a new current-head receipt. Never restart the
whole pipeline merely because the incoming receipt is stale.
- Promotion binds to the final full SHA — immediately before every verdict comment or label
mutation, fetch
headRefOid. The 40-character live SHA must equal the Stage 02
reviewed_head_sha, fail closed. On the first mismatch restart cleanly; on a second mismatch
or failed retrieval stop blocked. A delta inspection, file list, short SHA, or local HEAD
never substitutes for equality. A stale or blocked run never mutates double-checked or
triggers downstream automation.
- First-check promotion is explicitly positive only — apply
double-checked only when the
reviewer verdict is exactly ready and bound to the exact live head. Any negative, missing,
malformed, stale, or conflicting signal fails closed: remove/withhold double-checked, add or
retain needs-work, and do not create CTO, FlowChad, staging, or merge follow-ons.
- Success markers come from stage 04's templates ONLY (pylot#3392). The four documented
status=success markers in stage 04's "Emit outcome marker" section are the complete set.
Never synthesize a free-form success marker from the orchestrator, and never emit ANY
status=success unless stage 04 ran to completion — receipt comment posted and post-action
verification passed. On 2026-09-05 an orchestrator emitted "queued-worker result ok" without
running stage 04: the mission terminalized done with no receipt and the completed work was
invisible for ~6h. If you cannot run stage 04, the outcome is status=failed or
status=blocked with the reason — a fabricated success is the worst possible exit.
Reference files
CONTEXT.md — architecture overview
stages/NN-name/CONTEXT.md — per-stage inputs, task, output contract
shared/review-comment-template.md — curated PR comment template (stage 04)
shared/report-template.md — local report file template (stage 04)
1---2name: double-check3description: Use when performing a standalone PR double-check in a clean context — review, fix, and post.4---56# double-check78## Purpose910Standalone second-pass PR review, stage-partitioned into an ICM procedure. Fetch the PR + the11first review + the diff and check it out (setup), then run ONE cohesive critical-judgement12review in a clean context (verify the first review's claims, find missed edge cases, and check13tests/docs together → a single consolidated verdict), then apply fixes and re-run tests if14needed (fix), then post the curated comment, apply the `double-checked` label, and write the15report (post). Behaviorally equivalent to the original `double-check` skill, just isolated so the16judgement step is not polluted by the orchestrator's history.1718## Arguments1920| Param | Required | Default | Notes |21|-------|----------|---------|-------|22| `pr` | yes | — | PR number, e.g. `742` |23| `repo` | yes | — | `org/repo`, e.g. `fellowship-dev/booster-pack` |2425Parse from `$ARGUMENTS`: first token is `pr`, second is `repo`.26GitHub auth is ambient — no token env var. The pod's `git-credential-pylot` helper and the `gh`27shim mint short-lived App installation tokens per operation, so git URLs must stay plain28(`https://github.com/<org>/<repo>.git`); inline credentials bypass the helper and expire mid-run.2930## What it does31324-stage SEQUENTIAL ICM procedure (no parallel stages):3334| Stage | Mode | Description |35|-------|------|-------------|36| 01-setup | subagent | Fetch PR metadata including current HEAD, classify the incoming first-review receipt (its `Head reviewed` line) as current/stale/absent, capture comments + full diff, and checkout PR branch + merge base |37| 02-review | subagent | ONE cohesive critical review in clean context: reconcile the PR's claims against the diff, verify first review's claims, find missed edge cases, check tests/docs → consolidated verdict + curated findings |38| 03-fix | subagent | Apply MUST-FIX (and worthwhile NICE-TO-HAVE) fixes, re-run tests, push — only if fixes are needed |39| 04-post | inline | Re-fetch the live head, promote only when it equals the exact 40-hex head stage 02 reviewed, or perform one clean restart; then post curated review comment and apply labels only for the matching head |4041## Handoff locations4243All handoffs live in the repo working directory:44```45.procedure-output/double-check/{stage}/handoff.md46```4748The setup stage records the local checkout dir (`REPO_DIR`) in its handoff so the fix stage49operates on the same working tree. Each subagent stage receives only the handoffs it needs —50never the full orchestrator context.5152## Execution5354### Exact-head cycles (sequential subagents)5556Run one Task per stage, one after another. Do NOT launch any stages in parallel. Do not start the57next stage until the current one completes. Start with `restart_count=0`. If stage 04 sees a58different full remote SHA, it stops without posting a verdict or touching labels, and the59orchestrator runs a complete conditional 01 → 02 → 03 → 04 cycle at the observed SHA with60`restart_count=1`. Stage 02 still receives only its new setup handoff: never orchestration61history. A second transition, or any unreadable live head, stops blocked.6263Each Task prompt must be self-contained:64- Include only the stage's input handoff paths65- Include the path to the stage's CONTEXT.md66- Pass `pr` and `repo` values67- Do NOT pass orchestrator history or prior reasoning6869Task prompt template:70```71You are running stage {NN}-{name} of the double-check procedure.7273PR: {pr} REPO: {repo}7475Read your stage instructions:76 skills/double-check/stages/{NN}-{name}/CONTEXT.md7778Your inputs:79 {list only the input handoff paths from that stage's CONTEXT.md}8081Write your output to:82 .procedure-output/double-check/{NN}-{name}/handoff.md8384Execute all steps in CONTEXT.md. Write handoff.md before exiting.85```8687Stage gating:88- Stage 02 is the isolated critical-judgement step. Its prompt MUST carry only the setup handoff89 (PR + first review + diff) — nothing else. This is the clean-context window the whole proc exists for.90- After stage 02, read its handoff. If `fixes_needed: false`, SKIP stage 03 (no fixes to apply)91 and go straight to stage 04. Otherwise run stage 03.9293### Stage 04 (inline)9495Run stage 04 yourself in the orchestrator context — do NOT spawn a Task. Read CONTEXT.md:96```97skills/double-check/stages/04-post/CONTEXT.md98```99Run the live claims-vs-diff and exact-head gates (`gh pr view`), then only for a matching head100post the comment and apply the label. Verify labels/comment actually landed, write the report101file, and emit the `[pylot] outcome=...` marker from the orchestrator (never from a subagent).102If stage 04 exits `3`, set `RESTART_COUNT=1` and run a new complete conditional103Stage 01 → 02 → 03 → 04 cycle at the current live head; do not pass the old104Stage 02 or Stage 03 handoff. If it exits `2`, it is terminal blocked: do not run a promotion path.105106## Stage handoff chain107108```10901-setup ─► 02-review ─► 03-fix ─► 04-post (inline, reads 01+02+03)110 │ ▲111 └── fixes_needed:false ┘ (skip 03)112```113114## Exit paths115116- **First-check fail closed** (negative verdict or claims mismatch): stage 04 posts a117 `<!-- pylot:first-check-fail-closed -->` comment, removes/withholds `double-checked`, adds or118 retains `needs-work`, and creates no positive follow-on. It emits:119 `[pylot] outcome="double-check {repo}#{pr} — verdict {verdict}, double-checked withheld, needs-work retained" status=success`120- **Re-check PASS** (PR had `needs-work`, verdict=ready): stage 04 removes `needs-work`, re-toggles121 `double-checked` (remove + re-add), and emits:122 `[pylot] outcome="double-checked re-check PASS {repo}#{pr} — loop closed, cto-review re-fired" status=success`123- **Re-check FAIL** (PR had `needs-work`, verdict=needs-work): stage 04 leaves `needs-work` in place,124 does NOT re-toggle `double-checked`, posts a structured verdict comment with a125 `<!-- pylot:recheck-fail -->` marker (idempotent — skipped if marker already present), and emits:126 `[pylot] outcome="double-checked re-check FAIL {repo}#{pr} — needs-work retained" status=success`127- **First-check success**: only an explicit `ready` verdict at the exact live 40-hex head may128 apply `double-checked`; it emits:129 `[pylot] outcome="double-checked {repo}#{pr} — verdict ready, {N} findings curated, {N} fixes pushed" status=success`130- **Failure**: failing stage emits `[pylot] outcome="double-check failed at stage NN: {reason}" status=failed`131- **Blocked**: setup cannot fetch/checkout the PR (e.g. merge conflict, missing PR), the live132 head cannot be read, or a second head transition occurs →133 `[pylot] outcome="double-check blocked: {reason}" status=blocked` (a deliberate stop — `blocked`134 is its own terminal state, not a failure)135136## Rework Follow-up Mode137138Whether — and how — a "produce the fix" follow-up dispatch exists at all is139**repo policy, not protocol**: some orgs wire a CTO-rework automation on top140of this skill, some don't. Read the repo playbook (`GET141/admin/playbooks/<org>/<repo>`) for a rework-dispatch section; if it names a142contract, it looks like this:143144> A gate rule dispatches back into this skill when a prior review left145> `needs-work`, with a task string identifying it as a PRODUCE follow-up146> (not a re-judge). The playbook names the exact trigger string/label to147> match and the automation's identity — treat those as resolved values below,148> not literal text to hardcode here.149150When dispatched under that contract, do the following BEFORE running the151stages above (the playbook is the source of truth for the trigger and rule152name — keep this protocol in sync with it, don't let the two drift):1531540. Scan the PR's comments first. If a prior attempt already posted a155 `⚠️ NEEDS HUMAN` marker, or a previous rework already tried and failed on156 this SAME gate, do NOT loop — leave it for a human and stop. Never re-check157 and re-apply `needs-work` without producing anything.1581. Read the latest CTO review comment for its specific action items.1592. Actually ADDRESS them. Code/test/doc fixes: make the changes and push.160 Staging evidence requested: deploy the branch to staging, run the required161 procedures, and update the PR **body** (not a comment) with the evidence162 plus a `deployed_sha: <sha>` line — the CTO gate scans the body. If the ask163 genuinely exceeds this skill's scope (needs a specialized runner, a human164 decision, or credentials you lack): do NOT silently re-apply `needs-work` —165 post a comment beginning `⚠️ NEEDS HUMAN` stating exactly what's blocking166 and what would unblock it, then STOP.1673. Only after the items are actually addressed: `gh pr edit <number> --repo168 <repo> --remove-label "double-checked,needs-work"`, then run the stages169 above fresh so the label chain re-fires.170171> **No rework-dispatch section in the playbook?** This mode does not apply —172> run the stages above as a normal review/fix/post pass.173174## Hard Rules1751761. **SEQUENTIAL ONLY** — one Task per stage, run one after another. NO parallel Task launches, ever.1772. **The review is ONE cohesive stage** — do NOT split stage 02 into per-file or per-dimension178 subagents. Correctness, edge cases, tests, docs, deps, and security are judged together in a179 single verdict.1803. **Stage 02 gets a clean context** — only the setup handoff (PR + first review + diff). Never181 pass orchestrator history into it.1824. **Stage 04 runs inline** — the `[pylot] outcome=...` marker MUST come from the orchestrator.1835. **Never pass full orchestrator context** into subagent Task prompts — inputs only.1846. **Each stage writes handoff.md before the next stage reads it.**1857. **Do not skip stages** except stage 03 when `fixes_needed: false` (an explicit, allowed skip).1868. **NO Quest.** Reporting is the local report file only — no Quest POST, no `127.0.0.1:4242`,187 no `quest.fellowship.dev`, no `QUEST_TOKEN`.1889. **Apply labels only after the comment posts successfully** (stage 04). On re-check PASS,189 remove `needs-work` BEFORE re-adding `double-checked` — this is the structural loop-break.190 On re-check FAIL, do NOT touch labels or re-toggle `double-checked`.19110. **The diff is the only evidence; the PR body is a claim.** Stage 02 reconciles every concrete192 claim in the title/body against the changed files, and stage 04 re-checks it against the live193 PR. A claim with no code behind it and no pointer to where it landed is `needs-work` —194 "intentional", "the commit message explains it", and a LOW risk tier are NOT waivers.195 `double-checked` is withheld until the body matches the diff. (pylot#2649, PR pylot#2782.)19611. **Stage 04 verifies its own side effects** — after labelling, `gh pr view` the PR and confirm197 the expected labels/comment are actually there. Reporting success on unverified side effects198 is the failure this skill exists to catch in others.19912. **Curate the first review, never re-derive it blind** — setup captures the first review's200 comments verbatim and its `Head reviewed` receipt line; stage 02 curates those findings at201 tier-scaled depth (escalate-only). No first review found → full-depth fresh review.20213. **A stale first review is historical evidence, not a blocker or current coverage** — compare203 its `head_sha` with the post-rebase PR HEAD. Continue the cohesive review against the complete204 current diff, re-check prior findings, and post a new current-head receipt. Never restart the205 whole pipeline merely because the incoming receipt is stale.20614. **Promotion binds to the final full SHA** — immediately before every verdict comment or label207 mutation, fetch `headRefOid`. The 40-character live SHA must equal the Stage 02208 `reviewed_head_sha`, fail closed. On the first mismatch restart cleanly; on a second mismatch209 or failed retrieval stop blocked. A delta inspection, file list, short SHA, or local HEAD210 never substitutes for equality. A stale or blocked run never mutates `double-checked` or211 triggers downstream automation.21215. **First-check promotion is explicitly positive only** — apply `double-checked` only when the213 reviewer verdict is exactly `ready` and bound to the exact live head. Any negative, missing,214 malformed, stale, or conflicting signal fails closed: remove/withhold `double-checked`, add or215 retain `needs-work`, and do not create CTO, FlowChad, staging, or merge follow-ons.21616. **Success markers come from stage 04's templates ONLY (pylot#3392).** The four documented217 `status=success` markers in stage 04's "Emit outcome marker" section are the complete set.218 Never synthesize a free-form success marker from the orchestrator, and never emit ANY219 `status=success` unless stage 04 ran to completion — receipt comment posted and post-action220 verification passed. On 2026-09-05 an orchestrator emitted "queued-worker result ok" without221 running stage 04: the mission terminalized `done` with no receipt and the completed work was222 invisible for ~6h. If you cannot run stage 04, the outcome is `status=failed` or223 `status=blocked` with the reason — a fabricated success is the worst possible exit.224225## Reference files226227- `CONTEXT.md` — architecture overview228- `stages/NN-name/CONTEXT.md` — per-stage inputs, task, output contract229- `shared/review-comment-template.md` — curated PR comment template (stage 04)230- `shared/report-template.md` — local report file template (stage 04)