dev-loops 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +7 -7
  3. package/.claude/agents/fixer.md +1 -0
  4. package/.claude/agents/refiner.md +4 -4
  5. package/.claude/agents/review.md +10 -13
  6. package/.claude/commands/loop-continue.md +2 -1
  7. package/.claude/commands/loop-enqueue.md +9 -2
  8. package/.claude/commands/loop-info.md +1 -1
  9. package/.claude/commands/loop-start-spike.md +1 -1
  10. package/.claude/hooks/_bash-command-classify.mjs +7 -6
  11. package/.claude/hooks/_hook-decisions.mjs +5 -4
  12. package/.claude/skills/copilot-pr-followup/SKILL.md +42 -28
  13. package/.claude/skills/dev-loop/SKILL.md +8 -8
  14. package/.claude/skills/docs/acceptance-criteria-verification.md +13 -4
  15. package/.claude/skills/docs/anti-patterns.md +6 -5
  16. package/.claude/skills/docs/artifact-authority-contract.md +17 -14
  17. package/.claude/skills/docs/confirmation-rules.md +2 -1
  18. package/.claude/skills/docs/contract-style-guide.md +37 -0
  19. package/.claude/skills/docs/copilot-loop-operations.md +21 -15
  20. package/.claude/skills/docs/cross-harness-regression-contract.md +2 -2
  21. package/.claude/skills/docs/debt-remediation-contract.md +1 -1
  22. package/.claude/skills/docs/epic-tree-refinement-procedure.md +25 -22
  23. package/.claude/skills/docs/issue-intake-procedure.md +37 -22
  24. package/.claude/skills/docs/merge-preconditions.md +17 -13
  25. package/.claude/skills/docs/pr-lifecycle-contract.md +45 -36
  26. package/.claude/skills/docs/public-dev-loop-contract.md +61 -44
  27. package/.claude/skills/docs/retrospective-checkpoint-contract.md +25 -9
  28. package/.claude/skills/docs/spike-mode-contract.md +14 -7
  29. package/.claude/skills/docs/stop-conditions.md +30 -15
  30. package/.claude/skills/docs/tracker-first-loop-state.md +13 -8
  31. package/.claude/skills/docs/validation-policy.md +4 -5
  32. package/.claude/skills/local-implementation/SKILL.md +49 -143
  33. package/.claude/skills/loop-grill/SKILL.md +34 -14
  34. package/AGENTS.md +1 -1
  35. package/CHANGELOG.md +36 -0
  36. package/README.md +95 -189
  37. package/agents/dev-loop.agent.md +8 -8
  38. package/agents/fixer.agent.md +1 -0
  39. package/agents/refiner.agent.md +4 -4
  40. package/agents/review.agent.md +10 -13
  41. package/extension/README.md +5 -4
  42. package/package.json +7 -5
  43. package/scripts/_core-helpers.mjs +2 -0
  44. package/scripts/claude/headless-dev-loop.mjs +1 -1
  45. package/scripts/docs/validate-rule-ownership.mjs +442 -0
  46. package/scripts/docs/validate-state-machine-conformance.mjs +1169 -0
  47. package/scripts/github/_gate-names.mjs +5 -0
  48. package/scripts/github/_review-thread-mutations.mjs +5 -2
  49. package/scripts/github/capture-review-threads.mjs +2 -2
  50. package/scripts/github/create-pr.mjs +119 -10
  51. package/scripts/github/detect-checkpoint-evidence.mjs +109 -24
  52. package/scripts/github/edit-issue.mjs +259 -0
  53. package/scripts/github/list-review-threads.mjs +277 -0
  54. package/scripts/github/post-gate-findings.mjs +6 -3
  55. package/scripts/github/probe-ci-status.mjs +18 -0
  56. package/scripts/github/probe-copilot-review.mjs +24 -3
  57. package/scripts/github/reconcile-draft-gate.mjs +13 -13
  58. package/scripts/github/request-copilot-review.mjs +109 -42
  59. package/scripts/github/upsert-checkpoint-verdict.mjs +98 -27
  60. package/scripts/github/verify-briefing-prefixes.mjs +382 -0
  61. package/scripts/github/verify-fresh-review-context.mjs +78 -3
  62. package/scripts/github/wait-pr-checks.mjs +171 -0
  63. package/scripts/github/write-gate-context.mjs +220 -10
  64. package/scripts/github/write-gate-findings-log.mjs +54 -2
  65. package/scripts/loop/_post-convergence-change.mjs +2 -2
  66. package/scripts/loop/_pr-runner-coordination.mjs +112 -13
  67. package/scripts/loop/check-retro-tooling.mjs +14 -9
  68. package/scripts/loop/copilot-pr-handoff.mjs +47 -16
  69. package/scripts/loop/detect-change-scope.mjs +2 -2
  70. package/scripts/loop/detect-copilot-loop-state.mjs +34 -14
  71. package/scripts/loop/detect-internal-only-pr.mjs +6 -6
  72. package/scripts/loop/detect-pr-gate-coordination-state.mjs +152 -18
  73. package/scripts/loop/detect-refinement-grill-state.mjs +136 -0
  74. package/scripts/loop/resolve-dev-loop-startup.mjs +145 -8
  75. package/scripts/loop/run-watch-cycle.mjs +42 -7
  76. package/scripts/loop/sanctioned-commands.mjs +3 -0
  77. package/scripts/loop/validate-pr-body-spec.mjs +21 -5
  78. package/scripts/pages/build-state-atlas.mjs +65 -48
  79. package/scripts/projects/_resolve-project.mjs +1 -148
  80. package/scripts/projects/add-queue-item.mjs +87 -4
  81. package/scripts/projects/list-queue-items.mjs +3 -377
  82. package/scripts/projects/move-queue-item.mjs +3 -410
  83. package/scripts/projects/reorder-queue-item.mjs +3 -22
  84. package/skills/copilot-pr-followup/SKILL.md +42 -28
  85. package/skills/dev-loop/SKILL.md +3 -3
  86. package/skills/docs/acceptance-criteria-verification.md +13 -4
  87. package/skills/docs/anti-patterns.md +6 -5
  88. package/skills/docs/artifact-authority-contract.md +17 -14
  89. package/skills/docs/confirmation-rules.md +2 -1
  90. package/skills/docs/contract-style-guide.md +37 -0
  91. package/skills/docs/copilot-loop-operations.md +21 -15
  92. package/skills/docs/cross-harness-regression-contract.md +2 -2
  93. package/skills/docs/debt-remediation-contract.md +1 -1
  94. package/skills/docs/epic-tree-refinement-procedure.md +25 -22
  95. package/skills/docs/issue-intake-procedure.md +37 -22
  96. package/skills/docs/merge-preconditions.md +17 -13
  97. package/skills/docs/pr-lifecycle-contract.md +45 -36
  98. package/skills/docs/public-dev-loop-contract.md +61 -44
  99. package/skills/docs/required-rules.json +165 -0
  100. package/skills/docs/retrospective-checkpoint-contract.md +25 -9
  101. package/skills/docs/spike-mode-contract.md +14 -7
  102. package/skills/docs/stop-conditions.md +30 -15
  103. package/skills/docs/tracker-first-loop-state.md +13 -8
  104. package/skills/docs/validation-policy.md +4 -5
  105. package/skills/local-implementation/SKILL.md +49 -143
  106. package/skills/loop-grill/SKILL.md +38 -17
  107. package/scripts/docs/validate-no-duplicate-rules.mjs +0 -250
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-loops",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Agent-harness-agnostic dev-loop: agents, skills, and hooks for GitHub/Copilot-driven development loops.",
5
5
  "author": {
6
6
  "name": "Manuel Fittko",
@@ -12,25 +12,25 @@ Your job is to provide the callable `dev-loop` public façade and route to the c
12
12
 
13
13
  ## Handoff envelope mandate (first action)
14
14
 
15
- The agent's first action after resolving authoritative state must be to build the handoff envelope via `buildDevLoopHandoffEnvelope()` from `@dev-loops/core`.
15
+ The agent's first action after resolving authoritative state MUST be to build the handoff envelope via `buildDevLoopHandoffEnvelope()` from `@dev-loops/core`.
16
16
 
17
17
  The envelope is the primary handoff artifact — it is derived from resolver output, settings, and gate state, and it determines:
18
18
  - `requiredReads` — the canonical ordered list of files to load
19
19
  - `nextAction` — the bounded task to execute
20
- - `stopRules` — stop boundaries that must not be crossed without authorization
20
+ - `stopRules` — stop boundaries that MUST NOT be crossed without authorization
21
21
  - `acceptance` — self-validation criteria for declaring completion
22
22
  - `sanctionedCommands` — the operation → wrapper command map (reads/edits/lifecycle), plus the forbidden and orchestrator-owned lists. Carried by DEFAULT on every build so you never re-derive which wrapper performs a GitHub/loop operation. Do NOT restate the map here — the single source of truth is `scripts/loop/sanctioned-commands.mjs`, surfaced verbatim in the envelope.
23
23
 
24
24
  **Construction sequence:**
25
25
 
26
- 1. Run the deterministic startup resolver to produce the authoritative state bundle: `npx dev-loops@0.7.1 loop startup --issue <n>` for issues, or `npx dev-loops@0.7.1 loop startup --pr <n>` for PRs.
26
+ 1. Run the deterministic startup resolver to produce the authoritative state bundle: `npx dev-loops@0.8.0 loop startup --issue <n>` for issues, or `npx dev-loops@0.8.0 loop startup --pr <n>` for PRs.
27
27
  2. Pass the resolver output, resolved settings (merged from `.devloops` and `.pi/dev-loop/defaults.yaml`), and current gate state to `buildDevLoopHandoffEnvelope()`.
28
28
  3. **Validate the envelope** with `validateHandoffEnvelope()` before consuming any field. If validation returns `ok: false`, reject the handoff with the structured error — do not load requiredReads, do not execute nextAction, do not delegate.
29
29
  4. Read the envelope as the first artifact.
30
30
  5. Load every path listed in `requiredReads` (in order).
31
31
  6. Execute `nextAction` constrained by `stopRules` and `acceptance`.
32
32
 
33
- **The agent must not load skills, route packs, or delegate work before the envelope is built and read.** The derivation contract is [Workflow Handoff Contract](../skills/docs/workflow-handoff-contract.md).
33
+ **The agent MUST NOT load skills, route packs, or delegate work before the envelope is built and read.** The derivation contract is [Workflow Handoff Contract](../skills/docs/workflow-handoff-contract.md).
34
34
 
35
35
  Prose task composition is a fallback only when `buildDevLoopHandoffEnvelope()` is unavailable (missing `@dev-loops/core` package) — the handoff contract in `skills/docs/workflow-handoff-contract.md` applies in that fallback case.
36
36
 
@@ -40,7 +40,7 @@ After the handoff envelope is built and read, load the `dev-loop` skill ([Dev Lo
40
40
 
41
41
  When that skill is not available at the expected path, resolve it from the skill installation layout (see the skill's "Skill asset path resolution" section).
42
42
 
43
- This entrypoint must stay thin: do not restate the skill's phase sequencing or workflow policy here. The envelope owns handoff sequencing; the skill owns routed strategy execution procedures.
43
+ This entrypoint MUST stay thin: do not restate the skill's phase sequencing or workflow policy here. The envelope owns handoff sequencing; the skill owns routed strategy execution procedures.
44
44
 
45
45
  Treat the deterministic public routing contract in [Public Dev Loop Contract](../skills/docs/public-dev-loop-contract.md) and the `dev-loop` skill as the authority for choosing the current execution path. Do not force users to choose internal strategy names up front.
46
46
 
@@ -57,9 +57,9 @@ If local facts, GitHub facts, and helper/state-machine output do not agree well
57
57
 
58
58
  ## Subagent delegation
59
59
 
60
- All delegation must originate from the handoff envelope: the envelope's `nextAction`, `requiredReads`, `stopRules`, and `acceptance` define the bounded task. The envelope is passed to child subagents as their primary handoff artifact.
60
+ All delegation MUST originate from the handoff envelope: the envelope's `nextAction`, `requiredReads`, `stopRules`, and `acceptance` define the bounded task. The envelope is passed to child subagents as their primary handoff artifact.
61
61
 
62
- The pi-subagents skill is parent-only, so delegated subagents do not receive orchestration patterns. This section exists as the minimal locally-enforced subset needed for correct delegation — it is not a restatement of the full policy. The `dev-loop` skill owns all procedural rules; this section only declares the invariants the agent must follow when it cannot defer to the skill:
62
+ The pi-subagents skill is parent-only, so delegated subagents do not receive orchestration patterns. This section exists as the minimal locally-enforced subset needed for correct delegation — it is not a restatement of the full policy. The `dev-loop` skill owns all procedural rules; this section only declares the invariants the agent MUST follow when it cannot defer to the skill:
63
63
  - One writer thread; `async: true` default; `context: "fresh"` for reviewers.
64
64
  - No child subagent spawning beyond assigned fanout work.
65
65
  - Bounded tasks with concrete scope, exit conditions, and validation expectations.
@@ -26,6 +26,7 @@ You are a focused review-fix agent. You take an existing pull request with revie
26
26
 
27
27
  ## Review Workflow
28
28
  1. Read unresolved review threads and any general review comments.
29
+ - Prefer the deterministic helper `scripts/github/list-review-threads.mjs --unresolved-only` to enumerate threads with their reply/resolve ids, rather than hand-writing a GraphQL query.
29
30
  2. Group related comments by file and identify the underlying concern behind each comment.
30
31
  3. Decide the best resolution for each concern: exact requested change, better alternative fix, explanation-only resolution, or escalation for expert judgment.
31
32
  4. If expert input is needed, stop before editing or resolving the thread and report the question, evidence, and options.
@@ -35,13 +35,13 @@ For the active phase, require and produce:
35
35
 
36
36
  - use exact wording from the source issue(s); when the governing input is a phase doc or other spec instead of an issue, use that source wording exactly for every explicit item in the matrix
37
37
  - include every explicit acceptance criterion, definition-of-done item, and non-goal; do not skip items
38
- - if no explicit definition of done exists, add a `Proposed DoD` subsection before the matrix
38
+ - <!-- rule: REFINER-DOD-PROPOSED-SUBSECTION --> `REFINER-DOD-PROPOSED-SUBSECTION`: if no explicit definition of done exists, add a `Proposed DoD` subsection before the matrix
39
39
  - explicit risks, watchpoints, and unresolved questions
40
40
  - validation steps and tests to write first
41
- - durable decisions that should be preserved in the phase doc
41
+ - durable decisions that SHOULD be preserved in the phase doc
42
42
  - when the phase includes a bounded audit or scan: prioritized findings, the highest-value follow-up candidates, and an explicit statement of what the current phase will not rewrite or broaden
43
43
  - When an audit artifact is provided, treat it as a first-class planning input: summarize the audited scope, list prioritized findings, include the highest-value follow-up candidates, and classify each meaningful finding as exactly one of current-phase scope/AC, DoD expectation, explicit non-goal / defer, or risk/watchpoint
44
- - Do not invent audit findings when no audit artifact was provided
44
+ - <!-- rule: REFINER-NO-INVENT-AUDIT-FINDINGS --> `REFINER-NO-INVENT-AUDIT-FINDINGS`: Do not invent audit findings when no audit artifact was provided
45
45
  - when the phase includes watcher or predicate-driven behavior: explicit timeout semantics and negative-case expectations for non-target identities/events
46
46
  - when the phase relies on package-first shared helpers inside a source-loaded workspace: explicit integration expectations about whether local callers use published package imports or a thin source/workspace adapter during development
47
47
  - cross-check the phase's claims against the contracts and docs they reference (the autonomous docs-grill step, see ../docs/docs-grill-step.md): surface code-vs-doc drift, stale references, and contract-surface inaccuracies as refinement findings while the claims are still being verified
@@ -83,4 +83,4 @@ Return:
83
83
 
84
84
  ## Completion quality bar
85
85
  - A refinement is complete only when no item in the AC/DoD/Non-goal coverage matrix has status `Partial`, `Unmet`, or `Unverified`.
86
- - Any `Partial`, `Unmet`, or `Unverified` item means the refinement is still incomplete and must not be presented as ready.
86
+ - Any `Partial`, `Unmet`, or `Unverified` item means the refinement is still incomplete and MUST NOT be presented as ready.
@@ -26,18 +26,15 @@ You are a focused pull request review agent. You review an implementation for co
26
26
 
27
27
  ## Scoped angle-review mode
28
28
 
29
- This agent has two modes. The default mode is the full-PR review described in the rest of this file. The **scoped angle-review mode** is the per-angle reviewer of the gate-review fan-out ([Gate Review Sub-Loop Contract](../docs/gate-review-sub-loop-contract.md)): the gate skill builds ONE deterministic, neutral context bundle (diff + adjacent code) and seeds each independent `review` agent with that identical bundle, each scoped to exactly one review concern. You receive only the neutral artifact + your angle — never the main (orchestrating) agent's conversation, opinions, or state.
30
-
31
- You are in scoped angle-review mode when the invocation supplies a single review `<angle>` plus a gate-context artifact path (`tmp/gate-context/<repo-slug>/pr-<N>/<gate>-<headSha>.json`, written by `scripts/github/write-gate-context.mjs`). Review like an external code reviewer hunting real bugs, not a process auditor: your job is to find concrete defects in the changed code and its call paths, not to remark that "there is no test" or that a doc section is thin. In that mode:
32
-
33
- - **No worktree isolation (mandatory):** you review in the PR's actual worktree/head — the same checkout the gate-context preamble ran in — never a fresh isolated worktree. You are read-only and never mutate files, so isolation buys nothing; a fresh worktree would also be checked out from `main` (not the PR head) and would lack the gitignored, worktree-local gate-context bundle below, so you'd silently review the wrong stale tree (#1135).
34
- - **Fresh-context guard (mandatory):** run `node scripts/github/verify-fresh-review-context.mjs --scope <angle> --context-path <gate-context-artifact-path>` at startup. If it reports `fresh: false` (exit 1) or any error including a missing gate-context artifact (`gateContextPresent: false`), which is the fail-closed signal that you're not in the right worktree/headrefuse to proceed and report the failure; do not review on inherited context or without seeded context. "Fresh" here means your context is the **neutral builder artifact + your angle**, and explicitly NOT the main agent's conversation/state or a prior reviewer session's state. The injected neutral bundle is the INTENDED seed (allowed); main-agent or cross-session state bleed still fails closed.
35
- - **Use the PROVIDED neutral bundle as your base; do NOT re-derive it from scratch:** the gate-context artifact carries the diff (`scope.diffPath`) and a deterministic, neutral adjacent-code bundle in `adjacentCode` (each changed file plus its 1-hop import in/out-edges, with a `stripped`/`truncated`/`missing` manifest). Start from this bundle — it is the build-once, work-deduped seed shared verbatim across all reviewers. Only widen (load more files) per-angle when the bundle genuinely lacks something your angle needs; do not re-build the whole diff/adjacent-code graph yourself.
36
- - **Single-angle scope:** review ONLY the supplied angle's concern. Read the gate-context artifact for the resolved angle set, change scope (branch, head SHA, touched files), acceptance-criteria pointer, and validation posture. Do not review other angles; the fan-in pass consolidates across angles.
37
- - **Read the FULL diff, not just hunks:** the gate-context artifact records `scope.diffPath` (a repo-relative path to the complete captured diff) and `scope.changedFiles` (full repo-relative paths). Read the entire diff from `scope.diffPath`; if it is `null` or missing, reconstruct it yourself with `git diff` against the change's base (e.g. `git diff origin/main...HEAD` or the branch/head SHA in `scope`). Do not review off a partial or summarized hunk list — you must see every changed line for your angle.
38
- - **Reason about ADJACENT related code from the bundle first:** the `adjacentCode` bundle already includes the changed files' callers, callees, and imports (1-hop). Read those entries before opening anything else. For each changed function, trace the real call paths a defect would flow through, and confirm that caller and callee contracts still match (argument shapes, return shapes, error/null conventions, units). If the bundle truncated or stripped a file you need (see its `truncated`/`stripped` manifest), open that file directly to widen.
39
- - **Review ADVERSARIALLY — hunt concrete defects:** actively try to break the changed code. Hunt for edge cases, missing or wrong input validation, numeric coercion bugs (NaN, Infinity, floats where integers are assumed, negative values, string-to-number coercion), null/undefined handling, off-by-one and boundary conditions, mismatched caller/callee contracts, and dedup/identity bugs (wrong key, reference vs. value equality, unstable ordering). For every finding, give the concrete `file:line` plus the specific failing scenario or input that triggers the defect — not high-altitude "is there a test?" commentary.
40
- - **Strictly read-only, but WIDEN SCOPE when needed:** never edit files, stage, commit, push, request reviews, resolve threads, or post PR comments. Findings are returned via the output artifact only; fixes are applied later by the fix cycle, not by this agent. Because you are read-only, you MAY open any additional repository files required to judge your angle (callers, callees, contracts, sibling modules, configs, tests) — widening to read adjacent code is expected and safe. Record every file/module you consulted beyond the briefing's `changedFiles`/`diffPath` in the optional `contextWidened` field on your output artifact.
29
+ This agent has two modes. The default mode is the full-PR review described in the rest of this file. In **scoped angle-review mode** you are one per-angle reviewer of the gate-review fan-out. You are in this mode when the invocation supplies a single review `<angle>` plus a gate-context artifact path (`tmp/gate-context/<repo-slug>/pr-<N>/<gate>-<headSha>.json`, written by `scripts/github/write-gate-context.mjs`).
30
+
31
+ Its full execution shape is owned elsewhere read those owners before reviewing and do not re-derive their rules here:
32
+
33
+ - The build-once neutral bundle seeding, fresh-context guard (`verify-fresh-review-context.mjs`), no-worktree-isolation prohibition (#1135), single-angle read-only scope, and briefing composition are owned by the [Gate Review Sub-Loop Contract](../docs/gate-review-sub-loop-contract.md) (`GATE-EXEC-BUILD-ONCE-SEED`, `GATE-EXEC-BRIEFING-PREFIX`) you receive only the neutral artifact + your angle, never the orchestrating agent's conversation, opinions, or state.
34
+ - The adversarial reviewing behavior is owned by `COPILOT-FOLLOWUP-ADVERSARIAL-BRIEFING` in the [Copilot PR Follow-up Skill](../skills/copilot-pr-followup/SKILL.md): read the FULL diff (from `scope.diffPath`, or reconstruct it with `git diff` against the change base when `scope.diffPath` is null/missingnever a hunk-only review) plus the bundled adjacent code rather than re-deriving them, then hunt concrete `file:line` defects (edge cases, input validation, numeric coercion incl. NaN/Infinity/floats/negatives, null/undefined, boundary conditions, mismatched caller/callee contracts, dedup/identity bugs) over process nits, recording any scope-widening in the optional `contextWidened` field on your findings artifact.
35
+
36
+ Follow those owners, then return your findings via the structured artifact below (this agent's canonical output contract):
37
+
41
38
  - **Structured findings artifact:** return a single JSON object the fan-in consolidator (`@dev-loops/core/loop/gate-fanin`) can parse, written to the deterministic per-angle path `tmp/gate-reviews/<repo-slug>/pr-<N>/<gate>-<headSha>/<angle>.json`:
42
39
 
43
40
  ```json
@@ -73,7 +70,7 @@ When NOT given an angle scope, behave exactly as the full-PR review agent descri
73
70
  - Prefer concrete findings with file references and impact over generic style commentary.
74
71
  - Distinguish clearly between must-fix findings, lower-severity risks, and informational gaps.
75
72
  - If the PR description omits required sections, is too thin to ground review without reconstructing intent from commits, or includes verdict status, evidence, or changelog content, treat that as a first-class review issue.
76
- - The review verdict must carry the acceptance-criteria and definition-of-done assessment in explicit markdown verification tables, including status plus concise evidence for each row.
73
+ - The review verdict MUST carry the acceptance-criteria and definition-of-done assessment in explicit markdown verification tables, including status plus concise evidence for each row.
77
74
  - For follow-up reviews on the same PR, do not repost full AC/DoD tables: include only delta rows where status or supporting evidence changed, and explicitly note when there are no AC/DoD deltas.
78
75
  - When changelog coverage is needed, include a dedicated `## Changelog` section in the review verdict comment so post-merge automation can consume it without reading the PR description.
79
76
 
@@ -8,7 +8,8 @@ Continue a dev loop. Two forms, both handed to the `dev-loop` skill — do NOT p
8
8
 
9
9
  - With an argument (`$ARGUMENTS` is an issue or PR — `123`, `#123`, or a GitHub URL): run the `dev-loop` skill with the public intent `continue dev loop on $ARGUMENTS`. Resolve that artifact's authoritative state first, then route; ignore board position.
10
10
 
11
- - Bare (no `$ARGUMENTS`): resolve the single continue target from the board. Resolve the board's repo and project the same way the queue commands do, then run `node scripts/projects/resolve-active-board-item.mjs --repo <owner/name> --project <number>`. It continues the single **In Progress** item; if there is none, it picks the **HEAD of Next Up by position**. It fails closed (idle) when Next Up is empty, and still fails closed on multiple In-Progress items. It never pulls from Backlog.
11
+ - Bare (no `$ARGUMENTS`): resolve the single continue target from the board, per
12
+ `QUEUE-LIVE-PICKUP-SOURCE` <!-- rule-ref: QUEUE-LIVE-PICKUP-SOURCE --> (owned by `docs/projects-queue-contract.md`). Resolve the board's repo and project the same way the queue commands do, then run `node scripts/projects/resolve-active-board-item.mjs --repo <owner/name> --project <number>`. It continues the single **In Progress** item; if there is none, it picks the **HEAD of Next Up by position**. It fails closed (idle) when Next Up is empty (`QUEUE-NEXTUP-EMPTY-FAIL-CLOSED` <!-- rule-ref: QUEUE-NEXTUP-EMPTY-FAIL-CLOSED -->), and still fails closed on multiple In-Progress items. It never pulls from Backlog.
12
13
  - It returns `{ ok: true, target: { kind, number }, source }` (`source` is `"in-progress"` or `"next-up"`) → run the `dev-loop` skill with the public intent `continue dev loop on #<number>` (the resolved `target.number`), so the intent carries the concrete target instead of re-resolving board state.
13
14
  - It returns `{ ok: false, reason }` when there are multiple in-progress items, or when there is nothing in progress **and** Next Up is empty → FAIL CLOSED: print the reason verbatim and instruct the user to run `/loop-continue #N` explicitly (or prioritize a Backlog item into Next Up). Do NOT guess, and never pick from Backlog.
14
15
 
@@ -14,9 +14,16 @@ Add work to the dev-loop queue. Parse `$ARGUMENTS` before acting. The project is
14
14
  - Run `node scripts/projects/add-queue-item.mjs --repo <owner/repo> --item <n>`.
15
15
  - The script is idempotent and returns `{ ok, item: { status, alreadyPresent } }`. Report the resulting Status column and whether it was already present, e.g. `#<n> is in "Backlog" (already on the board)` or `#<n> added to "Backlog"`.
16
16
  - Exit `3` means the issue/PR was not found (`ITEM_NOT_FOUND` / `CONTENT_NOT_FOUND`): print `Error: issue/PR #<n> not found.` and stop. Do not retry with a guessed project.
17
+ - Exit `4` means the issue targets the pickup (`Next Up`) column without a refinement artifact (`MISSING_REFINEMENT_ARTIFACT`): print the error naming the missing sections and point at `/loop-grill <n> --auto`.
18
+ - **Headless / auto ordering (who grills):** the `add-queue-item.mjs` gate enforces and parks; it never grills. So in a headless/auto run, **first** run `/loop-grill <n> --auto` to synthesize AC/DoD, **then** enqueue with `--next-up`. Only if grilling cannot produce a usable artifact, pass `--auto` on the `add-queue-item.mjs` call so the issue parks in the non-pickup column with a recorded reason (from the JSON `refinement` field) rather than failing the run — surface that reason and leave the issue parked for a human to refine.
17
19
 
18
- 3. **Freeform path (quick-capture a new idea):** run these steps in order.
20
+ 3. **Freeform path (quick-capture a new idea):** run these steps in order. This is the
21
+ quick-capture exemption from `INTAKE-NEW-IDEA-SAFETY`
22
+ ([Issue intake procedure](../skills/docs/issue-intake-procedure.md)) — the up-front
23
+ proposal artifact, async classification, and second async mutation pass are deferred in
24
+ favor of the inline confirm→create→grill steps below (it always creates a new issue,
25
+ human-gated).
19
26
  1. **Confirm first — no mutation before approval.** Print a one-line preview of the issue to be created (`About to create issue: "<concise title>"`) and STOP for approval. Do not create the issue, grill, or enqueue anything until the human approves.
20
27
  2. **Create the issue.** After approval, create a minimal GitHub issue from the text. The repo has no dedicated create-issue wrapper under `scripts/github/`; the sanctioned create path in this codebase is `gh issue create --repo <owner/repo> --assignee @me --title "<concise title>" --body "<freeform text>"` (the same path used by `skills/docs/issue-intake-procedure.md` and permitted by `skills/docs/main-agent-contract.md`). Title = a concise summary of the text; body = the verbatim freeform text.
21
28
  3. **Grill it.** Run `/loop-grill <n> --auto` (delegates to the `loop-grill` skill). The skill writes a `## Grill findings` section back into the issue body and flags any unresolved items there, so they are visible before pickup. Do not re-implement grilling.
22
- 4. **Enqueue.** Run `node scripts/projects/add-queue-item.mjs --repo <owner/repo> --item <n>` to add the grill-annotated issue to the board, then report its Status column as in the numeric path. The `strategy: local-first` default is set repo-wide in `.devloops` (`strategy.default: local-first`), so the new issue is already local-first — this repo has no per-issue local-first label or body tag to apply, and you must not invent one.
29
+ 4. **Enqueue.** Run `node scripts/projects/add-queue-item.mjs --repo <owner/repo> --item <n>` to add the grill-annotated issue to the board, then report its Status column as in the numeric path. The `strategy: local-first` default is set repo-wide in `.devloops` (`strategy.default: local-first`), so the new issue is already local-first — this repo has no per-issue local-first label or body tag to apply, and you MUST NOT invent one.
@@ -4,4 +4,4 @@ argument-hint: "<issue|pr>"
4
4
  ---
5
5
  <!-- GENERATED from commands/loop-info.command.md by scripts/claude/generate-claude-assets.mjs — do not edit; edit the source and regenerate. -->
6
6
 
7
- Resolve the read-only dev-loop state for `$ARGUMENTS` via the `loop info` shortcut — no full dev-loop run. If `$ARGUMENTS` is an issue number, run `npx dev-loops@0.7.1 loop info --issue $ARGUMENTS`; if it is a PR number, run `npx dev-loops@0.7.1 loop info --pr $ARGUMENTS`. Report the strategy, route, linked PR / branch, CI, and next action. Do not start implementation.
7
+ Resolve the read-only dev-loop state for `$ARGUMENTS` via the `loop info` shortcut — no full dev-loop run. If `$ARGUMENTS` is an issue number, run `npx dev-loops@0.8.0 loop info --issue $ARGUMENTS`; if it is a PR number, run `npx dev-loops@0.8.0 loop info --pr $ARGUMENTS`. Report the strategy, route, linked PR / branch, CI, and next action. Do not start implementation.
@@ -11,6 +11,6 @@ Start a spike — a time-boxed exploration whose deliverable is a findings docum
11
11
  - Run `node scripts/refine/scaffold-spike-file.mjs --question "$ARGUMENTS" --out <path> --json`. It writes `## Question` (from the arg) plus stubbed `## Approach`/`## Findings` so the exploration scaffold passes `validateSpikeExplorationSections`; `## Recommendation` is left for the spike to fill in. It returns `{ ok: true, path, question }`.
12
12
  - Then run `node scripts/loop/resolve-dev-loop-startup.mjs --spike <path>` and route the resulting bundle through the `dev-loop` skill.
13
13
 
14
- - Pre-authored file (`$ARGUMENTS` begins with `--file <path>`): skip scaffolding and run `node scripts/loop/resolve-dev-loop-startup.mjs --spike <path>` directly, then route. The file must already carry the exploration scaffold (Question/Approach/Findings).
14
+ - Pre-authored file (`$ARGUMENTS` begins with `--file <path>`): skip scaffolding and run `node scripts/loop/resolve-dev-loop-startup.mjs --spike <path>` directly, then route. The file MUST already carry the exploration scaffold (Question/Approach/Findings).
15
15
 
16
16
  The spike runs under the relaxed `gates.spike` profile. When the exploration reaches a `## Recommendation`, conclude it with `node scripts/refine/exit-spike.mjs --spike-file <path> --disposition <discard|graduate> [--plan-file <path>]` per the spike-mode contract — discard leaves nothing behind, graduate emits a plan file into the local-planning flow. No new routing logic here.
@@ -235,18 +235,19 @@ function extractRepoFlagsFromGhSubcmdVerbSegments(command, subcmd, verb) {
235
235
 
236
236
  /**
237
237
  * The raw external-write verb forms that must be blocked when originating from a subagent:
238
- * ad-hoc GitHub issue/PR creation and comments run directly via `gh` (not the sanctioned node
239
- * wrappers). Each entry is `[subcmd, verb]`.
238
+ * ad-hoc GitHub issue/PR creation, comments, and edits run directly via `gh` (not the sanctioned
239
+ * node wrappers). Each entry is `[subcmd, verb]`.
240
240
  */
241
241
  const EXTERNAL_WRITE_VERB_FORMS = Object.freeze([
242
242
  ["issue", "create"],
243
243
  ["issue", "comment"],
244
+ ["issue", "edit"],
244
245
  ["pr", "comment"],
245
246
  ]);
246
247
 
247
248
  /**
248
- * Whether `command` contains a raw `gh issue create`, `gh issue comment`, or `gh pr comment`
249
- * invocation in ANY shell segment (ignoring --help/-h). PreToolUse gate use only — the gate
249
+ * Whether `command` contains a raw `gh issue create`, `gh issue comment`, `gh issue edit`, or
250
+ * `gh pr comment` invocation in ANY shell segment (ignoring --help/-h). PreToolUse gate use only — the gate
250
251
  * blocks these when they originate from a subagent context. Node-wrapper commands
251
252
  * (`node scripts/github/comment-issue.mjs …`) never match (first token is `node`, not `gh`).
252
253
  * @param {string} command @returns {boolean}
@@ -256,8 +257,8 @@ export function commandContainsRawExternalWrite(command) {
256
257
  }
257
258
 
258
259
  /**
259
- * Return `{ segment, explicitRepo }` for every raw external-write segment across all three verb
260
- * forms (`gh issue create` / `gh issue comment` / `gh pr comment`). PreToolUse gate use only —
260
+ * Return `{ segment, explicitRepo }` for every raw external-write segment across all four verb
261
+ * forms (`gh issue create` / `gh issue comment` / `gh issue edit` / `gh pr comment`). PreToolUse gate use only —
261
262
  * lets the gate decide in-scope-ness per segment so a leading out-of-scope write can't shield a
262
263
  * later in-scope one. `explicitRepo` is the segment's `--repo`/`-R` value or null.
263
264
  * @param {string} command @returns {{ segment: string, explicitRepo: string|null }[]}
@@ -62,7 +62,7 @@ export const DEV_LOOP_AGENT_TYPE = "dev-loop";
62
62
  * clean current-head draft_gate + pre_approval_gate). The loop runs this check before merging;
63
63
  * gating it here closes the hole where a hand-run `gh pr merge` skips the pre-approval gate
64
64
  * entirely. Everything else passes through.
65
- * - raw `gh issue create` / `gh issue comment` / `gh pr comment` — blocked ONLY when the call
65
+ * - raw `gh issue create` / `gh issue comment` / `gh issue edit` / `gh pr comment` — blocked ONLY when the call
66
66
  * originates from a SUBAGENT context (`agentType` is a non-null string) and targets the repo.
67
67
  * Sanctioned external writes flow through node wrappers (gate-verdict comments via
68
68
  * `upsert-checkpoint-verdict.mjs`, review replies via `reply-resolve*.mjs`, board sync,
@@ -86,7 +86,7 @@ export function decideBashGate({ command, repoSlug = null, gatePassed = false, g
86
86
  return ALLOW;
87
87
  }
88
88
  // Subagent-scoped external-write guard: block ad-hoc `gh issue create`/`gh issue comment`/
89
- // `gh pr comment` on the target repo from a subagent, so external writes flow through the
89
+ // `gh issue edit`/`gh pr comment` on the target repo from a subagent, so external writes flow through the
90
90
  // sanctioned node wrappers. The main-agent/operator path (agentType null) is unaffected (#1051).
91
91
  if (typeof agentType === "string" && commandContainsRawExternalWrite(command)) {
92
92
  const cwdTargets = (repoSlug ?? "").toLowerCase() === TARGET_REPO_SLUG.toLowerCase();
@@ -102,9 +102,10 @@ export function decideBashGate({ command, repoSlug = null, gatePassed = false, g
102
102
  return {
103
103
  decision: "deny",
104
104
  reason:
105
- "Ad-hoc GitHub issue/PR creation and comments from a subagent are blocked. Use the sanctioned " +
105
+ "Ad-hoc GitHub issue/PR creation, comments, and edits from a subagent are blocked. Use the sanctioned " +
106
106
  "node wrappers instead — gate-verdict comments via scripts/github/upsert-checkpoint-verdict.mjs, " +
107
- "review-thread replies via scripts/github/reply-resolve*.mjs, board sync, or scripts/github/comment-issue.mjs. " +
107
+ "review-thread replies via scripts/github/reply-resolve*.mjs, board sync, issue comments via " +
108
+ "scripts/github/comment-issue.mjs, or issue-body edits via scripts/github/edit-issue.mjs. " +
108
109
  "Direct `gh issue create` is reserved for the main agent / operator.",
109
110
  };
110
111
  }
@@ -9,7 +9,7 @@ user-invocable: false
9
9
 
10
10
  # Copilot PR Follow-up
11
11
 
12
- This skill is the canonical internal `copilot_pr_followup` route behind the public `dev-loop` façade.
12
+ Canonical owner for the internal `copilot_pr_followup` route behind the public `dev-loop` façade.
13
13
 
14
14
  It is also the canonical internal owner of the shared post-PR mechanics used by this repo:
15
15
  PR discovery and interpretation, async watch behavior, fix / reply-resolve / re-request flow,
@@ -63,6 +63,8 @@ Max watch timeout: **30 minutes** (from `policy-constants.mjs` COPILOT_REVIEW_WA
63
63
  ```sh
64
64
  node <resolved-skill-scripts>/github/request-copilot-review.mjs --help
65
65
  node <resolved-skill-scripts>/github/probe-copilot-review.mjs --help
66
+ node <resolved-skill-scripts>/github/list-review-threads.mjs --help
67
+ node <resolved-skill-scripts>/github/wait-pr-checks.mjs --help
66
68
  node <resolved-skill-scripts>/loop/detect-copilot-loop-state.mjs --help
67
69
  ```
68
70
 
@@ -86,7 +88,7 @@ Verify all material claims against source, tests, configuration, and CI.
86
88
  When this skill refers to helper paths such as `scripts/...` or `docs/...`, resolve them from the actual skill installation layout you are running, not from the active target repository checkout.
87
89
 
88
90
  Use this rule:
89
- - if the skill is installed as a normalized standalone copy, the required bundled contract docs live under the shared `../docs/` directory next to the installed skill directories; do not assume helper scripts are bundled unless that installed layout actually contains them
91
+ - if the skill is installed as a normalized standalone copy, the required bundled contract docs live under the shared `../docs/` directory next to the installed skill directories. <!-- rule: ASSET-PATH-INSTALLED-NO-ASSUME --> `ASSET-PATH-INSTALLED-NO-ASSUME`: Agents MUST NOT assume helper scripts are bundled unless that installed layout actually contains them.
90
92
  - if you are working in the `dev-loops` source repository, this skill file lives under `skills/copilot-pr-followup/`, so source-repo helper scripts live two levels up at `../../scripts/`, while required bundled contract docs live one level up at `../docs/`
91
93
  - when in doubt, resolve helper paths relative to this [skill file](./SKILL.md) first, then verify the target file exists before running it
92
94
 
@@ -97,7 +99,7 @@ Required bundled runtime contract docs for installed copies of this skill:
97
99
  - [Copilot Loop Operations](../docs/copilot-loop-operations.md)
98
100
 
99
101
  Read those bundled `../docs/` files from the installed skill layout instead of assuming the source repository checkout is present. If any required bundled contract doc is missing from the installed skill layout, treat that as a packaging/installer bug.
100
- Do not assume `scripts/...` is repo-local to the target codebase you are operating on.
102
+ <!-- rule: ASSET-PATH-SOURCE-NO-REPO-LOCAL --> `ASSET-PATH-SOURCE-NO-REPO-LOCAL`: Agents MUST NOT assume `scripts/...` is repo-local to the target codebase they are operating on.
101
103
 
102
104
  ## Authority and safety rules
103
105
 
@@ -115,16 +117,19 @@ Inspect: PR body/title (must satisfy [PR description contract](../docs/copilot-l
115
117
 
116
118
  At the issue-assignment seam, use `detect-initial-copilot-pr-state.mjs` and keep waiting when `waiting_for_initial_copilot_implementation`.
117
119
 
118
- When confirming whether Copilot is requested as a reviewer, do not rely solely on `gh pr view --json reviewRequests`. Use `request-copilot-review.mjs` (see [Operational cookbook](#operational-cookbook)). Do **not** request Copilot by posting literal `/copilot` or `/copilot re-review` PR comments. After draft→ready or fix push, explicitly decide whether another pass is desired; if yes, ensure green/credibly green posture first.
120
+ <!-- rule: COPILOT-FOLLOWUP-REQUEST-HELPER-ONLY -->
121
+ `COPILOT-FOLLOWUP-REQUEST-HELPER-ONLY`: Copilot review requests MUST go through `request-copilot-review.mjs` (see [Operational cookbook](#operational-cookbook)); the agent MUST NOT request Copilot by posting literal `/copilot` or `/copilot re-review` PR comments, and MUST NOT rely solely on `gh pr view --json reviewRequests` to confirm a request. After draft→ready or fix push, explicitly decide whether another pass is desired; if yes, ensure green/credibly green posture first.
119
122
 
120
- Branch on the `request-copilot-review.mjs` machine-readable result:
123
+ <!-- rule: COPILOT-FOLLOWUP-REQUEST-BRANCHING -->
124
+ `COPILOT-FOLLOWUP-REQUEST-BRANCHING`: The agent MUST branch on the `request-copilot-review.mjs` machine-readable result exactly as follows, and MUST NOT treat an attempted request as equivalent to a confirmed request:
121
125
  - `requested`: if another Copilot pass is actually desired, immediately re-baseline with `detect-copilot-loop-state.mjs` and follow its `nextAction` (enter persistent wait only through `dev-loops loop watch-cycle` or `gh run watch`)
122
126
  - `already-requested`: apply the same detector-first rebasing and wait branching as `requested`
123
127
  - `suppressed_same_head_clean`: report clean-converged state and stop unless `--force-rerequest-review` bypass is intentionally authorized
124
128
  - `unavailable`: report the limitation and stop
129
+ - `blocked_by_copilot_comment`: no request was placed. Delete the violating `violationCommentIds` (or confirm they only quote the rule inside a code span/fenced block, which does not arm the guard) and re-run; do NOT treat this as a placed request and do NOT enter a wait seam
125
130
  - non-zero / unexpected failure: stop and report error
126
131
 
127
- Do not treat an attempted request as equivalent to a confirmed request.
132
+ Branch on `status`, never on `ok`/exit-code truthiness alone: `ok: true` means the helper ran without error, not that a review was placed. Under `--silent`, the exit code is 0 only for `requested`; every other status (including `blocked_by_copilot_comment`) exits non-zero.
128
133
 
129
134
  ### Re-attachment guard (check for existing loop state first)
130
135
 
@@ -156,7 +161,8 @@ The outer-loop checkpoint is the canonical re-attachment artifact.
156
161
 
157
162
  Start every wait seam with a detector refresh: `detect-copilot-loop-state.mjs --repo <owner/name> --pr <number>`.
158
163
 
159
- Allowed wait tools: `detect-copilot-loop-state.mjs` (one-shot), `dev-loops loop watch-cycle` (persistent), `copilot-pr-handoff.mjs --watch-status` (refresh after timeout/idle), `dev-loops loop watch-ci --repo <owner/name> --pr <number>` (provider-agnostic CI: CircleCI / Actions / external commit-status), `gh run watch <run-id> --repo <owner/name>` (Actions-only fallback when the run id is known). Otherwise exit and resume later from fresh detector call.
164
+ <!-- rule: COPILOT-FOLLOWUP-WAIT-TOOLS -->
165
+ `COPILOT-FOLLOWUP-WAIT-TOOLS`: The agent MUST wait only through allowed deterministic tools: `detect-copilot-loop-state.mjs` (one-shot), `dev-loops loop watch-cycle` (persistent), `copilot-pr-handoff.mjs --watch-status` (refresh after timeout/idle), `dev-loops loop watch-ci --repo <owner/name> --pr <number>` (provider-agnostic CI: CircleCI / Actions / external commit-status), `scripts/github/wait-pr-checks.mjs --repo <owner/name> --pr <number>` (same provider-agnostic CI wait, with a direct 0/1/2 process-exit-code contract for shell/scripted callers instead of a JSON `status` field), `gh run watch <run-id> --repo <owner/name>` (Actions-only fallback when the run id is known); otherwise exit and resume later from a fresh detector call.
160
166
 
161
167
  Practical rules: do not poll manually. `waiting_for_copilot_review` → `run-watch-cycle.mjs` or report-and-resume. `waiting_for_ci` with pending/none CI → `dev-loops loop watch-ci --repo <owner/name> --pr <number>` (provider-agnostic; covers CircleCI / Actions / external commit-status) or report-and-resume; `gh run watch <run-id>` is an Actions-only fallback. `dev-loops loop watch-cycle` also auto-routes a `waiting_for_ci` boundary to this CI watcher. Bounded CI exception: zero current-head suites + previous-head green + local `npm run verify` passed → rerun detector with `--local-validation-head-sha` for `crediblyGreen` promotion. `ciStatus=failure` → stop/fix, never wait.
162
168
 
@@ -168,6 +174,7 @@ Preferred approach:
168
174
  - after a successful fix / reply-resolve / re-request cycle, returning to `waiting_for_copilot_review` is a persistence boundary: resume the watcher instead of reporting completion
169
175
  - dispatch fix findings to the `fixer` agent; do not run inline fix passes in-watcher
170
176
  - do not report completion while unresolved Copilot feedback remains
177
+ - once a watch/probe settles, do not parse its raw output: re-read via `detect-copilot-loop-state.mjs --repo <owner/name> --pr <number>` and `list-review-threads.mjs --repo <owner/name> --pr <number> --unresolved-only` — inline interpreters are barred by `OPS-NO-INLINE-INTERPRETER` in [Copilot loop operations](../docs/copilot-loop-operations.md)
171
178
 
172
179
  ### Canonical async dispatch wording
173
180
 
@@ -196,6 +203,7 @@ This step covers four responsibilities: the draft gate right before `gh pr ready
196
203
  When unresolved feedback exists, use a narrow follow-up loop:
197
204
 
198
205
  1. inspect unresolved comments/threads and failing checks
206
+ - enumerate unresolved threads (with the thread/comment ids the reply-resolve helpers below need) via `scripts/github/list-review-threads.mjs --repo <owner/name> --pr <number> --unresolved-only` rather than a hand-written `gh api graphql` query
199
207
  2. before the first local file write in each fixer pass on a Copilot-assigned PR, run `node <resolved-skill-scripts>/loop/pre-write-remote-freshness-guard.mjs --branch <headRefName>` as a required fail-closed guard
200
208
  - source `<headRefName>` from authoritative PR state (`headRefName`), not from a local branch guess
201
209
  - if the guard exits non-zero (`remote_ahead`), stop writing locally, reconcile to the refreshed remote head, then restart the fixer pass
@@ -209,7 +217,7 @@ When unresolved feedback exists, use a narrow follow-up loop:
209
217
  - source `<headRefName>` from authoritative PR state (`headRefName`), not from a local branch guess
210
218
  - if the guard exits non-zero (`branch_mismatch`), stop and realign to the expected branch before staging or committing
211
219
  7. if files changed, push the resolving commit before any thread reply claims the fix is present
212
- 8. when a comment or thread is actually addressed, reply on GitHub with a short resolution note that references the resolving commit SHA or commit URL when applicable
220
+ 8. <!-- rule: COPILOT-FOLLOWUP-REPLY-RESOLVE-HELPER --> `COPILOT-FOLLOWUP-REPLY-RESOLVE-HELPER`: When a comment or thread is actually addressed, the agent MUST reply on GitHub with a short resolution note that references the resolving commit SHA or commit URL when applicable, using the deterministic helpers below rather than ad hoc thread mutations:
213
221
  - for one thread, must use the deterministic helper `reply-resolve-review-thread.mjs` from the resolved skill scripts directory
214
222
  - when the same bounded resolution note applies to multiple matching unresolved threads, use `reply-resolve-review-threads.mjs` instead of ad hoc inline `gh api` / `gh api graphql` mutations
215
223
  - when using the single-thread helper, pair `--comment-id` and `--thread-id` from the same fresh PR thread snapshot rather than mixing ids across review rounds
@@ -217,27 +225,28 @@ When unresolved feedback exists, use a narrow follow-up loop:
217
225
  - when the intent is GitHub linkability, keep commit SHAs and issue/PR refs as plain text (for example 3ee82fc and owner/repo#70) and do not wrap them in backticks
218
226
  - keep backticks for actual code/path/CLI literals only
219
227
  - if either helper was newly added or recently changed, smoke-check it against one real thread before assuming the rest of the loop can rely on it
220
- 9. before resolving an addressed review thread, run a post-fix verification checkpoint
228
+ 9. <!-- rule: COPILOT-FOLLOWUP-VERIFY-BEFORE-RESOLVE --> `COPILOT-FOLLOWUP-VERIFY-BEFORE-RESOLVE`: before resolving an addressed review thread, run a post-fix verification checkpoint
221
229
  - confirm the GitHub reply actually exists on the intended thread/comment, not only in local notes or helper stdout
222
230
  - confirm the pushed current-head diff genuinely addresses the reviewer concern on the flagged lines or pattern; if the concern is only partially addressed, leave the thread open and explain what remains
223
231
  - refresh the API-backed thread snapshot via `dev-loops gate capture-threads` and use that refreshed data — including the unresolved thread count — for follow-up decisions rather than prose assumptions
224
232
  - if any verification check fails, do **not** resolve the thread; leave it open, add a short explanation when needed, and re-enter the fix/reply loop
225
- 10. resolve the addressed review thread only after the reply is attached successfully, the verification checkpoint passes, and the concern is genuinely addressed
233
+ 10. <!-- rule: COPILOT-FOLLOWUP-RESOLVE-AFTER-REPLY --> `COPILOT-FOLLOWUP-RESOLVE-AFTER-REPLY`: resolve the addressed review thread only after the reply is attached successfully, the verification checkpoint passes, and the concern is genuinely addressed
226
234
  - do not stop at a local fix if GitHub-side reply/resolve is authorized
227
235
  11. after completing reply/resolve for a pass, verify zero unresolved threads remain via `dev-loops gate capture-threads` before proceeding
228
236
  - if the refreshed snapshot reports unresolved threads, re-enter the reply/resolve loop for the missed threads
229
- 12. only after GitHub-side reply/resolve work is done for the addressed threads and the refreshed thread snapshot proves zero unresolved threads remain, decide whether another Copilot pass is desired
230
- - resolve the review-round cap from config via `resolveRefinementConfig(config, "maxCopilotRounds")` from `@dev-loops/core/config`; default config ships `maxCopilotRounds: 5`
237
+ 12. <!-- rule: COPILOT-FOLLOWUP-ROUND-CAP --> `COPILOT-FOLLOWUP-ROUND-CAP`: The agent MUST decide whether another Copilot pass is desired, applying the round-cap/signal-gating rules below, only after GitHub-side reply/resolve work is done for the addressed threads and the refreshed thread snapshot proves zero unresolved threads remain
238
+ - resolve the review-round cap from config via `resolveRefinementConfig(config, "maxCopilotRounds")` from `@dev-loops/core/config`; default config ships `maxCopilotRounds: 5`. For a light-dispatched PR, resolve `resolveEffectiveCopilotRoundCap(config, { lightweight: true })` instead — `min(localImplementation.lightMode.maxCopilotRounds ?? 1, maxCopilotRounds)` (default lightweight cap: 1) — see the [Artifact Authority Contract](../docs/artifact-authority-contract.md) lightweight section (issue #1210)
239
+ - for a light-dispatched PR, pass `--lightweight` on every round-cap-consuming helper invocation — `detect-copilot-loop-state.mjs`, `copilot-pr-handoff.mjs`, `detect-pr-gate-coordination-state.mjs`, `request-copilot-review.mjs`, and `upsert-checkpoint-verdict.mjs` — otherwise those tools resolve the full-PR cap and the composed lightweight cap is never enforced
231
240
  - **Opt out entirely:** `maxCopilotRounds: 0` disables the external Copilot review gate for the repo — the loop runs `draft_gate → pre_approval_gate` with the local harness only, never requesting or waiting on Copilot. Use this when the repo has no Copilot reviewer configured or prefers local-harness-only review.
232
241
  - use the completed Copilot review-round count from `detect-copilot-loop-state.mjs` / `copilot-pr-handoff.mjs` as the current PR's review-round count
233
- - if completed review rounds have reached the maximum (default: 5), do **not** re-request Copilot review within that concluded cycle
242
+ - if completed review rounds have reached the resolved round cap above, do **not** re-request Copilot review within that concluded cycle
234
243
  - if the loop already converged and then significant post-convergence changes land on a newer head (new/changed product or test logic, not doc/message/comment-only edits), treat that as a NEW cycle and re-request Copilot review when regular rounds are already > 0 (the prior cycle's cap does not suppress this new-cycle request)
235
244
  - when the round limit is reached **and** the refreshed thread snapshot proves zero unresolved threads **and** current-head CI is green or credibly green, treat that clean state as eligible for `pre_approval_gate` fallback instead of deadlocking on another Copilot rerequest
236
245
  - when using that fallback, add a short round-exhaustion note to the visible `pre_approval_gate` gate evidence so the PR records why no further Copilot rerequest occurred
237
246
  - if the round cap is reached before the PR is thread-clean or before CI is green/credibly green, reply-resolve any remaining intentionally deferred threads with a short `deferred to follow-up` note, then stop and report that the Copilot round limit was reached
238
247
  - **Signal-gated re-request suppression:** the `detect-copilot-loop-state.mjs` state machine classifies review-thread comments by signal level (High/Mid/Low). High-signal (bugs, security, contract violations) always re-requests; Low-signal (cosmetic nits) never re-requests. When low-signal detection is enabled and thresholds are met, the machine returns a low-signal-converged terminal state routing to `pre_approval_gate` without further re-requests. See [Copilot Loop Operations](../docs/copilot-loop-operations.md) for full signal-level semantics.
239
248
  - if that local validation is still known red, continue remediation instead of re-requesting Copilot
240
- - after a fix push advances the PR head SHA, re-run `detect-copilot-loop-state.mjs` for the new head and apply the [Copilot CI Status Contract](../docs/copilot-ci-status-contract.md). Previous-head CI is stale; only current-head results unblock CI-dependent steps. if GitHub CI/checks for the updated head are known red for a fixable issue, continue remediation instead of re-requesting Copilot. only once the updated head is green or credibly green, explicitly re-request Copilot review for the new head. Always use `request-copilot-review.mjs` — never `gh api POST repos/.../requested_reviewers` directly.
249
+ - after a fix push advances the PR head SHA, re-run `detect-copilot-loop-state.mjs` for the new head and apply the [Copilot CI Status Contract](../docs/copilot-ci-status-contract.md). Previous-head CI is stale; only current-head results unblock CI-dependent steps. if GitHub CI/checks for the updated head are known red for a fixable issue, continue remediation instead of re-requesting Copilot. <!-- rule: COPILOT-FOLLOWUP-REREQUEST-GREEN-GATE --> `COPILOT-FOLLOWUP-REREQUEST-GREEN-GATE`: only once the updated head is green or credibly green, explicitly re-request Copilot review for the new head. Always use `request-copilot-review.mjs` — never `gh api POST repos/.../requested_reviewers` directly.
241
250
  - only enter a wait/watch loop if the request result is confirmed as `requested` or `already-requested`
242
251
  - for `requested` / `already-requested`, immediately re-baseline with `detect-copilot-loop-state.mjs`; if the returned state is `waiting_for_copilot_review`, use `dev-loops loop watch-cycle` or stop/resume later, and if the returned state is `waiting_for_ci`, use `dev-loops loop watch-ci` (provider-agnostic CI wait; `gh run watch` is an Actions-only fallback) or stop/resume later after that single detector refresh
243
252
  - if the request result is `unavailable`, report that limitation and stop unless the user explicitly wants passive waiting anyway
@@ -250,7 +259,10 @@ Do not treat `fix applied locally` as the end of the loop when the workflow also
250
259
 
251
260
  ### Mandatory gate-comment command contract
252
261
 
253
- For every `draft_gate` or `pre_approval_gate` comment, you MUST run:
262
+ <!-- rule: COPILOT-FOLLOWUP-GATE-COMMENT-CANONICAL -->
263
+ `COPILOT-FOLLOWUP-GATE-COMMENT-CANONICAL`: For every `draft_gate` or `pre_approval_gate` comment, agents MUST run `upsert-checkpoint-verdict.mjs` and MUST NOT use `gh pr comment`, `gh api`, or `gh pr review` for gate comments.
264
+
265
+ Per `COPILOT-FOLLOWUP-GATE-COMMENT-CANONICAL` above, run:
254
266
 
255
267
  For a gate that ran via the fan-out/fan-in sub-loop, pass the structured per-angle review results via `--findings-json` (NOT the wall-of-text `--findings-summary`). The operator/loop writes that JSON file from the collected per-angle results — the same per-angle `{angle, verdict, findings}` objects the fan-out reviewers wrote to `tmp/gate-reviews/<repo-slug>/pr-<N>/<gate>-<headSha>/<angle>.json` that feed `consolidateFanin`. The helper renders a readable per-angle breakdown and derives the single-line `**Findings summary:**` digest itself:
256
268
 
@@ -284,19 +296,19 @@ node <resolved-skill-scripts>/github/upsert-checkpoint-verdict.mjs \
284
296
 
285
297
  `--execution-mode <fanout_fanin|inline_single_agent>` records how the gate review ran (default `inline_single_agent`). When the gate did not run via the fan-out/fan-in sub-loop ([Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md)), you MUST pass `--execution-mode inline_single_agent --inline-reason "<why>"` — silent inline runs are no longer allowed: inline mode requires a non-empty `--inline-reason` and emits a stderr warning. Because inline is the default mode, a bare call with neither flag now fails with an argument error, so always pass `--execution-mode` explicitly (and `--inline-reason` for inline). The recorded `executionMode` is surfaced by `detect-checkpoint-evidence.mjs` and gated by `gates.requireFanoutEvidence`.
286
298
 
287
- Do NOT use `gh pr comment`, `gh api`, or `gh pr review` for gate comments.
288
-
289
299
  `--force --force-reason` on `upsert-checkpoint-verdict.mjs` is a narrow operator-authorized CI override for the helper itself, not the default gate path. Use it only when the helper refuses gate entry solely because the current head is `blocked_needs_user_decision` with `ciStatus="failure"`, and only after the user explicitly authorizes ignoring that current-head CI failure for this one gate-comment upsert. It does **not** bypass stale-head checks, unresolved-thread / unsettled-review refusal, non-draft `draft_gate` refusal, merge conflicts, or other legality checks.
290
300
 
291
301
  ### Gate fan-out/fan-in procedure (agent-orchestrated)
292
302
 
293
- Both gates run this same checkpoint review chain. It is an **agent-orchestrated skill procedure** — a node script cannot spawn the per-angle reviewers, so the conductor agent drives the fan-out and uses the pure `@dev-loops/core/loop/gate-fanin` helpers only for consolidation, batching, and ledger mapping. The cost model is **build once, seed many**: the context-builder script builds ONE deterministic, neutral context bundle (diff + adjacent code) and each independent reviewer is seeded with that identical bundle verbatim instead of re-deriving it (work-dedup; no fork primitive, no Workflow tool). See [Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md).
303
+ Both gates run this same checkpoint review chain, owned end-to-end by [Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md) (`GATE-EXEC-BUILD-ONCE-SEED`, `GATE-EXEC-BRIEFING-PREFIX`, `GATE-EXEC-SEPARATE-CHAINS`, `GATE-EXEC-POST-BEFORE-FIX`, `GATE-EXEC-REGATE-MANDATORY`, `GATE-EXEC-LIGHT-ESCALATION`); this section owns only this skill's dispatch of that chain. It is an **agent-orchestrated skill procedure** — a node script cannot spawn the per-angle reviewers, so the conductor agent drives the fan-out and uses the pure `@dev-loops/core/loop/gate-fanin` helpers only for consolidation, batching, and ledger mapping.
294
304
 
295
- 1. **Context (Phase 1) — build the neutral bundle ONCE:** build/read the gate-context artifact via `scripts/github/write-gate-context.mjs` (`buildGateContext` resolves the angle set through `resolveGateAnglesDynamic`; mandatory angles always survive). The artifact at `tmp/gate-context/<repo-slug>/pr-<N>/<gate>-<headSha>.json` carries the resolved angles + change scope + acceptance-criteria pointer. When the resolver was handed a diff with `diffOutput`, `buildGateContext` also persists the FULL diff to `tmp/gate-context/<repo-slug>/pr-<N>/<gate>-<headSha>.diff` and records `scope.diffPath` (relative) plus `scope.changedFiles` (full repo-relative paths). It ADDITIONALLY builds a deterministic, neutral `adjacentCode` bundle — each changed file plus its 1-hop import in/out-edges (callers/callees/imports), with size guards (skip lockfiles/generated/binary/minified; cap per-file bytes; truncate the long tail) recorded in a `stripped`/`truncated`/`missing` manifest. This single bundle is the build-once seed; reviewers do not re-derive the diff + adjacent code.
296
- 2. **Fan-out (Phase 2) — seed each reviewer with the bundle verbatim:** plan batches with `planFanoutBatches(angles, cap)` using the `gates.maxFanoutReviewers` cap (default 8). Spawn one scoped `review` agent per resolved angle (plain Agent tool), parallel up to the cap, batching any overflow sequentially. When `degraded` is true, record the sequential degradation in the gate evidence. Reviewers are independent: each is seeded with the IDENTICAL neutral bundle (the gate-context artifact's `scope.diffPath` + `adjacentCode`) verbatim plus its single angle, and never inherits the conductor agent's conversation or opinions. Each reviewer follows the [review agent scoped angle-review mode](../../agents/review.agent.md): fresh context, single angle, read-only, writing its per-angle findings artifact to `tmp/gate-reviews/<repo-slug>/pr-<N>/<gate>-<headSha>/<angle>.json`. Brief each reviewer to use the provided bundle as its base and to review like an external code reviewer hunting real bugs: read the FULL diff (from `scope.diffPath`, or `git diff` when null) and the bundled adjacent code (callers, callees, imports) rather than re-deriving them, then review adversarially for concrete defects (edge cases, input validation, numeric coercion incl. NaN/Infinity/floats/negatives, null/undefined, boundary conditions, mismatched caller/callee contracts, dedup/identity bugs) with `file:line` + the failing scenario — not process nits like "no test exists". Reviewers are read-only and MAY widen scope (open adjacent repo files beyond the bundle) only when their angle genuinely needs more; that widening is recorded in the optional `contextWidened` field on each per-angle findings artifact.
297
- 3. **Fan-in (Phase 3):** consolidate the per-angle artifacts via `consolidateFanin({ angleResults, blockCleanOnFindingSeverities })` (blocking severities from `resolveGateConfig(config, <configKey>).blockCleanOnFindingSeverities`, where `<configKey>` is the config key for the gate — `draft` for `draft_gate`, `preApproval` for `pre_approval_gate` — not the `draft_gate|pre_approval_gate` artifact name). It yields the gate `verdict` (`clean` when no blocking-severity finding remains, `findings_present` when one does, `blocked` when any per-angle artifact is malformed/missing). Map consolidated findings with `toFindingsLogShape(...)` and write the disposition ledger via `write-gate-findings-log.mjs` **before** the visible comment. The ledger is written regardless of any comment opt-out. Then, when `resolveGatePostFindingsComments(config)` is true (default; opt-out via `gates.postFindingsComments: false`), post the consolidated findings as a visible, marker-tagged PR comment via `node <resolved-skill-scripts>/github/post-gate-findings.mjs --repo <owner/name> --pr <number> --gate <gate> --head-sha <current_head_sha> --findings '<toFindingsLogShape JSON>'` so the findings are auditable and Copilot/humans are aware of them. The helper is idempotent per gate (exactly one comment per gate, updated in place on each run; the reviewed head is shown in the body) and posts a brief "no findings" note when the consolidated set is empty. When `postFindingsComments` is false the helper no-ops with a `skipped` result and you skip this post step; the ledger still records the findings.
298
- 4. **Verdict (Phase 4):** post the verdict with the [Gate comment command](#mandatory-gate-comment-command-contract) using `--execution-mode fanout_fanin` and `--findings-json <path>`. Write that JSON from the collected per-angle results (the same per-angle `{angle, verdict, findings}` artifacts at `tmp/gate-reviews/<repo-slug>/pr-<N>/<gate>-<headSha>/<angle>.json` that fed `consolidateFanin` in Phase 3; the flat `toFindingsLogShape` output is also accepted and grouped by `.angle`) so the verdict comment renders the structured per-angle breakdown instead of a wall-of-text summary. Do NOT use `--findings-summary` for a fan-out gate that is the inline_single_agent fallback.
299
- 5. **Retry (Phase 5):** on blocking findings, drive each internal fan-out finding through the SAME fix reply-with-resolving-commit resolve loop used for external Copilot review comments (Step 7 above): fix on the branch, push the resolving commit, then re-run only the `findings_present` angles from the previous pass (the context-builder and fan-in always re-run); repeat until the consolidated verdict is `clean` for the current head SHA. Internal fan-out findings and external review comments are handled identically by that loop.
305
+ 1. **Context (Phase 1):** build/read the gate-context artifact via `scripts/github/write-gate-context.mjs` (`buildGateContext` resolves the angle set through `resolveGateAnglesDynamic`; mandatory angles always survive). Consume the resulting `tmp/gate-context/<repo-slug>/pr-<N>/<gate>-<headSha>.json` artifact: the FULL diff at `scope.diffPath` plus the top-level adjacent-code bundle (`adjacentCode`) are the build-once seed every reviewer uses verbatim see `GATE-EXEC-BUILD-ONCE-SEED` for the bundle contract; do not re-derive them per reviewer.
306
+ 2. **Fan-out (Phase 2):** plan batches with `planFanoutBatches(angles, cap)` using the `gates.maxFanoutReviewers` cap (default 8); spawn one scoped `review` agent per resolved angle (plain Agent tool), parallel up to the cap, sequential for overflow (record any degradation in the gate evidence). Each reviewer follows [review agent scoped angle-review mode](../../agents/review.agent.md): fresh context, single angle, read-only, writing its per-angle findings artifact to `tmp/gate-reviews/<repo-slug>/pr-<N>/<gate>-<headSha>/<angle>.json`.
307
+ <!-- rule: COPILOT-FOLLOWUP-ADVERSARIAL-BRIEFING -->
308
+ `COPILOT-FOLLOWUP-ADVERSARIAL-BRIEFING`: Each reviewer MUST be briefed to review like an external code reviewer hunting real bugs: read the FULL diff (from `scope.diffPath`, or `git diff` when null) and the bundled adjacent code (callers, callees, imports) rather than re-deriving them, then review adversarially for concrete defects (edge cases, input validation, numeric coercion incl. NaN/Infinity/floats/negatives, null/undefined, boundary conditions, mismatched caller/callee contracts, dedup/identity bugs) with `file:line` + the failing scenario not process nits like "no test exists". Reviewers MAY widen scope (open adjacent repo files beyond the bundle) only when their angle genuinely needs more, recording that in the optional `contextWidened` field on their findings artifact. The LAYOUT of this briefing (invariant block first, this adversarial angle prompt last) and the `--prefix-hash`/`--prefix-file` sentinel recording are owned by `GATE-EXEC-BRIEFING-PREFIX` not restated here.
309
+ 3. **Fan-in (Phase 3):** before consolidating, run `scripts/github/verify-briefing-prefixes.mjs --head-sha <current_head_sha>` and stop the pass on a fail-closed result (`GATE-EXEC-BRIEFING-PREFIX` owns this check). Then consolidate via `consolidateFanin({ angleResults, blockCleanOnFindingSeverities })` (blocking severities from `resolveGateConfig(config, <configKey>).blockCleanOnFindingSeverities` `draft` or `preApproval`, not the `<gate>` artifact name). Map with `toFindingsLogShape(...)` and write the disposition ledger via `write-gate-findings-log.mjs` before the visible comment (`GATE-EXEC-DISPOSITION-LEDGER`, `GATE-EXEC-POST-BEFORE-FIX` own that ordering and the ledger's opt-out-proof durability). When `resolveGatePostFindingsComments(config)` is true (default), post via `node <resolved-skill-scripts>/github/post-gate-findings.mjs --repo <owner/name> --pr <number> --gate <gate> --head-sha <current_head_sha> --findings '<toFindingsLogShape JSON>'`.
310
+ 4. **Verdict (Phase 4):** post via the [Gate comment command](#mandatory-gate-comment-command-contract) using `--execution-mode fanout_fanin` and `--findings-json <path>` built from the same per-angle artifacts consolidated in Phase 3 — that flag's accepted shapes are owned by the Gate comment command section above; do not restate them here.
311
+ 5. **Retry (Phase 5):** on blocking findings, drive each internal fan-out finding through the SAME fix → reply-with-resolving-commit → resolve loop used for external Copilot review comments (Step 7 above); re-run only the `findings_present` angles from the previous pass (context-builder and fan-in always re-run); repeat until the consolidated verdict is `clean` for the current head SHA (`GATE-EXEC-REGATE-MANDATORY`).
300
312
 
301
313
  ### Draft gate contract (before marking PR ready for review)
302
314
 
@@ -311,8 +323,8 @@ The canonical checkpoint verdict comment contract is [Gate Review Comment Contra
311
323
  - **Pass criteria:** all configured draft gate angles pass; all findings at severities in `blockCleanOnFindingSeverities` are addressed; validation passes; no unrelated files are included.
312
324
  - **Next step after passing:** mark the PR ready for review.
313
325
  - **Board status sync (built-in, after ready-for-review):** the In-Progress board move is now performed automatically as a deterministic tail of `ready-for-review.mjs` — marking the PR ready couples the board move to the ready transition (#1069), so no separate `sync-item-status` step is needed. It stays best-effort and NON-FATAL: it uses local `gh` auth (no CI/PAT), exits 0 when the board is not configured / the item is not on the board / the API fails, and never blocks marking the PR ready.
314
- - **Non-substitution rule:** a clean `draft_gate` comment only authorizes the draft → ready-for-review transition for that head SHA. It does **not** satisfy `pre_approval_gate`, final-approval readiness, or merge-ready requirements.
315
- - **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract). Keep validation reporting concise: include command names with pass/fail status. Do **not** paste raw passing test output into the visible gate comment. If you include a failing validation excerpt, keep it focused and truncate it to a deterministic retained-prefix length before posting the comment. See [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). Do not run `gh pr ready` unless a visible `clean` `draft_gate` checkpoint verdict comment exists for the current head SHA. A checkpoint verdict comment for an older head SHA does not satisfy this requirement for the current head. If findings exist, the PR stays draft and needs fixes before retry. If fixes advance the head SHA while still draft, post a new checkpoint verdict comment for the new head. If the checkpoint verdict comment cannot be posted, fail closed and do not run `gh pr ready`.
326
+ - **Non-substitution rule:** a clean `draft_gate` comment only authorizes the draft → ready-for-review transition for that head SHA; cross-gate non-substitution is owned by `GATE-COMMENT-NON-SUBSTITUTION` in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate that rule.
327
+ - **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract). Comment field content and validation-reporting format are owned by `GATE-COMMENT-VALIDATION-REPORTING`; the draft-boundary comment requirement is owned by `GATE-COMMENT-DRAFT-REQUIREMENTS`; posting-failure fail-closed behavior is owned by `GATE-COMMENT-FAIL-CLOSED` all in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate those field/format/fail-closed rules.
316
328
 
317
329
  ### Pre-approval gate contract
318
330
 
@@ -323,11 +335,12 @@ This is the default pre-approval gate for this workflow boundary. The canonical
323
335
  - **Execution directive:** run the [Gate fan-out/fan-in procedure](#gate-fan-outfan-in-procedure-agent-orchestrated) (the agent-orchestrated chain defined in [Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md)) with the pre-approval gate inspection angles resolved from config. The `acceptance-criteria` angle is mandatory for this gate (see `.devloops` `gates.preApproval.mandatoryAngles`) and always survives dynamic resolution. Retry rule: in subsequent cycles, only re-run reviewers that produced `findings_present` in the previous pass.
324
336
  - **Review angles:** resolved at runtime from config via `resolveGateAngles(config, "preApproval")` from `@dev-loops/core/config`. Default config enables all configured pre-approval gate angle families; consumer repos may opt out individual angles via `excludeAngles`.
325
337
  - **Persona mapping:** each angle resolves to a reviewer persona via `resolveReviewerRole(config, angle)` from `@dev-loops/core/config`. Include this prompt in each reviewer's briefing so the reviewer knows exactly what to look for.
326
- - **Pass criteria:** the sub-loop completes with verdict `clean`; all configured angles pass; if parallel execution is impractical, still run all configured lenses and explicitly record the limitation.
338
+ - **Pass criteria:** the sub-loop completes with verdict `clean`; all configured angles pass, following the sequential-fallback rule owned by `GATE-EXEC-FANOUT-SEQUENTIAL-FALLBACK` in [Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md).
327
339
  - **Acceptance criteria verification:** follow the canonical procedure in [Acceptance Criteria Verification](../docs/acceptance-criteria-verification.md) before posting the `pre_approval_gate` comment. After a clean verification this also ticks the verified PR-body checkboxes via `scripts/github/tick-verified-checkboxes.mjs`, so the merged PR shows checked AC.
328
340
  - **Next step after passing:** continue the Step 7 flow and then proceed to the human approval checkpoint below.
329
- - **Non-substitution rule:** a clean `pre_approval_gate` comment is separate from `draft_gate` evidence. It governs final-approval readiness for that head SHA; it does **not** replace the required `draft_gate` evidence for leaving draft.
330
- - **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract). Keep validation reporting concise: include command names with pass/fail status. Do **not** paste raw passing test output into the visible gate comment. If you include a failing validation excerpt, keep it focused and truncate it to a deterministic retained-prefix length before posting the comment. Do not declare final-approval readiness unless a visible `clean` `pre_approval_gate` checkpoint verdict comment exists for the current head SHA. Final-approval readiness must not rely only on local or hidden artifacts; the visible PR comment is the required auditable evidence. If the checkpoint verdict comment cannot be posted, fail closed and do not declare final-approval readiness. A checkpoint verdict comment for an older head SHA does not satisfy this requirement for the current head. If findings exist, follow-up fixes are required before final approval. The `pre_approval_gate` procedure must be entered and completed (visible comment posted) before any merge-ready or approval-ready declaration. Skipping the gate is not recoverable by asserting convergence. If fixes advance the head SHA, post a new checkpoint verdict comment for the new head.
341
+ - **Non-substitution rule:** a clean `pre_approval_gate` comment governs final-approval readiness for that head SHA and is separate from `draft_gate` evidence; cross-gate non-substitution is owned by `GATE-COMMENT-NON-SUBSTITUTION` in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate that rule.
342
+ - **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract). Comment field content and validation-reporting format are owned by `GATE-COMMENT-VALIDATION-REPORTING`; the pre-approval-boundary comment requirement is owned by `GATE-COMMENT-PREAPPROVAL-REQUIREMENTS`; posting-failure fail-closed behavior is owned by `GATE-COMMENT-FAIL-CLOSED` all in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate those field/format/fail-closed rules.
343
+ - <!-- rule: GATE-SKIP-NOT-RECOVERABLE-BY-CONVERGENCE --> `GATE-SKIP-NOT-RECOVERABLE-BY-CONVERGENCE`: Skipping the gate MUST NOT be treated as recoverable by asserting convergence.
331
344
 
332
345
  ### Conflict-resolution gate
333
346
 
@@ -433,6 +446,7 @@ Follow [Stop Conditions](../docs/stop-conditions.md). Genuine stops: `blocked` s
433
446
 
434
447
  See [Anti-patterns](../docs/anti-patterns.md). Key repo-specific additions:
435
448
  - Use `reply-resolve-review-thread.mjs` / `reply-resolve-review-threads.mjs` helpers instead of ad hoc `gh api`/`gh api graphql` thread-mutation commands. Do NOT use `gh pr comment`, `gh api`, or `gh pr review` for gate comments (use `upsert-checkpoint-verdict.mjs`).
449
+ - Use `list-review-threads.mjs` and `wait-pr-checks.mjs` instead of ad hoc `gh api graphql` review-thread queries or `gh pr checks` shell-pipe polling loops.
436
450
  - Do not declare merge-ready without visible `pre_approval_gate` comment on current head SHA. Do not declare merge-ready based solely on `mergeable_state: clean` + CI green without gate evidence. CI green + resolved threads alone is insufficient.
437
451
  - Do not blind-run `gh pr merge`/`gh pr update-branch`/unapproved rebase when conflicted. Do not dispatch async dev-loop tasks that omit the pre-approval gate requirement.
438
452
  - Do not assume generated wiki is authoritative over code or CI.