dev-loops 0.7.2 → 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 (71) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +1 -1
  3. package/.claude/agents/refiner.md +2 -2
  4. package/.claude/agents/review.md +9 -12
  5. package/.claude/commands/loop-continue.md +2 -1
  6. package/.claude/commands/loop-enqueue.md +8 -1
  7. package/.claude/commands/loop-info.md +1 -1
  8. package/.claude/hooks/_bash-command-classify.mjs +7 -6
  9. package/.claude/hooks/_hook-decisions.mjs +5 -4
  10. package/.claude/skills/copilot-pr-followup/SKILL.md +5 -5
  11. package/.claude/skills/dev-loop/SKILL.md +7 -7
  12. package/.claude/skills/docs/acceptance-criteria-verification.md +12 -4
  13. package/.claude/skills/docs/anti-patterns.md +3 -3
  14. package/.claude/skills/docs/artifact-authority-contract.md +6 -4
  15. package/.claude/skills/docs/confirmation-rules.md +1 -1
  16. package/.claude/skills/docs/copilot-loop-operations.md +4 -2
  17. package/.claude/skills/docs/issue-intake-procedure.md +12 -3
  18. package/.claude/skills/docs/merge-preconditions.md +5 -3
  19. package/.claude/skills/docs/pr-lifecycle-contract.md +1 -1
  20. package/.claude/skills/docs/public-dev-loop-contract.md +2 -1
  21. package/.claude/skills/docs/retrospective-checkpoint-contract.md +9 -6
  22. package/.claude/skills/docs/validation-policy.md +1 -1
  23. package/.claude/skills/local-implementation/SKILL.md +3 -3
  24. package/.claude/skills/loop-grill/SKILL.md +34 -14
  25. package/AGENTS.md +1 -1
  26. package/CHANGELOG.md +30 -0
  27. package/README.md +95 -189
  28. package/agents/refiner.agent.md +2 -2
  29. package/agents/review.agent.md +9 -12
  30. package/extension/README.md +5 -4
  31. package/package.json +5 -4
  32. package/scripts/docs/validate-state-machine-conformance.mjs +78 -1
  33. package/scripts/github/_gate-names.mjs +5 -0
  34. package/scripts/github/capture-review-threads.mjs +2 -2
  35. package/scripts/github/detect-checkpoint-evidence.mjs +7 -7
  36. package/scripts/github/edit-issue.mjs +259 -0
  37. package/scripts/github/probe-ci-status.mjs +18 -0
  38. package/scripts/github/probe-copilot-review.mjs +24 -3
  39. package/scripts/github/reconcile-draft-gate.mjs +13 -13
  40. package/scripts/github/request-copilot-review.mjs +17 -16
  41. package/scripts/github/upsert-checkpoint-verdict.mjs +25 -23
  42. package/scripts/github/verify-briefing-prefixes.mjs +224 -33
  43. package/scripts/github/write-gate-context.mjs +8 -4
  44. package/scripts/loop/_post-convergence-change.mjs +2 -2
  45. package/scripts/loop/_pr-runner-coordination.mjs +112 -13
  46. package/scripts/loop/check-retro-tooling.mjs +14 -9
  47. package/scripts/loop/copilot-pr-handoff.mjs +18 -14
  48. package/scripts/loop/detect-copilot-loop-state.mjs +11 -11
  49. package/scripts/loop/detect-internal-only-pr.mjs +6 -6
  50. package/scripts/loop/detect-pr-gate-coordination-state.mjs +117 -15
  51. package/scripts/loop/detect-refinement-grill-state.mjs +136 -0
  52. package/scripts/loop/run-watch-cycle.mjs +42 -7
  53. package/scripts/loop/sanctioned-commands.mjs +1 -0
  54. package/scripts/pages/build-state-atlas.mjs +15 -0
  55. package/scripts/projects/add-queue-item.mjs +87 -4
  56. package/skills/copilot-pr-followup/SKILL.md +5 -5
  57. package/skills/dev-loop/SKILL.md +2 -2
  58. package/skills/docs/acceptance-criteria-verification.md +12 -4
  59. package/skills/docs/anti-patterns.md +3 -3
  60. package/skills/docs/artifact-authority-contract.md +6 -4
  61. package/skills/docs/confirmation-rules.md +1 -1
  62. package/skills/docs/copilot-loop-operations.md +4 -2
  63. package/skills/docs/issue-intake-procedure.md +12 -3
  64. package/skills/docs/merge-preconditions.md +5 -3
  65. package/skills/docs/pr-lifecycle-contract.md +1 -1
  66. package/skills/docs/public-dev-loop-contract.md +2 -1
  67. package/skills/docs/required-rules.json +17 -1
  68. package/skills/docs/retrospective-checkpoint-contract.md +9 -6
  69. package/skills/docs/validation-policy.md +1 -1
  70. package/skills/local-implementation/SKILL.md +3 -3
  71. package/skills/loop-grill/SKILL.md +38 -17
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-loops",
3
- "version": "0.7.2",
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",
@@ -23,7 +23,7 @@ The envelope is the primary handoff artifact — it is derived from resolver out
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.2 loop startup --issue <n>` for issues, or `npx dev-loops@0.7.2 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.
@@ -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
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
@@ -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
@@ -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,8 +14,15 @@ 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.
@@ -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.2 loop info --issue $ARGUMENTS`; if it is a PR number, run `npx dev-loops@0.7.2 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.
@@ -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,
@@ -225,12 +225,12 @@ When unresolved feedback exists, use a narrow follow-up loop:
225
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
226
226
  - keep backticks for actual code/path/CLI literals only
227
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
228
- 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
229
229
  - confirm the GitHub reply actually exists on the intended thread/comment, not only in local notes or helper stdout
230
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
231
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
232
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
233
- 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
234
234
  - do not stop at a local fix if GitHub-side reply/resolve is authorized
235
235
  11. after completing reply/resolve for a pass, verify zero unresolved threads remain via `dev-loops gate capture-threads` before proceeding
236
236
  - if the refreshed snapshot reports unresolved threads, re-enter the reply/resolve loop for the missed threads
@@ -239,14 +239,14 @@ When unresolved feedback exists, use a narrow follow-up loop:
239
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
240
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.
241
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
242
- - 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
243
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)
244
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
245
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
246
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
247
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.
248
248
  - if that local validation is still known red, continue remediation instead of re-requesting Copilot
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. 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.
250
250
  - only enter a wait/watch loop if the request result is confirmed as `requested` or `already-requested`
251
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
252
252
  - if the request result is `unavailable`, report that limitation and stop unless the user explicitly wants passive waiting anyway
@@ -25,7 +25,7 @@ Required installed runtime contract docs are shared bundled copies under `../doc
25
25
 
26
26
  > Under the Claude Code harness the dev-loop runs as a single agent: run these steps directly — no read-only boundary and no separate async-subagent dispatch. See [Main Agent Contract](../docs/main-agent-contract.md).
27
27
 
28
- Resolve authoritative state via the startup resolver (`npx dev-loops@0.7.2 loop startup --issue <n>` for issues, `npx dev-loops@0.7.2 loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops@0.7.2 loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
28
+ Resolve authoritative state via the startup resolver (`npx dev-loops@0.8.0 loop startup --issue <n>` for issues, `npx dev-loops@0.8.0 loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops@0.8.0 loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
29
29
 
30
30
  **Retrospective checkpoint gate:** the resolver reads `.pi/dev-loop-retrospective-checkpoint.json` and injects the state. When the checkpoint is `missing` and the repo config `workflow.requireRetrospective` (set via `.devloops` at repo root) is `true`, the resolver returns `needs_reconcile`. Complete or explicitly skip the retrospective before starting.
31
31
 
@@ -99,10 +99,10 @@ When `@dev-loops/core` is available again, switch back to the full helper. The f
99
99
 
100
100
  ## Read-only info shortcut
101
101
 
102
- Info/handoff requests can be served directly via `npx dev-loops@0.7.2 loop info` (read-only; no full dev-loop run required):
103
- - `npx dev-loops@0.7.2 loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
104
- - `npx dev-loops@0.7.2 loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
105
- - `npx dev-loops@0.7.2 loop info --issue <n> --json` — machine-readable JSON output
102
+ Info/handoff requests can be served directly via `npx dev-loops@0.8.0 loop info` (read-only; no full dev-loop run required):
103
+ - `npx dev-loops@0.8.0 loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
104
+ - `npx dev-loops@0.8.0 loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
105
+ - `npx dev-loops@0.8.0 loop info --issue <n> --json` — machine-readable JSON output
106
106
 
107
107
  ## Reading tool output (token-economical convention)
108
108
 
@@ -130,9 +130,9 @@ When you need a fact from a dev-loops JSON-emitting script, climb this ladder an
130
130
 
131
131
  **Inline-first rule:** Prefer inline commands over nested async delegation when managing a single PR. Use nested delegation only for parallel fan-out or when the parent needs to continue other work.
132
132
 
133
- **Bounded async task contract:** Break work into discrete tasks with clear inputs, explicit outputs, bounded scope. No shell polling — use `run-watch-cycle.mjs` or `gh run watch`. For fan-out reviewer waits (gate sub-loops or any Agent-tool fan-out), await completion via the harness completion notification or the reviewer's findings artifact at its deterministic path, then join via `consolidateFanin` — never tail/parse a subagent transcript, never `node -e`/`python3`-parse tool JSON, never `sleep`-poll (see the [fan-in wait anti-pattern](../docs/anti-patterns.md) and [Checkpoint Review Chain Contract](../../docs/gate-review-sub-loop-contract.md) Phase 2).
133
+ **Bounded async task contract:** Break work into discrete tasks with clear inputs, explicit outputs, bounded scope. No shell polling — use `run-watch-cycle.mjs` or `gh run watch`. Fan-out reviewer waits (gate sub-loops or any Agent-tool fan-out) follow `ANTIPATTERN-FANIN-WAIT` in [Anti-patterns](../docs/anti-patterns.md): await completion via the harness notification or the reviewer's findings artifact at its deterministic path and join via `consolidateFanin` — never transcript-tail, `node -e`/`python3`-parse tool JSON, or `sleep`-poll.
134
134
 
135
- **Round-cap budget check (enforced):** After every watch cycle, fix pass, or reply-resolve, check whether completed Copilot review rounds have reached the maximum (default: 5). Stop re-requesting Copilot review when the limit is reached **within that review cycle**. Exception: 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), open a new Copilot review cycle and re-request review when regular rounds are already > 0, even if the previous cycle hit the cap. Read these gate-cadence facts via the token-economical convention above (`run-watch-cycle.mjs --concise`, or `--jq`/`--silent` for a single field/predicate) — never `| python3` or `node -e`.
135
+ **Round-cap budget check (enforced):** After every watch cycle, fix pass, or reply-resolve, check whether completed Copilot review rounds have reached the resolved round cap (`refinement.maxCopilotRounds`, default 5; light-dispatched PRs resolve the lower `resolveEffectiveCopilotRoundCap`, default 1 — owned by `COPILOT-FOLLOWUP-ROUND-CAP`). Stop re-requesting Copilot review when the limit is reached **within that review cycle**. Exception: the post-convergence new-cycle re-request carve-out (a converged loop that later takes significant post-convergence changes on a newer head opens a new cycle even if the previous one hit the cap) is owned by `COPILOT-FOLLOWUP-ROUND-CAP`. Read these gate-cadence facts via the token-economical convention above (`run-watch-cycle.mjs --concise`, or `--jq`/`--silent` for a single field/predicate) — never `| python3` or `node -e`.
136
136
 
137
137
  ## Shorthand issue-based auto trigger contract
138
138
 
@@ -5,9 +5,17 @@ Canonical owner for the acceptance-criteria verification procedure run during th
5
5
  ## Procedure
6
6
 
7
7
  <!-- rule: ACCEPT-CRITERIA-VERIFY-AND-REFLECT -->
8
- `ACCEPT-CRITERIA-VERIFY-AND-REFLECT`: Before posting the `pre_approval_gate` comment, the agent MUST verify every acceptance criteria checklist item in the issue linked to this PR, and MUST reflect the verified items back into both the linked issue body and the PR body (exception: under the lightweight path `specSource: pr_body`the step 5 issue-body mirroring is skipped, since the PR body, not the issue body, is the canonical spec surface; see the lightweight fork below):
9
-
10
- > **Lightweight (PR-body-as-spec) fork:** when the session is lightweight — the resolver output / handoff envelope carries `specSource: pr_body` (`canonicalSpecSource: pr_body`) — a linked issue still exists (`--lightweight` runs on the `--issue` path, so keep the `Closes #N` linkage and all issue-level tracking), but the **PR body**, not the issue body, is the canonical spec-of-record read for AC/DoD. In that case, still resolve the linked issue for tracking, but read the AC/DoD/invariants from the PR body rather than the issue body (skip step 2's issue-body read for spec purposes). Run `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <pr-number> --expected-issue <issue-number>` (reuses `@dev-loops/core/loop/issue-refinement-artifact`) to confirm the body carries the required invariants — it fails closed with a per-section `missing_*` reason if any is absent, in which case post the gate comment with verdict `blocked`. The PR body MUST carry the `Closes #N` linkage (or GitHub's other closing-keyword forms): `validate-pr-body-spec` fails closed with `missing_closing_issue_reference` without it, and with `closes_wrong_issue` when `--expected-issue` doesn't match. Then continue at step 3, extracting the **Acceptance criteria** checklist items from the PR body itself; step 5's issue-body AC-tick update is unnecessary for lightweight since the issue body is not the spec surface (the PR body is ticked in step 6 as usual). The default issue-backed procedure below is unchanged.
8
+ `ACCEPT-CRITERIA-VERIFY-AND-REFLECT`: Before posting the `pre_approval_gate` comment, the agent MUST verify every acceptance criteria checklist item in the issue linked to this PR, and MUST reflect the verified items back into both the linked issue body and the PR body (exception: when the linked issue body is not the spec-of-record under a lightweight session (tracker-backed or issue-less; the PR body is the spec) or a plan-file promotion session (the committed plan doc the PR body points to is the spec) — the step 5 issue-body mirroring is skipped; see the fork below):
9
+
10
+ > **Non-tracker spec-source fork:** when the linked issue body is not the spec-of-record, the fork covers three arms lightweight (the PR body itself is the spec; splits on whether a backing issue exists) and plan-file promotion (a distinct local-planning origin whose committed plan doc, pointed to from the PR body, is the spec). Handle each arm as below.
11
+ >
12
+ > **Tracker-backed lightweight** (`--issue --lightweight`): a linked issue exists, so keep the `Closes #N` linkage and all issue-level tracking, but the **PR body**, not the issue body, is the canonical spec-of-record read for AC/DoD. Still resolve the linked issue for tracking, but read the AC/DoD/invariants from the PR body rather than the issue body (skip step 2's issue-body read for spec purposes). Run `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <pr-number> --expected-issue <issue-number>` (reuses `@dev-loops/core/loop/issue-refinement-artifact`) to confirm the body carries the required invariants — it fails closed with a per-section `missing_*` reason if any is absent, in which case post the gate comment with verdict `blocked`. The PR body MUST carry the `Closes #N` linkage (or GitHub's other closing-keyword forms): `validate-pr-body-spec` fails closed with `missing_closing_issue_reference` without it, and with `closes_wrong_issue` when `--expected-issue` doesn't match. Then continue at step 3, extracting the **Acceptance criteria** checklist items from the PR body itself; step 5's issue-body AC-tick update is unnecessary since the issue body is not the spec surface (the PR body is ticked in step 6 as usual).
13
+ >
14
+ > **Issue-less lightweight** (`--lightweight` alone): no linked issue exists — by design, not a gap. There is nothing to resolve at step 1 and nothing to read or mirror at steps 2 and 5; skip all three. Read AC/DoD/invariants directly from the PR body and run `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <pr-number> --no-issue`, which fails closed with a per-section `missing_*` reason for any absent invariant and with `unexpected_closing_issue_reference` if the body carries a closing reference to an issue that doesn't back it (a closing reference MUST NOT be present). On any validation failure, post the gate comment with verdict `blocked`. Step 1's "zero candidates → blocked" rule is scoped to tracker-backed sessions; for a deliberately issue-less session, zero closing references is the expected state, not a blocked condition. Continue at step 3 against the PR body, then step 4 and 6 as usual.
15
+ >
16
+ > **Plan-file promotion (P4) — a distinct local-planning origin, not a lightweight sub-arm:** no linked issue exists — similar to the issue-less lightweight arm, there is nothing to resolve at step 1 and nothing to read or mirror at steps 2 and 5; skip all three. AC/DoD live in the PR body, copied from the committed plan doc that is the spec-of-record; read them from the PR body as usual.
17
+ >
18
+ > The default issue-backed procedure below is unchanged.
11
19
 
12
20
  1. **Resolve the linked issue number deterministically:** use `gh pr view <pr-number> --repo <owner/name> --json closingIssuesReferences,body` and apply this decision tree: if there is exactly one closing issue reference, use it; else if there is exactly one PR-body `Closes #N` / `Fixes #N` pattern, use it; otherwise (zero or multiple candidates), post the gate comment with verdict `blocked` (gate cannot complete deterministically) rather than guessing.
13
21
 
@@ -17,7 +25,7 @@ Canonical owner for the acceptance-criteria verification procedure run during th
17
25
 
18
26
  4. **Verify each AC item** against the proposed changes on the current PR head.
19
27
 
20
- 5. **Update the issue body once:** compute the fully-updated issue body by replacing each verified item's `- [ ]` with `- [x]`, write it to a temporary file, and perform a single `gh issue edit <issue-number> --body-file <tmp-file> --repo <owner/name>`. Do not issue one edit per item; prefer `--body-file` over inline `--body` to avoid shell quoting/escaping hazards.
28
+ 5. **Update the issue body once:** compute the fully-updated issue body by replacing each verified item's `- [ ]` with `- [x]`, write it to a temporary file, and perform a single `node scripts/github/edit-issue.mjs --repo <owner/name> --issue <issue-number> --body-file <tmp-file>`. Do not issue one edit per item; prefer `--body-file` over inline `--body` to avoid shell quoting/escaping hazards.
21
29
 
22
30
  6. **Tick the PR body once:** after the verification is clean, flip each verified item's `- [ ]` to `- [x]` in the PR body via a single `gh pr edit --body-file` update, so the merged PR shows checked AC/DoD. Run `node scripts/github/tick-verified-checkboxes.mjs --repo <owner/name> --pr <pr-number> --verified <exact label>...` (one edit; exact-label match; only items actually verified are ticked; unverified or deferred items stay `- [ ]`; fail closed). This runs automatically as part of the `pre_approval_gate`, not by memory.
23
31
 
@@ -6,18 +6,18 @@ Canonical owner for anti-pattern guidance across all workflow families.
6
6
 
7
7
  1. **Skipping fan-out/fan-in for non-trivial changes**: Do not implement directly without refinement when scope exceeds light-mode thresholds.
8
8
  2. **Routing review-only work through local_implementation**: Review-only comparison, synthesis, or consolidation must use `refiner` agent, not `dev-loop` + `local_implementation`.
9
- 3. **Thin placeholder PR descriptions**: Always include change summary, scope/context, acceptance criteria, definition of done, non-goals, and `Closes #N`.
9
+ 3. **Thin placeholder PR descriptions**: Always include change summary, scope/context, acceptance criteria, definition of done, non-goals, and `Closes #N` (tracker-backed; issue-less lightweight and plan-file promotion PRs instead follow [merge precondition 7](merge-preconditions.md#required-before-merge)).
10
10
  4. **Merging directly to main without PR**: Use PR-based remote loop when practical.
11
11
  5. **Duplicate worktree paths**: See `WORKTREE-DEDUPE` in [Worktree usage guidance](../../docs/worktree-guidance.md#coordination-and-collision-checks).
12
12
  6. **Main-checkout mutation**: See `WORKTREE-DEFAULT-USE` in [Worktree usage guidance](../../docs/worktree-guidance.md#default-rule-use-a-worktree-for-mutating-local-work).
13
13
  7. **Spelunking tooling internals instead of using the public surface**: Do not read installed package internals, scan tooling source, or run ad-hoc scripts to understand a tool's behavior. Use the CLI, its `--help` subcommands, and `skills/docs/`. Read tool source only when the task is to inspect or change that tool, or when a concrete failure path is inside it and no public CLI/docs path exists. Once a failure is concrete, search changed files for the exact pattern — don't run duplicate broad searches.
14
14
  8. **Hand-editing the queue file when a board is configured**: When a GitHub Projects board is configured (`queue.projectNumber`/`queue.boardTitle` in `.devloops`), the board is the authoritative queue MEMBERSHIP and ordering source — not just status. Add work via the board (`dev-loops queue add ... --next-up` to land directly in the normative `Next Up` pickup queue; default lands in Backlog, which is unprioritized intake and never auto-picked), not by hand-editing `.pi/dev-loop-queue.json`; the queue runner reconciles board `Next Up` items into queue entries before each run. See the operator guides under `docs/` (queue board usage/setup) for the workflow.
15
- 9. **Hand-rolling a fan-in wait by polling a subagent transcript or parsing tool JSON with `node -e`/`python3`**: When awaiting fan-out reviewers (gate `draft_gate`/`pre_approval_gate` sub-loops, or any Agent-tool fan-out), NEVER tail/parse another agent's JSONL transcript, NEVER use `node -e`/`python3` to parse tool/subagent JSON, and NEVER `sleep`-poll in a shell loop for completion. These improvisations breach the [internal-tooling-only rule (issue #982)](retrospective-checkpoint-contract.md#internal-tooling-only-rule-issue-982--now-advisory) (no `node -e`/`python3` parsing of tool JSON) and the no-shell-polling rule, and a single improvised wait adds a non-empty entry to the advisory `retrospectiveFindings.rawCallViolations` array (issue #1077: reported to the conductor, **never blocking**). **Sanctioned wait:** rely on the harness completion notification, or read each reviewer's findings artifact at its deterministic output path (see [Checkpoint Review Chain Contract](../../docs/gate-review-sub-loop-contract.md) Phase 2), then join via `consolidateFanin` from `@dev-loops/core/loop/gate-fanin` (Phase 3). See also the dev-loop SKILL "Bounded async task contract".
15
+ 9. <!-- rule: ANTIPATTERN-FANIN-WAIT --> `ANTIPATTERN-FANIN-WAIT` — **Hand-rolling a fan-in wait by polling a subagent transcript or parsing tool JSON with `node -e`/`python3`**: When awaiting fan-out reviewers (gate `draft_gate`/`pre_approval_gate` sub-loops, or any Agent-tool fan-out), NEVER tail/parse another agent's JSONL transcript, NEVER use `node -e`/`python3` to parse tool/subagent JSON, and NEVER `sleep`-poll in a shell loop for completion. These improvisations breach the [internal-tooling-only rule (issue #982)](retrospective-checkpoint-contract.md#internal-tooling-only-rule-issue-982--now-advisory) (no `node -e`/`python3` parsing of tool JSON) and the no-shell-polling rule, and a single improvised wait adds a non-empty entry to the advisory `retrospectiveFindings.rawCallViolations` array (issue #1077: reported to the conductor, **never blocking**). **Sanctioned wait:** rely on the harness completion notification, or read each reviewer's findings artifact at its deterministic output path (see [Checkpoint Review Chain Contract](../../docs/gate-review-sub-loop-contract.md) Phase 2), then join via `consolidateFanin` from `@dev-loops/core/loop/gate-fanin` (Phase 3). See also the dev-loop SKILL "Bounded async task contract".
16
16
  10. **Ad hoc `gh api graphql` review-thread queries or `gh pr checks`/shell-pipe CI polling**: Do not hand-write a GraphQL query to enumerate review threads, and do not wrap `gh pr checks` in an `awk`/`grep`/`until`-based shell loop to wait for CI. Enumerate threads (with the ids `reply-resolve-review-thread.mjs` needs) via `scripts/github/list-review-threads.mjs`; block on current-head CI settling via `scripts/github/wait-pr-checks.mjs` (or `dev-loops loop watch-ci` for the JSON-status variant); read the aggregate loop/CI/thread state via `detect-copilot-loop-state.mjs` instead of re-deriving it from raw `gh` output.
17
17
 
18
18
  ## Light mode exception
19
19
 
20
- Small scoped changes (≤3 files, ≤200 lines) may skip fan-out/fan-in but must still run validation and a single review pass. See [Local Implementation](../local-implementation/SKILL.md) for details.
20
+ Small scoped changes under the configured `localImplementation.lightMode` thresholds (owned by `LOCAL-LIGHT-MODE-CONFIG-SURFACE` in [Local Implementation](../local-implementation/SKILL.md)) may skip fan-out/fan-in but must still run validation and a single review pass.
21
21
 
22
22
  ## Cross-references
23
23
 
@@ -1,17 +1,19 @@
1
1
  # Artifact authority contract
2
2
 
3
- This document is the canonical authority for the artifact-selection model: whether a work item originates from a GitHub issue (tracker-first) or from a persisted markdown plan file (local-planning).
3
+ Canonical owner for the artifact-selection model: whether a work item originates from a GitHub issue (tracker-first), a persisted markdown plan file (local-planning), or the sanctioned lightweight PR-body-as-spec path.
4
4
 
5
5
  This canonical owner lives in the shipped `skills/docs/` surface because installed skill/runtime consumers reliably own the skills subtree. In installed layouts, read the same contract via [Artifact Authority Contract](../docs/artifact-authority-contract.md) from the installed skill directory.
6
6
 
7
7
  Other repo docs may summarize or link this contract, but they should not redefine it.
8
8
 
9
- ## Two-tier model
9
+ ## Three-origin model
10
10
 
11
11
  <!-- rule: ARTIFACT-TWO-TIER-EXCLUSIVE -->
12
- dev-loops supports two mutually exclusive artifact authority modes. Every work item MUST originate from exactly one authoritative artifact: a GitHub issue or a persisted markdown plan file. Work MUST NOT originate from a PR or a direct local change unless explicitly requested.
12
+ <!-- The rule ID predates the three-origin wording below (it was written when the
13
+ model was two-tier); retained as-is for ID stability, not renamed. -->
14
+ dev-loops supports three mutually exclusive artifact authority origins. Every work item MUST originate from exactly one authoritative artifact: a GitHub issue, a persisted markdown plan file, or — on the sanctioned lightweight path — the PR description itself as the spec-of-record (no committed plan artifact). Work MUST NOT originate from a PR (other than the sanctioned lightweight PR-body-as-spec path) or a direct local change unless explicitly requested.
13
15
 
14
- The shipped extension default selects local-planning; see [Shipped default posture](#shipped-default-posture) below. The mode names that follow describe the two tiers; "default" in their headings refers to the local-first code-level default in `BUILT_IN_DEFAULTS`.
16
+ The shipped extension default selects local-planning; see [Shipped default posture](#shipped-default-posture) below. The mode names that follow describe the origins; "default" in their headings refers to the local-first code-level default in `BUILT_IN_DEFAULTS`.
15
17
 
16
18
  ### Tracker-first
17
19
 
@@ -5,7 +5,7 @@ Canonical owner for agent confirmation / authorization rules across all workflow
5
5
  ## Core rule
6
6
 
7
7
  <!-- rule: CONFIRM-CORE-EXPLICIT -->
8
- Before any state-changing action, get explicit confirmation unless the latest user message already clearly authorizes that exact action.
8
+ `CONFIRM-CORE-EXPLICIT`: Before any state-changing action, the agent MUST obtain explicit confirmation unless the latest user message already clearly authorizes that exact action.
9
9
 
10
10
  ## What counts as confirmation
11
11
 
@@ -1,6 +1,6 @@
1
1
  # Copilot loop operations
2
2
 
3
- This document is the canonical operational reference for the deterministic Copilot PR follow-up state machine used by the routed `copilot_pr_followup`, `wait_watch`, `reviewer_fixer`, and `final_approval` paths behind `dev-loop`.
3
+ Canonical owner for operating the deterministic Copilot PR follow-up state machine the operational reference for the routed `copilot_pr_followup`, `wait_watch`, `reviewer_fixer`, and `final_approval` paths behind `dev-loop`. The machine's states and transitions are defined by [Copilot Loop State Graph](../../docs/copilot-loop-state-graph.md).
4
4
 
5
5
  Use it together with:
6
6
  - [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md)
@@ -162,12 +162,14 @@ When you do hand work to Copilot:
162
162
 
163
163
  ## PR description contract
164
164
 
165
+ This section governs tracker-backed (Copilot-handoff) PRs; issue-less/plan-file-backed PRs are governed by merge precondition 7 in merge-preconditions.md instead.
166
+
165
167
  Follow the PR description contract (see [Agent Instructions](../../AGENTS.md) if present; otherwise use the structure below): detailed structured descriptions, not thin placeholders. At minimum include change summary, scope/context, explicit acceptance criteria, explicit definition of done, and explicit non-goals, and `Closes #N` (or `Fixes #N`) for the linked issue so GitHub auto-closes it on merge.
166
168
 
167
169
  Checkbox rule: acceptance criteria, definition-of-done items, and any task list must be rendered as real GitHub markdown checkboxes inside list items (`- [ ]` / `- [x]`, also `* [ ]` / `* [x]`). Do not wrap checkbox markers (e.g. `[x]`) in backticks. Do not place checkbox markers inside table cells — task lists are not interactive there even with a leading `- `.
168
170
 
169
171
  <!-- rule: OPS-DRAFT-FIRST-PR -->
170
- `OPS-DRAFT-FIRST-PR`: New PRs in this workflow MUST be opened as **draft** PRs first when the repository enables `.devloops` at repo root `workflow.requireDraftFirst` (the built-in shipped default remains permissive; this repo opts in) mechanically enforced by the `create-pr.mjs` wrapper below. Agents MUST NOT create a fresh PR directly in ready-for-review state unless the user explicitly overrides that policy for the current PR scope. The draft gate inspection is a real workflow boundary, so a new PR must exist in draft before `gh pr ready` is even eligible.
172
+ `OPS-DRAFT-FIRST-PR`: New PRs in this workflow MUST always be opened as **draft** PRs mechanically enforced by the `create-pr.mjs` wrapper below, which is unconditionally draft-only and rejects `--ready`. Agents MUST NOT create a fresh PR directly in ready-for-review state; `gh pr ready` (via the `ready-for-review.mjs` wrapper) is the only path out of draft, gated on clean draft-gate evidence. This rule owns the draft-first mechanism itself; [TRACKER-PROJECTION-REQUIRED-METADATA](./tracker-first-loop-state.md#31-required-pr-metadata) owns the tracker-facing "PR MUST start as a draft" metadata expectation. The draft gate inspection is a real workflow boundary, so a new PR must exist in draft before `gh pr ready` is even eligible.
171
173
 
172
174
  Only use `node <resolved-skill-scripts>/github/create-pr.mjs` when authoritative issue↔PR resolution says there is no already-open linked PR. If a PR already exists, reuse/update that canonical PR instead of opening another one. This wrapper preserves the underlying `gh pr create` output contract while enforcing draft-first mechanically and self-assigning the PR by default (`--assignee @me`).
173
175
 
@@ -26,6 +26,15 @@ For **all new ideas** that are not already anchored to an existing issue (includ
26
26
  - after approval, run a second async mutation pass (dispatched via the procedure) instead of mutating directly from inherited context
27
27
  - verify post-mutation artifact state and record what actually changed
28
28
 
29
+ **Quick-capture exemption:** board quick-capture enqueue (the `/loop-enqueue` freeform
30
+ path — freeform text turned directly into a minimal issue, gated on explicit human
31
+ approval, then grilled inline and added to the board) MAY defer the up-front proposal
32
+ artifact, the async classification pass, the async fan-out/fan-in proposal generation, and
33
+ the second async mutation pass; its inline confirm→create→grill steps stand in for them,
34
+ with grilling supplying classification/refinement after creation. The human-gated-mutation
35
+ and create-new-only safeties still apply — it always creates a new issue and never
36
+ repurposes/overwrites an existing one.
37
+
29
38
  Deterministic intake + mutation-gate state machine:
30
39
 
31
40
  ```text
@@ -127,15 +136,15 @@ Preflight verdicts:
127
136
  - `ready_for_followup`: linked PR has become substantive; resume from that PR
128
137
  - `timed_out`: observational first; refresh authoritative state
129
138
  - if refreshed state is still `waiting_for_initial_copilot_implementation`, remain attached to the same durable wait seam and continue waiting
130
- - if the refreshed state exits this seam, route based on that refreshed state instead of surfacing timeout attention
139
+ - <!-- rule: INTAKE-TIMEOUT-ROUTE-ON-SEAM-EXIT --> `INTAKE-TIMEOUT-ROUTE-ON-SEAM-EXIT`: if the refreshed state exits this seam, route based on that refreshed state instead of surfacing timeout attention
131
140
  - when the refreshed state is `linked_pr_ready_for_followup`, re-enter normal PR follow-up per [FACADE-BOOTSTRAP-ISOLATED-WORKTREE-CONTINUATION](public-dev-loop-contract.md): if the follow-up handoff carries `conductorRouting.handoffEnvelope.requiresLocalIsolation=true`, perform the expected isolated-checkout/worktree handoff and continue
132
- - only surface timeout attention when the seam's durable watch budget is actually exhausted
141
+ - <!-- rule: INTAKE-TIMEOUT-SURFACE-BUDGET-EXHAUSTED --> `INTAKE-TIMEOUT-SURFACE-BUDGET-EXHAUSTED`: only surface timeout attention when the seam's durable watch budget is actually exhausted
133
142
  - for explicit inspect/status requests, report the still-waiting state and exit normally
134
143
  - carry that resolved repo slug through every later GitHub issue/PR command
135
144
 
136
145
  ### From a plan-doc path
137
146
 
138
- - Resolve the target repository slug for this work item before any GitHub search or mutation
147
+ - <!-- rule: INTAKE-REPO-SLUG-RESOLVE-FIRST --> `INTAKE-REPO-SLUG-RESOLVE-FIRST`: Resolve the target repository slug for this work item before any GitHub search or mutation
139
148
  - default to the current repository slug
140
149
  - if the plan-doc reference explicitly points at another GitHub repository, resolve `<resolved-repo>` first
141
150
  - search existing issues with:
@@ -39,13 +39,15 @@ doing manually; it is not a general conflict-resolution engine.
39
39
  ## Required before merge
40
40
 
41
41
  <!-- rule: MERGE-PRECOND-REQUIRED -->
42
+ Before merge, ALL of the following MUST hold:
43
+
42
44
  1. ✅ Conflict-free with base (`mergeable: MERGEABLE`; not `CONFLICTING`/`DIRTY`/`BEHIND`/`UNKNOWN`)
43
45
  2. ✅ CI green on current head (or crediblyGreen via `--local-validation-head-sha`)
44
46
  3. ✅ Draft gate satisfied — clean `draft_gate` verdict per `GATE-COMMENT-VERDICT-VALUES` ([Checkpoint Verdict Comment Contract](../../docs/gate-review-comment-contract.md))
45
47
  4. ✅ Pre-approval gate satisfied — clean `pre_approval_gate` verdict on the current head, same rule
46
48
  5. ✅ All review threads resolved
47
49
  6. ✅ Explicit merge authorization from operator
48
- 7. ✅ Issue-backed work: PR body contains `Closes #N` or `Fixes #N`; for an issue-less lightweight PR the closing reference is instead absent by design (owned by `ARTIFACT-LIGHTWEIGHT-BODY-INVARIANTS` in [Artifact Authority Contract](artifact-authority-contract.md))
50
+ 7. ✅ Closing-reference state matches artifact backing, each arm owned by a different contract: tracker-backed work PR body contains `Closes #N` or `Fixes #N` (owned by the PR description contract in [copilot-loop-operations.md](copilot-loop-operations.md)); issue-less lightweight (PR-body-as-spec, no backing issue) — the closing reference is absent by design and `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <number> --no-issue` passes clean (owned by `ARTIFACT-LIGHTWEIGHT-BODY-INVARIANTS` in [Artifact Authority Contract](artifact-authority-contract.md)); plan-file promotion (P4) — the PR body carries the committed plan-doc path as the spec-of-record and, being issue-less by design (`buildPromotionPrBody` neutralizes closing keywords), the closing reference MUST NOT be present
49
51
  8. ✅ PR **title** free of merge-blocking markers — `WIP`, `[WIP]`, `DRAFT`, `DO NOT MERGE`, `🚧` (case-insensitive)
50
52
 
51
53
  > Runner-coordination lock: the pre-merge evidence check fails closed on a stale/foreign runner claim for the PR. A completing run releases its claim best-effort at every terminal stop (including the human approval checkpoint), so a merge re-dispatch normally proceeds. If a lock held by a completed/dead run still blocks the merge, take it over explicitly with `node <resolved-skill-scripts>/loop/pr-runner-coordination.mjs takeover --repo <owner/name> --pr <number>`. Never take over a genuinely active (non-stale) run — that fail-closed block is intentional.
@@ -80,9 +82,9 @@ A marker is allowed only while the PR is still in draft; it must be removed befo
80
82
 
81
83
  ## Merge authorization
82
84
 
83
- - Must be explicit for the active issue/PR scope
85
+ - Merge authorization MUST be explicit for the active issue/PR scope
84
86
  - `"Merge authorized if gates green"` is valid explicit authorization
85
- - Implied approval from prior turns is not sufficient
87
+ - Implied approval from prior turns MUST NOT be treated as sufficient
86
88
 
87
89
  ### `autonomy.humanMergeOnly` — fixed human-only merge (non-overridable)
88
90
 
@@ -80,7 +80,7 @@ This gate uses review angles resolved from config (`resolveGateAngles(config, "p
80
80
  Boundary note:
81
81
  - `pre_approval_gate` governs only final approval readiness for the reviewed head
82
82
  - a clean verdict requires no findings at any severity in the gate's `blockCleanOnFindingSeverities` (resolved from config via `resolveGateConfig(config, "preApproval").blockCleanOnFindingSeverities`)
83
- - non-draft PRs do not need visible `draft_gate` evidence to enter the post-draft review / `pre_approval_gate` lifecycle; only the draft -> ready transition depends on `draft_gate`
83
+ - non-draft PRs do not need *per-head* `draft_gate` evidence to enter the post-draft review / `pre_approval_gate` lifecycle the one-time draft -> ready transition record still applies; a non-draft PR with no clean `draft_gate` evidence at all fails closed and must reconcile that missing evidence first (see the current-head `draft_gate` evidence bullet under [Fail-closed rules](#fail-closed-rules))
84
84
 
85
85
  ## Lifecycle states
86
86
 
@@ -72,7 +72,8 @@ Use this taxonomy consistently across docs, discovery surfaces, and tests:
72
72
  | Internal routed strategy modules | `issue-intake`, `copilot-pr-followup`, `local-implementation`, `final-approval` | keep internal-only behind `dev-loop`; do not expose as executable peer workflow entrypoints |
73
73
  | Reusable role agents | `developer`, `docs`, `review`, `fixer`, `quality`, `refiner` | keep framed as reusable building blocks, not peer public workflow entrypoints |
74
74
 
75
- Any remaining specialized Copilot behavior stays internal-only behind `dev-loop`.
75
+ <!-- rule: FACADE-COPILOT-INTERNAL-ONLY -->
76
+ `FACADE-COPILOT-INTERNAL-ONLY`: Any remaining specialized Copilot behavior stays internal-only behind `dev-loop`.
76
77
 
77
78
  <!-- rule: FACADE-TAXONOMY-DRIFT-TEST -->
78
79
  `FACADE-TAXONOMY-DRIFT-TEST`: Regression tests MUST fail if this taxonomy drifts in wording or surfaced entrypoint assets.
@@ -96,8 +96,8 @@ If the gate result is `needs_reconcile`, the caller MUST NOT proceed with the pr
96
96
  <!-- rule: RETRO-ADVISORY-NEVER-GATE -->
97
97
  The retrospective is **advisory**: it runs, records flagged raw-call / discipline
98
98
  observations honestly, and passes them back to the conductor (main agent) to
99
- **decide** what to do with them — but it MUST NOT block a merge or any lifecycle
100
- transition. The pre-merge retrospective gate (`evaluateRetrospectiveMergeApproval`
99
+ **decide** what to do with them — but it MUST NOT block a merge or any PR-lifecycle
100
+ transition of the current run. The pre-merge retrospective gate (`evaluateRetrospectiveMergeApproval`
101
101
  and the `requireRetrospectiveGate` / `requireRetrospectiveInternalTooling` config
102
102
  keys) has been **removed**. There is no `retrospective_gate_pending` / `blocked`
103
103
  disposition on account of the internal-tooling raw-call record.
@@ -135,10 +135,13 @@ scripts legitimately call `gh`/GraphQL internally; that is the tooling. The rule
135
135
  targets the agent's own top-level shell calls, not a script's internals.
136
136
 
137
137
  **Write-op allowlist (verifier only):** `gh pr merge`, `gh pr ready`,
138
- `gh issue create`, `gh issue edit` have no internal wrapper today. The deterministic
139
- verifier records these as `allowedWriteOps` rather than violations so they are
140
- surfaced distinctly, not as breaches. Close the gap with a wrapper to remove an
141
- entry. None of these block anything.
138
+ `gh issue create` have no internal wrapper today. `gh issue edit` now has one
139
+ (`scripts/github/edit-issue.mjs`) and stays as a belt-and-suspenders entry like
140
+ `gh label create` (`scripts/github/create-label.mjs`) so a bare invocation
141
+ surfaced from the wrapper's own subprocess is not miscounted as a breach. The
142
+ deterministic verifier records all of these as `allowedWriteOps` rather than
143
+ violations so they are surfaced distinctly, not as breaches. Close the gap with a
144
+ wrapper to remove or reframe a no-wrapper entry. None of these block anything.
142
145
 
143
146
  **Inline-interpreter check item:** the raw-call scan below mechanically catches
144
147
  `node -e`/`python3 -c`/heredoc calls as a `rawCallViolations` entry — the same
@@ -17,7 +17,7 @@ Canonical owner for validation requirements across all workflow families.
17
17
  ## Coverage requirements
18
18
 
19
19
  <!-- rule: VALIDATE-COVERAGE-THRESHOLD -->
20
- `VALIDATE-COVERAGE-THRESHOLD`: Changed files MUST have ≥90% coverage for lines, statements, functions, and branches, and non-trivial logic MUST be test-first.
20
+ `VALIDATE-COVERAGE-THRESHOLD`: Changed files SHOULD maintain ≥90% coverage for lines, statements, functions, and branches (not enforced by the shipped verify config; treat it as the working target), and non-trivial logic MUST be test-first.
21
21
 
22
22
  ## Cross-references
23
23