dev-loops 0.6.0 → 0.7.1

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 (172) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +2 -1
  3. package/.claude/agents/review.md +2 -1
  4. package/.claude/commands/{auto.md → loop-auto.md} +1 -1
  5. package/.claude/commands/loop-continue.md +15 -0
  6. package/.claude/commands/loop-enqueue.md +22 -0
  7. package/.claude/commands/loop-grill.md +17 -0
  8. package/.claude/commands/{info.md → loop-info.md} +2 -2
  9. package/.claude/commands/loop-queue-status.md +24 -0
  10. package/.claude/commands/{start-spike.md → loop-start-spike.md} +1 -1
  11. package/.claude/commands/{start.md → loop-start.md} +1 -1
  12. package/.claude/commands/{status.md → loop-status.md} +1 -1
  13. package/.claude/hooks/_bash-command-classify.mjs +333 -29
  14. package/.claude/hooks/_hook-decisions.mjs +138 -15
  15. package/.claude/hooks/pre-tool-use-bash-gate.mjs +36 -15
  16. package/.claude/skills/copilot-pr-followup/SKILL.md +24 -12
  17. package/.claude/skills/dev-loop/SKILL.md +15 -11
  18. package/.claude/skills/docs/acceptance-criteria-verification.md +6 -2
  19. package/.claude/skills/docs/anti-patterns.md +2 -1
  20. package/.claude/skills/docs/artifact-authority-contract.md +22 -4
  21. package/.claude/skills/docs/copilot-loop-operations.md +1 -1
  22. package/.claude/skills/docs/cross-harness-regression-contract.md +60 -0
  23. package/.claude/skills/docs/issue-intake-procedure.md +1 -0
  24. package/.claude/skills/docs/local-planning-flow.md +1 -1
  25. package/.claude/skills/docs/local-planning-worked-example.md +1 -1
  26. package/.claude/skills/docs/merge-preconditions.md +17 -1
  27. package/.claude/skills/docs/plan-file-contract.md +1 -1
  28. package/.claude/skills/docs/public-dev-loop-contract.md +1 -1
  29. package/.claude/skills/docs/retrospective-checkpoint-contract.md +90 -76
  30. package/.claude/skills/docs/ui-e2e-scoping-step.md +32 -0
  31. package/.claude/skills/docs/workflow-handoff-contract.md +39 -0
  32. package/.claude/skills/local-implementation/SKILL.md +25 -14
  33. package/.claude/skills/loop-grill/SKILL.md +163 -0
  34. package/AGENTS.md +1 -0
  35. package/CHANGELOG.md +95 -0
  36. package/README.md +9 -9
  37. package/agents/dev-loop.agent.md +3 -2
  38. package/agents/developer.agent.md +1 -1
  39. package/agents/docs.agent.md +1 -1
  40. package/agents/fixer.agent.md +1 -1
  41. package/agents/quality.agent.md +1 -1
  42. package/agents/refiner.agent.md +1 -1
  43. package/agents/review.agent.md +3 -2
  44. package/cli/index.mjs +35 -42
  45. package/extension/README.md +4 -4
  46. package/extension/checks.ts +1 -5
  47. package/extension/harness-types.ts +1 -0
  48. package/extension/index.ts +6 -0
  49. package/extension/pi-extension-adapter.ts +2 -0
  50. package/extension/presentation.ts +7 -19
  51. package/package.json +9 -10
  52. package/scripts/github/capture-review-threads.mjs +2 -9
  53. package/scripts/github/comment-issue.mjs +2 -9
  54. package/scripts/github/create-label.mjs +133 -0
  55. package/scripts/github/detect-checkpoint-evidence.mjs +170 -14
  56. package/scripts/github/detect-linked-issue-pr.mjs +22 -6
  57. package/scripts/github/edit-pr.mjs +259 -0
  58. package/scripts/github/fetch-ci-logs.mjs +2 -9
  59. package/scripts/github/list-issues.mjs +2 -9
  60. package/scripts/github/manage-sub-issues.mjs +46 -10
  61. package/scripts/github/offer-human-handoff.mjs +8 -5
  62. package/scripts/github/post-gate-findings.mjs +14 -2
  63. package/scripts/github/probe-ci-status.mjs +8 -2
  64. package/scripts/github/probe-copilot-review.mjs +21 -14
  65. package/scripts/github/ready-for-review.mjs +26 -8
  66. package/scripts/github/reconcile-draft-gate.mjs +7 -3
  67. package/scripts/github/reply-resolve-review-thread.mjs +16 -5
  68. package/scripts/github/reply-resolve-review-threads.mjs +69 -7
  69. package/scripts/github/request-copilot-review.mjs +8 -2
  70. package/scripts/github/resolve-handoff-candidates.mjs +7 -3
  71. package/scripts/github/resolve-tracker-local-spec.mjs +9 -3
  72. package/scripts/github/stage-reviewer-draft.mjs +10 -2
  73. package/scripts/github/tick-verified-checkboxes.mjs +202 -0
  74. package/scripts/github/upsert-checkpoint-verdict.mjs +31 -10
  75. package/scripts/github/verify-fresh-review-context.mjs +150 -31
  76. package/scripts/github/view-pr.mjs +150 -0
  77. package/scripts/github/write-gate-context.mjs +248 -61
  78. package/scripts/github/write-gate-findings-log.mjs +75 -1
  79. package/scripts/lib/jq-output.mjs +18 -0
  80. package/scripts/loop/_post-convergence-change.mjs +211 -0
  81. package/scripts/loop/_pr-runner-coordination.mjs +70 -0
  82. package/scripts/loop/_repo-root-resolver.mjs +47 -0
  83. package/scripts/loop/build-handoff-envelope.mjs +14 -4
  84. package/scripts/loop/check-retro-tooling.mjs +14 -3
  85. package/scripts/loop/checkpoint-contract.mjs +7 -4
  86. package/scripts/loop/cleanup-worktree.mjs +12 -3
  87. package/scripts/loop/conductor-monitor.mjs +12 -18
  88. package/scripts/loop/copilot-pr-handoff.mjs +135 -14
  89. package/scripts/loop/debt-remediate.mjs +24 -12
  90. package/scripts/loop/detect-change-scope.mjs +36 -7
  91. package/scripts/loop/detect-copilot-loop-state.mjs +11 -5
  92. package/scripts/loop/detect-copilot-session-activity.mjs +7 -3
  93. package/scripts/loop/detect-initial-copilot-pr-state.mjs +7 -3
  94. package/scripts/loop/detect-internal-only-pr.mjs +8 -2
  95. package/scripts/loop/detect-issue-refinement-artifact.mjs +6 -3
  96. package/scripts/loop/detect-pr-gate-coordination-state.mjs +149 -66
  97. package/scripts/loop/detect-reviewer-loop-state.mjs +12 -2
  98. package/scripts/loop/detect-tracker-first-loop-state.mjs +9 -1
  99. package/scripts/loop/detect-tracker-pr-state.mjs +10 -3
  100. package/scripts/loop/ensure-worktree.mjs +8 -3
  101. package/scripts/loop/info.mjs +12 -4
  102. package/scripts/loop/inspect-run-viewer/constants.mjs +4 -18
  103. package/scripts/loop/inspect-run-viewer/vendor/mermaid.min.js +3405 -0
  104. package/scripts/loop/inspect-run-viewer-ci-changes.mjs +18 -6
  105. package/scripts/loop/inspect-run-viewer.mjs +67 -4
  106. package/scripts/loop/inspect-run.mjs +8 -2
  107. package/scripts/loop/outer-loop.mjs +8 -4
  108. package/scripts/loop/pr-runner-coordination.mjs +2 -9
  109. package/scripts/loop/pre-commit-branch-guard.mjs +16 -10
  110. package/scripts/loop/pre-flight-gate.mjs +9 -9
  111. package/scripts/loop/pre-pr-ready-gate.mjs +8 -4
  112. package/scripts/loop/pre-write-remote-freshness-guard.mjs +13 -4
  113. package/scripts/loop/provision-worktree.mjs +74 -3
  114. package/scripts/loop/resolve-dev-loop-startup.mjs +76 -7
  115. package/scripts/loop/resolve-gate-dispatch.mjs +134 -0
  116. package/scripts/loop/resolve-pr-conflicts.mjs +14 -7
  117. package/scripts/loop/run-conductor-cycle.mjs +8 -2
  118. package/scripts/loop/run-queue.mjs +18 -9
  119. package/scripts/loop/run-refinement-audit.mjs +8 -9
  120. package/scripts/loop/run-watch-cycle.mjs +2 -9
  121. package/scripts/loop/sanctioned-commands.mjs +104 -0
  122. package/scripts/loop/steer-loop.mjs +29 -16
  123. package/scripts/loop/validate-pr-body-spec.mjs +207 -0
  124. package/scripts/loop/watch-initial-copilot-pr.mjs +8 -3
  125. package/scripts/pages/build-site.mjs +59 -8
  126. package/scripts/pages/build-state-atlas.mjs +441 -0
  127. package/scripts/projects/_resolve-project.mjs +148 -0
  128. package/scripts/projects/add-queue-item.mjs +60 -54
  129. package/scripts/projects/archive-done-items.mjs +49 -84
  130. package/scripts/projects/ensure-queue-board.mjs +10 -36
  131. package/scripts/projects/list-queue-items.mjs +116 -65
  132. package/scripts/projects/move-queue-item.mjs +28 -49
  133. package/scripts/projects/reconcile-queue.mjs +253 -0
  134. package/scripts/projects/reorder-queue-item.mjs +41 -47
  135. package/scripts/projects/resolve-active-board-item.mjs +108 -46
  136. package/scripts/projects/sync-item-status.mjs +15 -10
  137. package/scripts/refine/_refine-helpers.mjs +21 -5
  138. package/scripts/refine/exit-spike.mjs +18 -8
  139. package/scripts/refine/promote-plan.mjs +22 -16
  140. package/scripts/refine/prose-linkage-detector.mjs +1 -1
  141. package/scripts/refine/refine-plan-file.mjs +13 -4
  142. package/scripts/refine/refinement-completeness-checker.mjs +1 -1
  143. package/scripts/refine/scaffold-spike-file.mjs +4 -8
  144. package/scripts/refine/scope-boundary-cross-checker.mjs +1 -1
  145. package/scripts/refine/tree-integrity-validator.mjs +1 -1
  146. package/scripts/refine/validate-plan-file.mjs +1 -1
  147. package/scripts/refine/validate-spike-file.mjs +1 -1
  148. package/scripts/refine/verify.mjs +11 -6
  149. package/scripts/release/assert-core-dependency-version.mjs +123 -0
  150. package/scripts/release/extract-changelog-section.mjs +16 -2
  151. package/skills/copilot-pr-followup/SKILL.md +24 -12
  152. package/skills/dev-loop/SKILL.md +10 -6
  153. package/skills/docs/acceptance-criteria-verification.md +6 -2
  154. package/skills/docs/anti-patterns.md +2 -1
  155. package/skills/docs/artifact-authority-contract.md +22 -4
  156. package/skills/docs/copilot-loop-operations.md +1 -1
  157. package/skills/docs/cross-harness-regression-contract.md +60 -0
  158. package/skills/docs/issue-intake-procedure.md +1 -0
  159. package/skills/docs/local-planning-flow.md +1 -1
  160. package/skills/docs/local-planning-worked-example.md +1 -1
  161. package/skills/docs/merge-preconditions.md +17 -1
  162. package/skills/docs/plan-file-contract.md +1 -1
  163. package/skills/docs/public-dev-loop-contract.md +1 -1
  164. package/skills/docs/retrospective-checkpoint-contract.md +90 -76
  165. package/skills/docs/ui-e2e-scoping-step.md +32 -0
  166. package/skills/docs/workflow-handoff-contract.md +39 -0
  167. package/skills/local-implementation/SKILL.md +25 -14
  168. package/skills/loop-grill/SKILL.md +165 -0
  169. package/.claude/commands/continue.md +0 -15
  170. package/scripts/loop/conductor.mjs +0 -233
  171. package/scripts/loop/detect-stale-runner.mjs +0 -265
  172. package/scripts/loop/pre-push-main-guard.mjs +0 -117
@@ -39,11 +39,17 @@ Treat missing optional files as normal bootstrap conditions, not errors.
39
39
 
40
40
  ### Tracker-backed local implementation
41
41
 
42
- Local implementation supports two durable spec inputs:
42
+ Local implementation supports these spec-of-record inputs. The first two are durable/committed artifacts; the third is a spec-of-record that is NOT a durable committed artifact:
43
+
44
+ Durable/committed spec artifacts:
43
45
 
44
46
  - phase-doc-backed local sessions ([Phase Plan](../../docs/phases/phase-x.md) is canonical)
45
47
  - tracker-backed local sessions (the tracker issue is canonical)
46
48
 
49
+ Non-durable spec-of-record (no committed plan artifact):
50
+
51
+ - lightweight PR-body-as-spec sessions (`--lightweight`; the PR description is the canonical spec-of-record but NOT a durable committed artifact — the resolver output carries `canonicalSpecSource: pr_body` and the derived handoff envelope carries `specSource: pr_body`; no phase/plan doc minted or committed). Same gate sequence as the phase-doc path; only the backing artifact differs. See [Artifact Authority Contract](../docs/artifact-authority-contract.md) "Lightweight (PR-body-as-spec)".
52
+
47
53
  Tracker-backed local implementation stays inside the existing `local_implementation` path. For sub-issue tree decomposition, see [Sub-Issue Tree Contract](../../docs/sub-issue-tree-contract.md) (this is a source-repo reference; it is not part of the bundled `../docs/` runtime contract surface for installed skill copies). It does not introduce a new routing mode.
48
54
 
49
55
  When the local spec already lives in a tracker issue:
@@ -126,33 +132,33 @@ Follow [Anti-patterns](../docs/anti-patterns.md) for the general tooling-interna
126
132
 
127
133
  Apply [Structural Quality](../docs/structural-quality.md) from the `deep` review angle.
128
134
 
129
- ## Light mode (small changes) — config-only
135
+ ## Light mode (small changes)
136
+
137
+ Light mode is wired into gate dispatch. A PR that is **under threshold** (≤ `maxLines` lines changed AND ≤ `maxFiles` files, per `.devloops` field `localImplementation.lightMode` — this repo sets `maxLines: 20`, `maxFiles: 2`; the shipped built-in default has light mode disabled (`enabled: false`); if enabled without explicit values it falls back to `maxFiles: 3` / `maxLines: 200`) AND carries no `gate:full` label collapses BOTH `draft_gate` and `pre_approval_gate` to a single inline single-agent correctness + no-op check. The Copilot review request and its polling are skipped.
130
138
 
131
- Light mode is currently **config-only**: the schema, resolver, and scope detector are implemented, but no functional wiring exists yet in the local-implementation flow. When scope is small enough, the intent is to skip fan-out/fan-in and use a single review pass instead. Light mode will still require validation and pre-approval gate once wired.
139
+ **Escalation:**
140
+ 1. Auto-escalate — if the inline check surfaces any finding whose severity is in the gate's `blockCleanOnFindingSeverities` (i.e. ≥ `worth-fixing-now`), dispatch escalates to full fan-out.
141
+ 2. Label override — the `gate:full` label forces full fan-out regardless of PR size.
132
142
 
133
- **Eligibility:** ≤3 files AND ≤200 lines changed (configurable via `.devloops` field `localImplementation.lightMode`).
143
+ The `draft_gate` boundary (draft ready) is still recorded because `requireDraftFirst` is honored: light mode only changes HOW the gate runs (inline vs fan-out), not WHETHER the draft boundary exists.
134
144
 
135
- Use `scripts/loop/detect-change-scope.mjs` to determine eligibility:
145
+ Dispatch is resolved deterministically via `resolveGateDispatchMode(config, gate, { scope, hasFullLabel, inlineFindingSeverities })` from `@dev-loops/core/config`. `scripts/loop/resolve-gate-dispatch.mjs` is the CLI wrapper the orchestrator calls, combining `detect-change-scope.mjs` output with the `gate:full` label fact.
146
+
147
+ Use `scripts/loop/detect-change-scope.mjs` to determine scope:
136
148
  ```sh
137
149
  node scripts/loop/detect-change-scope.mjs
138
150
  ```
139
151
 
140
- **Planned light mode path (not yet wired):**
141
- 1. Validation (`npm run verify`)
142
- 2. Single review pass (not multi-angle fan-out)
143
- 3. Pre-approval gate
144
- 4. Finalization
145
-
146
152
  **Override threshold:**
147
153
  ```yaml
148
154
  localImplementation:
149
155
  lightMode:
150
156
  enabled: true
151
- maxFiles: 5
152
- maxLines: 300
157
+ maxFiles: 2
158
+ maxLines: 20
153
159
  ```
154
160
 
155
- Disabled by default (opt-in). Scope above threshold falls back to full fan-out/fan-in path.
161
+ Scope above threshold falls back to the full fan-out/fan-in path.
156
162
 
157
163
  ## Deterministic logging structure
158
164
 
@@ -260,6 +266,10 @@ For the **current phase only**, run this loop before implementation.
260
266
 
261
267
  ### 1. Create or update the durable phase doc and tmp scaffold
262
268
 
269
+ **Lightweight (PR-body-as-spec) exception:** when the session is lightweight — the resolver output carries `canonicalSpecSource: pr_body` and the derived handoff envelope carries `specSource: pr_body` (started via `resolve-dev-loop-startup.mjs --issue <n> --lightweight`) — SKIP the durable phase-doc mint entirely. The PR description is the spec-of-record; do NOT create or commit any `docs/phases/*.md` for this session. The ephemeral `tmp/phases/` scaffold may still be used for local execution state. All other steps (read prior learning, plan, implement) proceed unchanged, and the gate sequence (draft → pre-approval fanout → detect-evidence → human merge) is identical. See [Artifact Authority Contract](../docs/artifact-authority-contract.md) "Lightweight (PR-body-as-spec)".
270
+
271
+ Otherwise (default phase-doc path), create or update the durable phase doc.
272
+
263
273
  Use paths like:
264
274
  - `docs/phases/phase-0.md`
265
275
  - `tmp/phases/phase-0/`
@@ -639,6 +649,7 @@ See [Stop Conditions](../docs/stop-conditions.md). Local-specific stops: phase c
639
649
  - PR creation always goes through `dev-loops pr create`, which is ALWAYS draft and always assigned — self-assigned by default (`--assignee @me` when none is given; honors an explicit `--assignee <login>` / `-a <login>`), never via raw `gh pr create`, and the PR body must contain `Closes #N` (or `Fixes #N`) for the linked issue so GitHub auto-closes it on merge. When `.devloops` sets `workflow.requireDraftFirst` to true, use `dev-loops pr create --assignee @me ...`. Do 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 eligible.
640
650
  - When authorization is pending, record the phase as `awaiting-finalization` and describe the exact missing step.
641
651
  - For phase-doc-backed sessions, merge the fully reviewed, locally validated branch back into local `main` when authorized.
652
+ - Behind-branch integration before merge (for example a sibling PR merged first and both touch shared files): prefer a merge commit (`git merge origin/main`) over rebase + force-push. Since dev-loop PRs are squash-merged, intermediate branch history is discarded at merge time, so a merge commit lands an identical result on `main` while avoiding a non-fast-forward push to the remote. Force-push (`--force-with-lease` only, never bare `--force`) remains acceptable only for a branch the loop solely owns and where no integration alternative exists (rare); document that carve-out rather than leaving it implicit. After any integration that changes the head SHA, re-verify gate evidence at the new head (existing behavior — see [Merge Preconditions](../docs/merge-preconditions.md#required-before-merge)).
642
653
 
643
654
  ## Commit policy
644
655
 
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: "loop-grill"
3
+ description: "Standalone pre-loop Socratic Q&A grill for issues (tracker-first) or local plan files (local-planning). Detects spec gaps, asks clarifying questions (interactive) or self-answers them from codebase context (--auto), then writes a ## Grill findings section back to the source artifact."
4
+ allowed-tools: Read Bash Edit Write
5
+ user-invocable: false
6
+ ---
7
+ <!-- GENERATED from skills/loop-grill/SKILL.md by scripts/claude/generate-claude-assets.mjs — do not edit; edit the source and regenerate. -->
8
+
9
+ # Loop-grill skill
10
+
11
+ A standalone, on-demand pre-loop grilling skill. Run it against an issue or a local plan file **before** the dev loop starts to surface underspecified acceptance criteria, fuzzy scope boundaries, unresolved primary actors, and undocumented hard-to-reverse decisions.
12
+
13
+ It is entirely separate from the in-loop docs-grill (`docs/docs-grill-step.md`), which audits code/doc drift while the loop runs. This skill operates on the *spec* before any implementation begins.
14
+
15
+ ## Interface
16
+
17
+ ```
18
+ /loop-grill <issue-number> # tracker-first, interactive
19
+ /loop-grill <issue-number> --auto # tracker-first, auto-answer
20
+ /loop-grill <path/to/plan.md> # local-planning, interactive
21
+ /loop-grill <path/to/plan.md> --auto # local-planning, auto-answer
22
+ ```
23
+
24
+ ## Argument validation (fail-closed)
25
+
26
+ Before doing anything else:
27
+
28
+ 1. Confirm exactly one positional argument is present (issue number or path). No argument → error, stop.
29
+ 2. Confirm the only optional flag is `--auto`. Any other flag → error, stop.
30
+ 3. **Tracker-first:** verify the issue exists. Non-existent issue → `Error: issue #<n> not found.`, stop.
31
+ 4. **Local-planning:** verify the file exists. Missing file → `Error: plan file not found: <path>`, stop.
32
+ 5. Never mutate any artifact when argument validation fails.
33
+
34
+ ## Step 1 — Load the target
35
+
36
+ - **Tracker-first:** fetch the issue body (title + description + any existing `## Grill findings` section).
37
+ - **Local-planning:** read the plan file from disk.
38
+
39
+ ## Step 1b — Surface external resources
40
+
41
+ Before detecting gaps, scan the loaded content for external resource references: links, other repo URLs, API endpoints, doc URLs, screenshots, or Playwright navigation descriptors.
42
+
43
+ - **Interactive mode:** if external resources are present, ask the operator to confirm they are accessible or to provide them before the Q&A starts.
44
+ - **`--auto` mode:** attempt to fetch each resource using bounded wrapper commands (e.g. `gh`, API wrapper scripts under `scripts/`). Do not fetch resources inline with raw `curl` or token-heavy calls. Flag any inaccessible resource as `unresolved` in the findings rather than silently skipping it.
45
+ - When no external resources are present, skip this step silently.
46
+
47
+ ## Step 2 — Detect gaps
48
+
49
+ Scan the loaded content and identify each gap. The minimum required gap detectors are:
50
+
51
+ | Gap kind | Detection signal |
52
+ |---|---|
53
+ | Missing acceptance criteria | No `## Acceptance criteria` section, or section is empty / stub |
54
+ | Missing scope boundary | No explicit in-scope / out-of-scope statement or non-goals section |
55
+ | Unresolved primary actor | No named user, system, or role that is the main beneficiary of the feature |
56
+ | Undocumented hard-to-reverse decision | Destructive or irreversible operations described without a rationale or rollback note |
57
+
58
+ Additional gaps discovered through semantic reading of the spec are also recorded.
59
+
60
+ For each gap, classify it as either:
61
+ - **Bounded choice** — the answer is one of a small discrete set (e.g. yes/no, A/B/C).
62
+ - **Open-ended** — the answer requires free-form elaboration.
63
+
64
+ ## Step 3 — Fill gaps
65
+
66
+ ### Interactive mode (default)
67
+
68
+ For each gap, in order:
69
+
70
+ - **Bounded choice gap:** use `AskUserQuestion` with the question text and the choice options, plus an "Other / free text" option. Block until the user answers.
71
+ - **Open-ended gap:** present the question as a plain text turn. Block until the user answers.
72
+
73
+ Record each answer with its source: `human`.
74
+
75
+ > `AskUserQuestion` is a Claude Code–native construct. If you are running outside Claude Code, use `--auto` mode instead.
76
+
77
+ ### Auto mode (`--auto`)
78
+
79
+ Answer every grilling question yourself without prompting the user. Source answers from (in priority order):
80
+
81
+ 1. **`codebase`** — inspectable source files, tests, scripts, config in the repository.
82
+ 2. **`docs`** — markdown files under `docs/`, `skills/docs/`, and adjacent contract docs.
83
+ 3. **`context`** — `CONTEXT.md` at the repo root, if present. When absent, skip silently — do not crash, no warning required.
84
+ 4. **`inferred`** — reasoning from the issue/plan text alone, with no external citation.
85
+
86
+ Record the evidence source for every answer. Flag a question as **`unresolved`** when:
87
+ - The only available source is `inferred`, **and**
88
+ - No codebase path, doc section, or issue/plan text can be cited as the basis for the answer.
89
+
90
+ Do not silently guess an `inferred` answer when no evidence can be cited — flag it `unresolved` instead.
91
+
92
+ ## Step 4 — Write back (replace-section semantics)
93
+
94
+ Write a `## Grill findings` section back to the source artifact using **replace-section** semantics:
95
+
96
+ - **Find** the existing `## Grill findings` section: the range from the `## Grill findings` heading through the next `##`-level heading (exclusive) or end of file.
97
+ - **Replace** that range in place with the new section content.
98
+ - If no `## Grill findings` section exists, **append** it.
99
+ - This makes re-runs idempotent — no accumulated noise, no duplicate sections.
100
+ - If parsing the section boundary fails, **abort with an error** rather than silently truncating.
101
+
102
+ **Tracker-first write-back:** update the GitHub issue body using:
103
+
104
+ ```
105
+ gh issue edit <n> --repo <owner/repo> --body-file <tmp-path>
106
+ ```
107
+
108
+ The `## Grill findings` section lives in the issue body, not as a comment. Do not use `comment-issue.mjs` here — that creates a comment, not a body update.
109
+
110
+ **GitHub body size guard:** issue bodies are capped at 65,536 characters. Before writing back, check whether the updated body would exceed this limit. If so, warn: `Warning: updated body would exceed GitHub's 65,536-character limit — write-back skipped. Truncate the findings or the issue body manually.` Do not silently truncate.
111
+
112
+ **Local-planning write-back:** update the plan file in place using the edit tool.
113
+
114
+ ## Output artifact format
115
+
116
+ The `## Grill findings` section written to the artifact:
117
+
118
+ ```markdown
119
+ ## Grill findings
120
+
121
+ <!-- loop-grill: <timestamp> mode:<interactive|auto> -->
122
+
123
+ ### Resolved gaps
124
+
125
+ | # | Gap | Question | Answer | Source |
126
+ |---|-----|----------|--------|--------|
127
+ | 1 | Missing AC | <question text> | <answer text> | codebase \| docs \| context \| inferred \| human |
128
+
129
+ ### Unresolved gaps
130
+
131
+ | # | Gap | Question | Reason unresolved |
132
+ |---|-----|----------|-------------------|
133
+ | 1 | Unresolved primary actor | <question text> | No citable evidence found |
134
+
135
+ ### Verdict
136
+
137
+ grill-clean
138
+ ```
139
+
140
+ Replace `grill-clean` with `N unresolved items` when unresolved gaps remain.
141
+
142
+ ## Step 5 — Emit verdict
143
+
144
+ After write-back, emit the verdict line to stdout:
145
+
146
+ - `grill-clean` when no unresolved gaps remain.
147
+ - `N unresolved items` (e.g. `3 unresolved items`) when gaps remain after all questions are answered.
148
+
149
+ ## CONTEXT.md degradation rule
150
+
151
+ When `CONTEXT.md` is absent from the repo root, skip the context-source check silently. Do not crash. Do not emit a warning. The grill continues with the remaining sources (`codebase`, `docs`, `inferred`).
152
+
153
+ ## Idempotency guarantee
154
+
155
+ Running `/loop-grill` twice on the same target must produce a single `## Grill findings` section, not two. The replace-section logic handles the already-present-section case. On the second run, if the gap set is identical to the first run, the section content is replaced with an equivalent section (same questions, same answers, updated timestamp).
156
+
157
+ ## Non-goals
158
+
159
+ - Auto-triggering from `issue_intake` — this is on-demand only.
160
+ - Replacing or modifying the in-loop docs-grill (`docs/docs-grill-step.md`, `scripts/loop/docs-grill-contract.mjs`) — different concern, different firing surface.
161
+ - Full DDD `CONTEXT.md` management.
162
+ - Scheduling or storing grill runs — stateless and on-demand.
163
+ - Any CI/CD integration.
package/AGENTS.md CHANGED
@@ -23,3 +23,4 @@
23
23
  - Keep workflow procedure out of AGENTS; put shared contracts under `skills/docs/`. Brief, high-signal main-agent dispatch contracts (e.g., sequential dispatch) are the narrow exception — they stay in AGENTS.md because AGENTS.md is the canonical main-agent instruction surface.
24
24
  - No PR scope has gate exemptions (#579): see skills/dev-loop/SKILL.md "No gate exemptions" section.
25
25
  - **Sequential dispatch contract (#693):** When the user says "sequential", "one at a time", "serially", or "in order" with multiple `dev-loop` targets, the main agent must dispatch `dev-loop` async subagents one at a time — each blocking on prior completion before the next starts. This is deterministic: the keyword must always produce serial execution, never concurrent. The main agent is the sequencer; do not dispatch all targets at once.
26
+ - **Harness-agnostic non-regression (#1086):** dev-loops is harness-agnostic (Pi + Claude Code). Any harness-specific change MUST NOT regress other harnesses — add/keep cross-harness regression coverage. Canonical procedure: [Cross-Harness Regression Contract](skills/docs/cross-harness-regression-contract.md).
package/CHANGELOG.md CHANGED
@@ -4,6 +4,101 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.7.1 - 2026-07-05
8
+
9
+ ### Changed
10
+
11
+ - **`captureDiffFromBase` pins `diff.renames`/`diff.algorithm`/`diff.context` and `core.abbrev`/`core.autocrlf` for cross-environment reproducibility (#1168).** The isolation `-c` overrides for the persisted `--base` neutral diff seed already made the diff byte-identical within a single run, but a machine with a contrary local gitconfig could still produce different bytes for the same base/HEAD pair — and, via `diff.renames`, a different `scope.changedFiles`/`adjacentCode` membership (rename vs delete+add). The five settings are now pinned alongside the existing overrides.
12
+
13
+ ### Removed
14
+
15
+ - **Dead `isFenceLine` field dropped from `stepFence` (#1166).** `stepFence` (`packages/core/src/loop/issue-refinement-artifact.mjs`) computed and returned `isFenceLine` on every branch, but none of its three call sites (`parseMarkdownSections`, `extractChecklistItems`, `sectionHasBody`) read it. Removed from the return shape and its JSDoc; `fence`/`insideFence` and all fence-detection logic are unchanged.
16
+
17
+ ### Fixed
18
+
19
+ - **Worktree provisioning creates the `@dev-loops/core` workspace link so `scripts/**` CLIs validate the branch's core (#1144).** Worktrees provisioned by `ensure-worktree.mjs`/`provision-worktree.mjs` have no `node_modules`, so any `scripts/**` CLI importing `@dev-loops/core` resolved the bare specifier by walking up to the MAIN checkout's `node_modules/@dev-loops/core` instead of the worktree's own `packages/core` — `npm run verify` in a worktree silently tested main's core, not the branch's. Provisioning now creates the relative symlink `node_modules/@dev-loops/core → ../../packages/core` unconditionally: idempotent on a correct existing link, replaces a stale/broken link, never clobbers a real file/dir already at the destination, and is reported in the provisioning summary.
20
+ - **Sibling logical columns resolved via `loadStateColumnMap` in pickup and archive (#1143).** `resolve-active-board-item.mjs` hardcoded the literal `"In Progress"` column for the live `/loop-continue` pickup query instead of resolving it through `loadStateColumnMap`/`LOGICAL_COLUMN.IN_PROGRESS` the way #1142 already did for Next Up, so a repo overriding `queue.statusColumns.in_progress` would have pickup silently miss the active item. An audit found the same gap in `archive-done-items.mjs` (`selectArchivable()` hardcoded `"Done"`). Both now resolve through the shared mapping, fail closed on a malformed `.devloops`, and honor configured overrides; `--help`/display literals in other scripts were left as-is (illustrative examples, not routing logic).
21
+ - **The post-convergence significance detector is content-aware, so comment/JSDoc-only changes don't reopen Copilot past the round cap (#1137).** Significance in `_post-convergence-change.mjs` (shared by `copilot-pr-handoff` and `detect-pr-gate-coordination-state`) was classified by path/size only, so a trivial comment fix could re-trip a re-request past the cap. A new `isCommentOnlyFileChange` classifier inspects the compare diff's patch hunks with a per-hunk block-comment state machine (reset at every `@@` header, conservative toward "significant") and filters comment-only JS/TS files out before the existing `>=20 lines`/`>=2 files` thresholds. A file with no `patch` (binary/oversized) is never treated as comment-only; the stale `+++`/`---` header-skip (never present in `gh compare` patches) was removed.
22
+ - **`copilot-pr-handoff` fails closed on a pending Copilot review at the round cap (#1165).** At the cap with a `--force-rerequest-review` in flight, handoff routed to `ROUND_CAP_CLEAN_FALLBACK` ("continue to pre_approval_gate") whenever the fail-silent secondary reopen-facts fetch (`gh pr view` + `gh compare`) failed — silently reading significance as false — while `detect-pr-gate-coordination-state`, reusing its already-validated PR data, correctly gated; the loop follows handoff as the pre-flight authority, so a significant change could skip Copilot's review (hit live on PR #1164). While a requested review is pending on the current head at the cap, handoff now surfaces `waiting_for_copilot_review` (action `watch`) before the fragile escape-hatch fetch can downgrade to "proceed"; a `--watch-status` refresh skips the guard so a request that never resolves re-settles to the clean fallback and the loop cannot dead-end.
23
+ - **Merge-block hook message explains that evidence writes and `gh pr merge` must be separate tool calls (#1172).** The "vanishing" gate-evidence ledgers on PRs #1167/#1169/#1171 were never swept: the PreToolUse bash gate evaluates `gh pr merge` before the tool call executes, so a compound command that both writes gate evidence (`write-gate-findings-log`/`upsert-checkpoint-verdict`) and merges in the same call is blocked at hook time — the write never runs. `decideBashGate` now appends a hint to the deny message in exactly that compound case (a bare `gh pr merge` keeps the standard message; no allow/deny boundary changes), and the write → verify → merge-alone contract is documented in `skills/docs/merge-preconditions.md`.
24
+ - **Light-mode-aware pre-merge gate accepts scoped inline verdicts (#1174).** `buildPreMergeGateCheck` rejected any `inline_single_agent` verdict whenever `gates.requireFanoutEvidence` was on (the repo default), so a genuinely under-threshold micro-PR's #1043 light-path verdict was unmergeable without a full fan-out (hit live on PR #1173, 1 file / 11 lines). An inline verdict is now accepted only when lightMode is enabled, the merge-base scope re-derived at merge time is under threshold (underivable scope → rejected, fail-closed), no `gate:full` label is present, and a non-empty inline reason is recorded; the findings-log ledger is still required, and the fan-out path (`fanout_fanin` verdicts, ledger, provenance enforcement) is unchanged.
25
+ - **`validate-pr-body-spec` enforces a `Closes #N` closing-keyword reference (#1181).** Five lightweight PRs merged without auto-closing their issues because the PR-body-as-spec validator never required the linkage. It now fails closed with `missing_closing_issue_reference` when the body carries no GitHub closing-keyword issue reference, supports `--expected-issue <n>` (`closes_wrong_issue` on a mismatch), and reports the extracted `closesIssues` — all keyword variants and the cross-repo `owner/repo#N` form accepted, while references inside fenced code blocks or inline code spans don't count.
26
+
27
+ ## 0.7.0 - 2026-07-05
28
+
29
+ ### Added
30
+
31
+ - **Lightweight local-first path — PR body as spec-of-record, no phase doc (#1025).** Local-first now supports a lightweight path for chores, bugfixes, and small mechanical changes where the PR description itself is the spec-of-record — no phase/plan doc is minted or committed. When active, `loop startup` no longer requires `--plan-file` and the pre-approval review agent reads the PR body (objective, in-scope/non-goals, testable acceptance criteria, definition of done, risks) instead of resolving a plan-file path; a new `validate-pr-body-spec` CLI enforces the required sections fail-closed. The gate sequence is unchanged (draft → pre-approval fanout → detect-evidence → human merge); only the backing artifact differs. Closes the phase-history pollution from minting a numbered phase doc for a six-file rename chore.
32
+ - **`lightMode` wired to gate dispatch — micro-PRs skip full fanout (#1043).** A PR under the `localImplementation.lightMode` threshold (`maxFiles`/`maxLines`, and no `gate:full` label) collapses the `draft_gate` + `pre_approval_gate` fanout to a single inline correctness + no-op check and skips Copilot polling; the draft→ready boundary is still recorded (`requireDraftFirst`). Two escalation paths: the inline check auto-escalates to full fanout on any finding at or above `worth-fixing-now`, and the maintainer-controlled `gate:full` label forces full fanout regardless of size. Scope detection fails closed — a diff whose size can't be determined runs the full fanout, not the light path.
33
+ - **Context-builder additive angle authority (#1048).** Gate angle resolution was subtractive-only (it could drop configured angles but never add one). With `gates.<gate>.additiveAngles` enabled (default off), the resolver may now pull in a review lens the diff's change categories imply even when it isn't in the gate's configured pool — drawn from `gates.anglePool` (or the union of known personas). Bounded and auditable: `excludeAngles` stays a hard ceiling, `mandatoryAngles` the floor, and each addition is recorded with an `"added"` rationale naming the triggering category alongside the existing kept/dropped entries.
34
+ - **Grouped board-summary mode for `queue list` (#1061).** `dev-loops queue list --summary` (alias `--group-by status`) returns a whole-board digest grouped by Status column in board order (`{ ok, groups: { <status>: { count, items } } }`), with `--done-limit` to cap the Done group, composing with `--jq`/`--silent`. One sanctioned call for the by-status overview that previously tempted an inline `| python3` grouping (retro rule 5); `--summary` is mutually exclusive with `--column`/`--limit`.
35
+ - **Cross-harness regression contract (#1086).** dev-loops is harness-agnostic (Pi + Claude Code); the rule "any harness-specific change MUST NOT regress other harnesses" is now a repo-wide working rule in `AGENTS.md`, with the detailed procedure in `skills/docs/cross-harness-regression-contract.md` (what counts as a harness-specific change, the required Claude-Code-path suites, and the additive-on-Pi / no-op-or-validated-on-Claude bar). Promotes the constraint from an ad-hoc section of #1084 to a universal, pre-0.7 enforced contract.
36
+
37
+ ### Changed
38
+
39
+ - **`LOGIC_CHANGE` maps to a core review subset instead of fallback-to-all (#1049).** `dynamicAngles` was effectively decorative for code PRs: any non-trivial code change classified as `ambiguous`, which fell back to the full 15-angle draft pool. `LOGIC_CHANGE` now maps to a core-for-code angle subset in `CATEGORY_ANGLE_MAP` and no longer forces `ambiguous`/fallback-to-all (reserved for genuinely unclassifiable diffs); other categories still union in their mapped angles. A typical code PR now resolves to a handful of angles, with peripheral lenses (link-check, config-drift, etc.) appearing only when the diff implicates their category. Mandatory floor and exclude ceiling unchanged; each dropped angle keeps its recorded rationale.
40
+ - **`--jq`/`--silent` are now a base-CLI guarantee for every JSON-emitting command (#1071).** The output contract was per-script opt-in, so JSON-emitting commands like `sync-item-status.mjs` rejected `--jq` and forced grep/inline-parse workarounds. Every JSON-emitting command now routes through the shared emit path (`scripts/lib/jq-output.mjs`), so `--jq` filtering, invalid-filter fail-closed (exit 2), and `--silent` exit-code-only come for free; a contract test enforces it so new commands inherit the guarantee and can't regress.
41
+ - **Sanctioned operation→wrapper command map baked into the default handoff (#1081).** Which dev-loops wrapper to use for which operation (reads: `view-pr`/`probe-ci-status`/`fetch-ci-logs`/`list-issues`/`probe-copilot-review`; edits: `edit-pr`/`comment-issue`; lifecycle: `ready-for-review`/`create-pr`/`request-copilot-review`/`reply-resolve`/gate-verdict) is now a structural part of every dev-loop handoff, sourced once (`scripts/loop/sanctioned-commands.mjs`, carried via the handoff envelope + `agents/dev-loop.agent.md`) instead of an ad-hoc per-handoff decision — so a spawned subagent no longer re-derives the map or rediscovers a wrapper mid-run.
42
+ - **Playwright configs consolidated into one projects-based, registry-driven config (#1056).** The five byte-identical `playwright.*.config.mjs` files (one per UI slice) collapse to a single `playwright.config.mjs` with one Playwright project per registered slice; `test:playwright:<slice>` scripts run `--project=<slice>`. Adding a UI artifact now needs only a registry entry (already required by the ui-e2e-scoping gate) and no new config file; per-slice output dirs / report paths are preserved.
43
+ - **Deduplicated the three Playwright smoke jobs via a composite action (#1058).** `viewer-smoke` / `deck-smoke` / `article-smoke` shared copy-pasted setup (checkout, Node, `npm ci`, WebKit cache-restore, `playwright install`). The shared setup moves into `.github/actions/playwright-webkit/action.yml`, keeping the three separate jobs and their zero-cost `changes`-gated skips (a matrix would have paid setup on skipped legs). `UI_E2E_CHECK_NAMES` and the scoping contract are unaffected.
44
+ - **Faster `test:scripts` real-git tests (#1129).** Real-git fixtures paid `git` fsync on every object write; `test:scripts` now runs with `core.fsync=none` / `core.fsyncObjectFiles=false` (via `GIT_CONFIG_*`), cutting the process-spawn-heavy suite's wall time with no behavior change. The originally-proposed concurrent single-pool `verify` runner was reverted (oversubscription risk); the fsync disable is the kept win.
45
+
46
+ ### Removed
47
+
48
+ - **`mermaid` is no longer a runtime dependency (#1089).** The inspect-run viewer serves the same pinned `mermaid.min.js` (11.15.0) from a vendored copy at `scripts/loop/inspect-run-viewer/vendor/mermaid.min.js` instead of resolving it out of `node_modules`; the `/assets/mermaid.min.js` route, `loadMermaidBrowserScript` seam, and offline behavior are unchanged. The vendored bundle is sha256-pinned by test (`test/loop/inspect-run-viewer-client.test.mjs`) and `test/extension-package-contract.test.mjs` guards that mermaid stays out of `dependencies`. Net install footprint shrinks (the mermaid transitive tree no longer installs); the npm tarball grows ~3.3MB unpacked from the vendored asset.
49
+ - **Breaking: dead-code deletion — ponytail audit batch 1 (#1088).** Removed unused public surface with no in-repo callers: the `@dev-loops/core` export subpaths `./debt/signals`, `./debt/score`, `./debt/finding`, and `./refinement/ac-dod-matrix` along with the now-dead `refinementContract` handoff-envelope plumbing (builder pass-through and validation in `handoff-envelope.mjs`) that only that module fed (the underlying `debt/score.mjs` module remains and is still tested internally); the `dev-loops-capture-deep-persona-signals` bin and the whole deep-persona-signals feature (`packages/core/src/debt/deep-persona-signals.mjs`); `createClaudeExtensionAdapter` (removed from the `@dev-loops/core/harness` exports along with its `claude-extension-adapter.mjs` module); the dead OUTER-graph tables `OUTER_STATE_TO_ROUTING_OUTCOME`, `OUTER_STATE_TO_OUTER_ACTION`, and `OUTER_NONTERMINAL_STATES` from `@dev-loops/core/loop/conductor-routing`; the config surface `localPlanning.plansDir` with its resolver `resolvePlansDir`, plus the unrelated dead config resolvers `resolveInternalPathPatterns` and `resolveMaxFanoutReviewers`; and the script CLIs `scripts/loop/conductor.mjs`, `scripts/loop/detect-stale-runner.mjs`, and `scripts/loop/pre-push-main-guard.mjs` (with their tests). Deprecation shim: a `localPlanning` block in a consumer `.devloops` file is still **accepted and ignored** (tolerated deprecated key in both zod schemas and `schemas/dev-loop-config.schema.json`) so existing configs keep parsing; remove the block at your convenience.
50
+
51
+ ### Fixed
52
+
53
+ - **Guard `draft_gate` self-heal against unbounded recursion on a lagged draft-state read (#1020).** `upsert-checkpoint-verdict`'s `postDraftGateViaDraftTransition` converts a `ready` PR to draft, re-posts the verdict, then restores ready — but when GitHub's draft-state read lagged the conversion mutation, the re-entry saw the PR still non-draft and recursed, hanging with a swallowed Node exit 13. A `_draftTransitionInProgress` reentrancy guard now breaks the loop and fails closed with the complete manual recovery commands instead of hanging.
54
+ - **Resolve repo root and ledgers worktree-relative, not from `process.cwd()` (#1052; folds #1050 and #1019).** Under the normal per-PR-worktree flow the session cwd and the PR worktree are different checkouts, so cwd-bound reads/writes silently diverged: the pre-merge fan-out ledger check bound to the session cwd and false-blocked clean gates (#1050, hit PR #1044 twice), and `detect-copilot-loop-state` read `maxCopilotRounds` from cwd instead of the worktree `.devloops` (#1019). A shared worktree resolver now derives the repo root from the PR worktree, so the ledger writer and the pre-merge check bind to the same checkout regardless of session cwd. Pure resolution-location correctness — no gate semantics or ledger-format change.
55
+ - **Block subagent-initiated external writes (#1051).** Gate/dev-loop subagents had twice minted unauthorized, user-attributed GitHub issues. The bash gate now blocks raw `gh issue create` / `gh issue comment` / `gh pr comment` on the target repo when initiated by a subagent and directs to the sanctioned path; contract-driven writes (gate-verdict comments, reply-resolve review threads, board sync) and the main-agent/operator path are unaffected. Unit + e2e coverage mirrors the `gh pr create` guard.
56
+ - **Wrapped `gh pr view` / `gh pr edit` so the retro internal-tooling gate is satisfiable (#1057).** The remaining PR-facing reads/edits a real gate run needs had no dev-loops wrapper, so an honest run could never record a clean retrospective (`internalToolingOnly: true`, `rawCallViolations: []`) and the gate blocked code-clean PRs by waiver. New `scripts/github/view-pr.mjs` and `edit-pr.mjs` (jq-output-backed, `--jq`/`--silent`) complete the coverage; `workflow.requireRetrospectiveInternalTooling` is re-armed to `true`.
57
+ - **Tick verified PR-body acceptance-criteria checkboxes after a clean `pre_approval_gate` (#1066).** The gate verified acceptance criteria but only updated the linked issue, so merged PRs displayed all `- [ ]` unchecked, reading as never-confirmed. A single `gh pr edit --body-file` now flips `- [ ]` → `- [x]` for the items the gate positively confirmed (CRLF preserved, duplicate flipped labels de-duped, only verified items ticked, unconfirmable items left unchecked — fail closed).
58
+ - **Key fresh-context review sentinels per head SHA / round (#1095).** On a `draft_gate` retry after a fix commit, the round-1 per-(cwd, scope) sentinel from `verify-fresh-review-context.mjs` was still present, so every retry reviewer failed closed and burned a launch to refuse. The sentinel key now includes the head-SHA round component, so a new head never collides with a prior round while same-round re-entry still fails closed; the orchestrator-owned sentinel lifecycle is documented in the gate-review sub-loop contract.
59
+ - **Honor `queue.statusColumns.next_up` across ordering, pickup, and the projects layer (#1098).** Board-sync resolved logical columns through the `statusColumns` display-name overrides, but the ordering (`resolveNextUpOrder`) and `add-queue-item --next-up` hardcoded the literal `"Next Up"`, so a repo that renamed its Next Up column got a driver querying a non-existent column (fail-closed board-query-error or empty). The logical→display resolution now flows through the same shared mapping everywhere; board-sync stays the single source of the mapping.
60
+ - **Auto-release the runner-coordination lock on run completion + document stale-lock takeover (#1109, Seam 1 of #1084).** A completed/stopped run left its `.pi/runner-coordination/<repo>/pr-<n>.json` claim in place, so a merge-authorized re-dispatch (fresh run id) was refused `ownership_lost` — hit live on a 9.5h-old stale lock. A run now releases its coordination claims on completion/stop, and the stale-lock takeover path is documented; a genuinely active (non-stale) claim still fails closed.
61
+ - **Record fan-out provenance + opt-in enforcement, route-to-conductor (#1110, Seam 2 dev-loops of #1084).** `requireFanoutEvidence` was artifact-only, so a single agent could self-produce every per-angle review plus a ledger and label the verdict `fanout_fanin`. The findings-log ledger now records fan-out provenance (distinct reviewer count and dispatch provenance), and an opt-in `requireFanoutProvenance` check in `buildPreMergeGateCheck` re-validates it (internally consistent across checkouts, `distinctReviewers >=` the floor) to distinguish real parallel review from single-agent artifacts; a child that cannot fan out fails closed with a route-to-conductor message.
62
+ - **Fix `tools:` frontmatter so pi provisions the subagent tool at child depth (#1111, Seam 2 core of #1084).** pi-subagents parses the `tools:` frontmatter line with a naive comma split (no real YAML), so the flow-sequence form `tools: [read, …, subagent]` read `subagent]` (bracket attached) and never matched — child fan-out was impossible on pi. The `tools:` lines in `agents/*.agent.md` and `.pi/agents/*.agent.md` drop the brackets for the comma-scalar form; the generated `.claude/` mirror regenerates byte-identical (`normalizeToolList` already splits on commas), so the fix is additive on pi and a no-op on Claude Code (governed by the #1086 cross-harness contract).
63
+ - **Make `copilot-pr-handoff` and gate-coordination agree past the Copilot round cap (#1126).** Round-cap enforcement lived only in `copilot-pr-handoff.mjs`, so after `maxCopilotRounds` was reached `detect-pr-gate-coordination-state.mjs` still surfaced `rerequest_copilot_review` — advertising a re-request the handoff would refuse. Both now defer to a shared round-cap + significant-change detector, so they agree at the cap boundary.
64
+ - **Treat body-mention cross-references as non-owning board links (#1130, #1124).** `detect-linked-issue-pr` accepted a bare `CrossReferencedEvent` as an issue's linked open ready PR, but that event fires on any body mention (`Seam 1 of #1084`, `part of #X`), so a sub-issue-tree PR dragged every mentioned sibling into In Progress and jammed `resolve-active-board-item` with "multiple in-progress". An owning link now requires `willCloseTarget: true` (or a `ConnectedEvent`); a direct `gatherLiveFacts` open-issue→linked-ready-PR unit test (#1124) covers the reconcile bridge.
65
+ - **Run gate fan-out reviewers in the PR's actual worktree/head (#1135).** Reviewers dispatched with worktree isolation got a fresh checkout of stale `main` and no access to the gitignored `tmp/gate-context` bundle, so a reviewer could silently review the wrong tree without the seeded context. The read-only per-angle review dispatch now runs in the PR worktree/head with the seeded gate-context bundle (a worktree-locality guard rejects an out-of-tree `--context-path`), honoring the build-once/seed-many contract.
66
+ - **`write-gate-context --base` builds the diff + adjacentCode bundle on the CLI path (#1140).** The library built the "build once, seed many" bundle only when handed a diff; the CLI exposed no diff flag, so every CLI-driven gate wrote a thin briefing (`scope.diffPath: null`, empty `changedFiles`, no `adjacentCode`) and each of N reviewers re-ran the same `git diff`. A new `--base <ref>` flag captures the full diff plus the per-file 1-hop adjacent-code bundle once (`scope.diffSource="base"`), so every reviewer is seeded with the identical neutral bundle; without it the CLI still emits an explicit thin briefing.
67
+ - **Fix `manage-sub-issues reorder` always failing (#1160).** Reorder seeded its cursor with `after_id=0`, but GitHub's priority endpoint rejects `0` with an empty-body HTTP 500, which `gh` surfaced as `unexpected end of JSON input` — the loop aborted on the first call so no reorder ever succeeded. The first item now uses `before_id=<current head>` (or skips when it is already first) and chains `after_id` after; `ghApi` also surfaces the HTTP status on empty-body non-2xx responses.
68
+ - **Eliminated the version-skew class: pinned consumer-facing CLI invocations + release-time core-version assertion (#1036, root cause of #1033).** Every consumer-facing CLI example now pins `npx dev-loops@<version>` (single source: the package version) instead of a bare `npx dev-loops` that resolves a possibly-stale global/cached copy: `README.md`, `docs/migrating-to-dev-loops.md`, and the `cli/index.mjs` help/setup strings. Global `npm install -g dev-loops` is reframed as an optional, drift-prone shell convenience — explicitly not the supported invocation path. A new fail-closed contract test (`test/contracts/cli-invocation-contract.test.mjs`) bans bare `npx dev-loops` across the README/docs/CLI surface. The Pi extension already resolves the CLI and `@dev-loops/core` from the installed pinned package (module imports, updated via `pi update git:...`), never a global install; `extension/README.md` now states that lockstep contract. Root-cause fix for the mis-published-bundle failure mode: `scripts/release/assert-core-dependency-version.mjs` (node-builtins-only) fails the release when root `dev-loops`'s `@dev-loops/core` dependency major.minor differs from the version being released, wired as a step in `.github/workflows/release.yml` before the GitHub Release is created; a unit test (`test/docs/assert-core-dependency-version.test.mjs`) rejects the #1033-shaped bad manifest (`0.6.2` / `^0.2.6`) and guards the real manifest. Non-goals (YAGNI): no per-startup runtime skew watcher, no auto `npm i -g` on update.
69
+
70
+ - **Bash gate now blocks raw `gh pr create`, closing the draft-first hole.** The PreToolUse bash gate already blocked `gh pr ready`/`gh pr merge` without evidence, but nothing guarded PR *creation* — so raw `gh pr create` (which defaults to ready-for-review) silently bypassed the `workflow.requireDraftFirst` contract. It now denies raw `gh pr create` on the target repo and directs callers to the canonical wrapper `scripts/github/create-pr.mjs` (`dev-loops pr create`), which always drafts and self-assigns. The wrapper runs `gh pr create` inside a node child process, so its command is unaffected; an explicit non-target `--repo` and non-target-repo cwd pass through. The create gate now denies an explicit `--repo` targeting the repo regardless of cwd (#1047) — raw `gh pr create --repo <target>` run from outside the repo previously slipped through. New `commandContainsGhPrCreate`/`extractRepoFlagFromGhPrCreateAnywhere` in `packages/core/src/loop/bash-command-classify.mjs`, a create branch in `decideBashGate` (`packages/core/src/claude/hook-decisions.mjs`), and detection wiring in `.claude/hooks/pre-tool-use-bash-gate.mjs`; unit + e2e coverage added. The `gh pr <verb>` matcher now also treats newlines/`\r` as segment separators and normalizes a leading env-assignment (`GH_TOKEN=x`), `command`/`env`/`exec` wrapper, or absolute/relative gh path before the `^gh` test — closing the same bypass gap identically for `gh pr ready`/`gh pr merge` (subshell/group forms remain out of scope). The create gate no longer short-circuits ready/merge gating in compound commands: an out-of-scope create (e.g. `gh pr create --repo other/repo && gh pr merge 5`) now falls through so the gated ready/merge segment is still evaluated instead of being allowed early. The create gate now evaluates each create segment's scope so a leading out-of-scope create can't shield a later in-scope raw create.
71
+
72
+ - **`strategy.default: local-first` now respected end to end as the built-in code default (#1033).** `BUILT_IN_DEFAULT_TARGET_PREFERENCE` and `BUILT_IN_DEFAULTS.strategy.default` changed from `github-first` to `local-first` in `packages/core/src/loop/public-dev-loop-routing.mjs` and `packages/core/src/config/config.mjs`. The `resolveTargetPreference` fallback in `scripts/loop/resolve-dev-loop-startup.mjs` (line 267) and the config-load-error fallback (line 675) were also changed from `prefer_github_first` to `prefer_local`, so configless repos now resolve to local-first through the CLI path too. The 32 tests that previously relied on the implicit github-first default were updated to pin `PREFER_GITHUB_FIRST` explicitly; one config test assertion and one routing-config test updated to match the new built-in default. Docs updated: `targetPreference` table in `skills/docs/public-dev-loop-contract.md` now shows `prefer_local (default)`; `BUILT_IN_DEFAULTS` description in `skills/docs/artifact-authority-contract.md` updated to `local-first`.
73
+
74
+ ## 0.6.2 - 2026-06-30
75
+
76
+ ### Changed
77
+
78
+ - **Terminology pass: 'named human/person' → 'contributor' across all doc surfaces (#1026).** Articles (`docs/articles/dev-loops-deep-dive.{md,html}`, `introducing-dev-loops.html`), decks (`docs/presentations/dev-loops-deep-dive.html`, `introducing-dev-loops.html`, `applied-dev-loops-presentation.md`), `skills/docs/merge-preconditions.md`, `schemas/dev-loop-config.schema.json`, and `packages/core/src/config/config.mjs` docstring. Deep-dive article subtitle tightened; attention-bottleneck section rewritten for clarity. `.md`/`.html` pair synced (`fast enough to be routine`).
79
+
80
+ ### Fixed
81
+
82
+ - **`deriveUiE2ePassed` blocked the gate on SKIPPED UI e2e checks (#1026).** `viewer-smoke` is `SKIPPED` (not `FAILURE`) on PRs that don't touch viewer files — it means "not applicable to this run." The function now treats `SKIPPED` the same as `SUCCESS` so a skipped check no longer incorrectly blocks `run_pre_approval_gate`. Test coverage added.
83
+
84
+ - **`plugin.json` was missing from the v0.6.1 version bump (#1026).** `.claude/.claude-plugin/plugin.json` was still at `0.6.0`; bumped to `0.6.1` (now `0.6.2` in this release). Stale generated `.claude/` assets regenerated.
85
+
86
+ ## 0.6.1 - 2026-06-30
87
+
88
+ ### Changed
89
+
90
+ - **Docs: refresh intro article + deck to the 0.6.0 command surface + current velocity snapshot (#1015, #1018).** `/dev-loops:continue` now shows both forms — `/dev-loops:continue 112` (issue or PR) and bare `/dev-loops:continue` (resumes the single in-progress board item, fail-closed on 0/multiple). Adds `/dev-loops:start-spike "…"` to the command list in both the intro article (`docs/articles/introducing-dev-loops.{md,html}`) and the intro deck (`docs/presentations/introducing-dev-loops.html`). The Pi command set in the catch-all router note now lists `start-spike`. The velocity snapshot paragraph is updated from the previous "two-week, ~100 PR" estimate to a verified four-day snapshot (87 PRs, ~22/day, v0.4.0 + v0.5.0).
91
+
92
+ - **Deep-dive deck: desktop responsiveness, mandatory snap, keyboard nav (#1021, #1022).** Desktop layout adjusts for wider viewports. Section scroll-snap is enforced. Keyboard navigation is enabled.
93
+
94
+ - **Namespace Claude Code slash commands with `loop-` prefix to avoid collisions (#1023).** The 6 dev-loop commands (`.claude/commands/`) are now `/loop-start`, `/loop-auto`, `/loop-continue`, `/loop-start-spike`, `/loop-info`, `/loop-status` — renamed from bare `/start` etc. which collided with Claude Code built-ins (notably `/status`). Source files renamed from `commands/*.command.md` to `commands/loop-*.command.md`; generated assets updated; no-drift test updated.
95
+
96
+ ### Fixed
97
+
98
+ - **`release.yml` auto-Release never ran — the CHANGELOG extractor pulled in `@dev-loops/core` (#1016, regression in #996's first run).** `scripts/release/extract-changelog-section.mjs` imported `isDirectCliRun` from `scripts/_core-helpers.mjs`, which re-exports from the `@dev-loops/core` workspace package. `release.yml` runs the extractor with **no `npm ci`** (a text parser needs no install), so on a `v*` tag push the import `ERR_MODULE_NOT_FOUND`ed before any notes were extracted and the GitHub Release was never created (the same hands-off-publish gap #996 set out to close). The extractor now imports **only `node:` builtins** — `isDirectCliRun` is inlined (`node:fs` `realpathSync` + `node:url` `fileURLToPath`), all behavior unchanged (`--version`/`--changelog`, leading-`v` strip, heading lookahead so `0.5.1` ≠ `0.5.10`, stop-at-next-`## `, no Unreleased bleed, exit 1 absent/empty, exit 2 arg/file error, the `extractChangelogSection()` export). A new guard test parses the script's `import` statements and asserts every specifier is `node:`-prefixed, so a future workspace import can't silently reintroduce the deps-free break. `release.yml` is unchanged — the standalone script is the fix.
99
+
100
+ - **Close `gh pr merge` bypass hole in the PreToolUse gate (#1024).** A hand-run `gh pr merge` could skip the pre-approval gate entirely because the PreToolUse Bash hook only blocked `gh pr ready`. The hook now also blocks `gh pr merge` unless `detect-checkpoint-evidence.mjs` confirms clean current-head `draft_gate` + `pre_approval_gate` evidence (fanout_fanin, no inline verdicts). Two compound-command bypass paths are also closed: (1) the all-segments scanner (`commandContainsGhPrReady`/`commandContainsGhPrMerge`) catches gated verbs in any shell segment, not just the first; (2) `gh pr ready N && gh pr merge N` no longer short-circuits to ready-only — both verbs are detected independently and the stricter merge gate is applied. Pi extension public API (`isGhPrReadyCommand`, extractors) retains first-segment-only semantics (correct: `false && gh pr ready 42` short-circuits so ready never ran).
101
+
7
102
  ## 0.6.0 - 2026-06-29
8
103
 
9
104
  ### Added
package/README.md CHANGED
@@ -49,17 +49,17 @@ The same entrypoints are also available as direct named commands — thin wrappe
49
49
 
50
50
  ### CLI only
51
51
 
52
- ```bash
53
- npm install -g dev-loops # global install
54
- dev-loops --help # verify
55
- ```
56
-
57
- Or run directly without installing:
52
+ Run the CLI **version-pinned** to the plugin/extension you have installed. The pin is the
53
+ single source of truth (`<version>` = your installed `dev-loops` version) and cannot drift:
58
54
 
59
55
  ```bash
60
- npx dev-loops --help
56
+ npx dev-loops@<version> --help
61
57
  ```
62
58
 
59
+ A global `npm install -g dev-loops` is **not** the supported invocation path: the global
60
+ binary updates independently of the plugin/extension and silently drifts out of version
61
+ (#833/#1036). Install it only as an optional bare shell convenience.
62
+
63
63
  ### Claude Code plugin
64
64
 
65
65
  The repo ships a Claude Code plugin rooted at `.claude/` (manifest at
@@ -202,7 +202,7 @@ Phase 8 is the active durable phase; Phase 7 second-repo pilot is deferred. See
202
202
  Gate review angles, refinement settings, persona mappings, and workflow defaults are config-driven via `.pi/dev-loop/defaults.yaml`. Consumer repos override values in `.devloops` at repo root (legacy `.pi/dev-loop/settings.yaml` still loads with a deprecation warning). The loader also accepts `.yml` and `.json` extensions and legacy `overrides.*` files as fallback formats. See [Extension Documentation](./extension/README.md) for details.
203
203
 
204
204
  ```bash
205
- npx dev-loops gates # see what reviewers will check
205
+ npx dev-loops@<version> gates # see what reviewers will check
206
206
  ```
207
207
 
208
208
  Key surfaces:
@@ -264,7 +264,7 @@ The `/dev-loops` command surface covers the direct dev-loop entrypoints plus rea
264
264
  npx dev-loops@<version> gates
265
265
  ```
266
266
 
267
- Use `npm install -g dev-loops` only if you want the shell command available outside Pi.
267
+ Use `npm install -g dev-loops` only as a bare shell convenience outside Pi; it is not version-pinned, can drift against the plugin/extension, and is not the supported invocation path (prefer `npx dev-loops@<version>`).
268
268
 
269
269
  The package exposes the `/dev-loops` extension command surface, the `dev-loops` shell CLI, and packaged skills from `package.json` `pi.skills`.
270
270
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "dev-loop"
3
3
  description: "Use as the single public workflow entrypoint. Route from canonical current state to the deterministic internal strategy, preferring GitHub-first paths and only using local phase implementation when explicitly requested. Keywords: dev-loop, public entrypoint, route workflow, continue dev loop."
4
- tools: [read, search, execute, bash, agent, todo, subagent]
4
+ tools: read, search, execute, bash, agent, todo, subagent
5
5
  argument-hint: "A dev-loop intent such as issue number/URL, PR number/URL, or a request to continue/inspect current state."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -23,6 +23,7 @@ The envelope is the primary handoff artifact — it is derived from resolver out
23
23
  - `nextAction` — the bounded task to execute
24
24
  - `stopRules` — stop boundaries that must not be crossed without authorization
25
25
  - `acceptance` — self-validation criteria for declaring completion
26
+ - `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.
26
27
 
27
28
  **Construction sequence:**
28
29
  <!-- pi-only -->
@@ -71,7 +72,7 @@ If local facts, GitHub facts, and helper/state-machine output do not agree well
71
72
  ## Subagent delegation
72
73
 
73
74
  <!-- pi-only -->
74
- This agent has `tools: [subagent]` and `maxSubagentDepth: 3` to allow orchestrating parallel review, chains, and staged fix passes.
75
+ This agent's frontmatter `tools:` comma-token scalar includes `subagent` (single-line comma form, no brackets — see #1111) and it sets `maxSubagentDepth: 3` to allow orchestrating parallel review, chains, and staged fix passes.
75
76
  <!-- /pi-only -->
76
77
 
77
78
  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.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "developer"
3
3
  description: "Use for direct product implementation in this repository: focused code changes, refactors, tests, bug fixes, and feature work within an already-scoped task. Keywords: implement feature, write code, refactor module, add tests, fix bug, update source."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Focused implementation task, relevant files, success criteria, and required verification."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "docs"
3
3
  description: "Use for README updates, plan docs, architecture notes, agent docs, migration notes, narrow documentation changes that must stay aligned with implementation work, and documentation-correctness review for the current change. Keywords: docs, README, plans, documentation, agent docs, rollout notes, changelog-style summary, docs review."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Documentation task or documentation-correctness review, affected files, source changes to reflect, and required level of detail."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "fixer"
3
3
  description: "Use for addressing active pull request review comments and threads: inspect unresolved feedback, make the narrow fix, verify it, push the fixing commit, reply with the resolving commit, and resolve the thread. Keywords: fixer, PR comments, address review feedback, resolve review threads, push fix commit."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Review-fix task, PR number or branch, target reviewer/thread/file, and required verification."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "quality"
3
3
  description: "Use for build systems, test runners, type-checking, linting, package scripts, GitHub Actions workflows, caches, release verification, and quality gates. Keywords: CI, workflow, GitHub Actions, build, test, cache, typecheck, package scripts, branch protection."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Quality or CI task, relevant workflows/config files, required checks, and verification expectations."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "refiner"
3
3
  description: "Use for refining one approved implementation phase at a time into a complete, testable plan with acceptance criteria, definition of done, risks, non-goals, unresolved questions, and RFC escalation notes. Keywords: refiner, phase refinement, acceptance criteria, definition of done, RFC escalation, merged plan."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Active phase doc or rough plan, phase boundary, known constraints, and any prior planning artifacts to refine."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "review"
3
3
  description: "Use for pull request review from a product and engineering perspective: check the implementation against the PR description, relevant plan, acceptance criteria, definition of done, non-goals, coding best practices, security expectations, and merge readiness. Keywords: review, PR review, acceptance criteria review, DoD review, security review, plan compliance."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "PR number or branch, relevant plan files, and any specific review focus areas or constraints."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -33,7 +33,8 @@ This agent has two modes. The default mode is the full-PR review described in th
33
33
 
34
34
  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:
35
35
 
36
- - **Fresh-context guard (mandatory):** run `node scripts/github/verify-fresh-review-context.mjs --scope <angle>` at startup. If it reports `fresh: false` (exit 1) or any error, refuse to proceed and report contamination do not review on inherited 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.
36
+ - **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).
37
+ - **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/head — refuse 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.
37
38
  - **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.
38
39
  - **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.
39
40
  - **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.