dev-loops 0.6.0 → 0.7.2

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 (203) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +8 -7
  3. package/.claude/agents/fixer.md +1 -0
  4. package/.claude/agents/refiner.md +2 -2
  5. package/.claude/agents/review.md +4 -3
  6. package/.claude/commands/{auto.md → loop-auto.md} +1 -1
  7. package/.claude/commands/loop-continue.md +15 -0
  8. package/.claude/commands/loop-enqueue.md +22 -0
  9. package/.claude/commands/loop-grill.md +17 -0
  10. package/.claude/commands/{info.md → loop-info.md} +2 -2
  11. package/.claude/commands/loop-queue-status.md +24 -0
  12. package/.claude/commands/{start-spike.md → loop-start-spike.md} +2 -2
  13. package/.claude/commands/{start.md → loop-start.md} +1 -1
  14. package/.claude/commands/{status.md → loop-status.md} +1 -1
  15. package/.claude/hooks/_bash-command-classify.mjs +333 -29
  16. package/.claude/hooks/_hook-decisions.mjs +138 -15
  17. package/.claude/hooks/pre-tool-use-bash-gate.mjs +36 -15
  18. package/.claude/skills/copilot-pr-followup/SKILL.md +60 -34
  19. package/.claude/skills/dev-loop/SKILL.md +16 -12
  20. package/.claude/skills/docs/acceptance-criteria-verification.md +8 -3
  21. package/.claude/skills/docs/anti-patterns.md +5 -3
  22. package/.claude/skills/docs/artifact-authority-contract.md +30 -11
  23. package/.claude/skills/docs/confirmation-rules.md +1 -0
  24. package/.claude/skills/docs/contract-style-guide.md +37 -0
  25. package/.claude/skills/docs/copilot-loop-operations.md +19 -15
  26. package/.claude/skills/docs/cross-harness-regression-contract.md +60 -0
  27. package/.claude/skills/docs/debt-remediation-contract.md +1 -1
  28. package/.claude/skills/docs/epic-tree-refinement-procedure.md +25 -22
  29. package/.claude/skills/docs/issue-intake-procedure.md +26 -19
  30. package/.claude/skills/docs/local-planning-flow.md +1 -1
  31. package/.claude/skills/docs/local-planning-worked-example.md +1 -1
  32. package/.claude/skills/docs/merge-preconditions.md +30 -12
  33. package/.claude/skills/docs/plan-file-contract.md +1 -1
  34. package/.claude/skills/docs/pr-lifecycle-contract.md +44 -35
  35. package/.claude/skills/docs/public-dev-loop-contract.md +60 -44
  36. package/.claude/skills/docs/retrospective-checkpoint-contract.md +106 -79
  37. package/.claude/skills/docs/spike-mode-contract.md +14 -7
  38. package/.claude/skills/docs/stop-conditions.md +30 -15
  39. package/.claude/skills/docs/tracker-first-loop-state.md +13 -8
  40. package/.claude/skills/docs/ui-e2e-scoping-step.md +32 -0
  41. package/.claude/skills/docs/validation-policy.md +4 -5
  42. package/.claude/skills/docs/workflow-handoff-contract.md +39 -0
  43. package/.claude/skills/local-implementation/SKILL.md +62 -145
  44. package/.claude/skills/loop-grill/SKILL.md +163 -0
  45. package/AGENTS.md +1 -0
  46. package/CHANGELOG.md +101 -0
  47. package/README.md +9 -9
  48. package/agents/dev-loop.agent.md +11 -10
  49. package/agents/developer.agent.md +1 -1
  50. package/agents/docs.agent.md +1 -1
  51. package/agents/fixer.agent.md +2 -1
  52. package/agents/quality.agent.md +1 -1
  53. package/agents/refiner.agent.md +3 -3
  54. package/agents/review.agent.md +5 -4
  55. package/cli/index.mjs +35 -42
  56. package/extension/README.md +4 -4
  57. package/extension/checks.ts +1 -5
  58. package/extension/harness-types.ts +1 -0
  59. package/extension/index.ts +6 -0
  60. package/extension/pi-extension-adapter.ts +2 -0
  61. package/extension/presentation.ts +7 -19
  62. package/package.json +12 -12
  63. package/scripts/_core-helpers.mjs +2 -0
  64. package/scripts/claude/headless-dev-loop.mjs +1 -1
  65. package/scripts/docs/validate-rule-ownership.mjs +442 -0
  66. package/scripts/docs/validate-state-machine-conformance.mjs +1092 -0
  67. package/scripts/github/_review-thread-mutations.mjs +5 -2
  68. package/scripts/github/capture-review-threads.mjs +2 -9
  69. package/scripts/github/comment-issue.mjs +2 -9
  70. package/scripts/github/create-label.mjs +133 -0
  71. package/scripts/github/create-pr.mjs +119 -10
  72. package/scripts/github/detect-checkpoint-evidence.mjs +259 -18
  73. package/scripts/github/detect-linked-issue-pr.mjs +22 -6
  74. package/scripts/github/edit-pr.mjs +259 -0
  75. package/scripts/github/fetch-ci-logs.mjs +2 -9
  76. package/scripts/github/list-issues.mjs +2 -9
  77. package/scripts/github/list-review-threads.mjs +277 -0
  78. package/scripts/github/manage-sub-issues.mjs +46 -10
  79. package/scripts/github/offer-human-handoff.mjs +8 -5
  80. package/scripts/github/post-gate-findings.mjs +20 -5
  81. package/scripts/github/probe-ci-status.mjs +8 -2
  82. package/scripts/github/probe-copilot-review.mjs +21 -14
  83. package/scripts/github/ready-for-review.mjs +26 -8
  84. package/scripts/github/reconcile-draft-gate.mjs +7 -3
  85. package/scripts/github/reply-resolve-review-thread.mjs +16 -5
  86. package/scripts/github/reply-resolve-review-threads.mjs +69 -7
  87. package/scripts/github/request-copilot-review.mjs +98 -26
  88. package/scripts/github/resolve-handoff-candidates.mjs +7 -3
  89. package/scripts/github/resolve-tracker-local-spec.mjs +9 -3
  90. package/scripts/github/stage-reviewer-draft.mjs +10 -2
  91. package/scripts/github/tick-verified-checkboxes.mjs +202 -0
  92. package/scripts/github/upsert-checkpoint-verdict.mjs +105 -15
  93. package/scripts/github/verify-briefing-prefixes.mjs +191 -0
  94. package/scripts/github/verify-fresh-review-context.mjs +226 -32
  95. package/scripts/github/view-pr.mjs +150 -0
  96. package/scripts/github/wait-pr-checks.mjs +171 -0
  97. package/scripts/github/write-gate-context.mjs +458 -65
  98. package/scripts/github/write-gate-findings-log.mjs +128 -2
  99. package/scripts/lib/jq-output.mjs +18 -0
  100. package/scripts/loop/_post-convergence-change.mjs +211 -0
  101. package/scripts/loop/_pr-runner-coordination.mjs +70 -0
  102. package/scripts/loop/_repo-root-resolver.mjs +47 -0
  103. package/scripts/loop/build-handoff-envelope.mjs +14 -4
  104. package/scripts/loop/check-retro-tooling.mjs +14 -3
  105. package/scripts/loop/checkpoint-contract.mjs +7 -4
  106. package/scripts/loop/cleanup-worktree.mjs +12 -3
  107. package/scripts/loop/conductor-monitor.mjs +12 -18
  108. package/scripts/loop/copilot-pr-handoff.mjs +162 -14
  109. package/scripts/loop/debt-remediate.mjs +24 -12
  110. package/scripts/loop/detect-change-scope.mjs +38 -9
  111. package/scripts/loop/detect-copilot-loop-state.mjs +34 -8
  112. package/scripts/loop/detect-copilot-session-activity.mjs +7 -3
  113. package/scripts/loop/detect-initial-copilot-pr-state.mjs +7 -3
  114. package/scripts/loop/detect-internal-only-pr.mjs +8 -2
  115. package/scripts/loop/detect-issue-refinement-artifact.mjs +6 -3
  116. package/scripts/loop/detect-pr-gate-coordination-state.mjs +184 -69
  117. package/scripts/loop/detect-reviewer-loop-state.mjs +12 -2
  118. package/scripts/loop/detect-tracker-first-loop-state.mjs +9 -1
  119. package/scripts/loop/detect-tracker-pr-state.mjs +10 -3
  120. package/scripts/loop/ensure-worktree.mjs +8 -3
  121. package/scripts/loop/info.mjs +12 -4
  122. package/scripts/loop/inspect-run-viewer/constants.mjs +4 -18
  123. package/scripts/loop/inspect-run-viewer/vendor/mermaid.min.js +3405 -0
  124. package/scripts/loop/inspect-run-viewer-ci-changes.mjs +18 -6
  125. package/scripts/loop/inspect-run-viewer.mjs +67 -4
  126. package/scripts/loop/inspect-run.mjs +8 -2
  127. package/scripts/loop/outer-loop.mjs +8 -4
  128. package/scripts/loop/pr-runner-coordination.mjs +2 -9
  129. package/scripts/loop/pre-commit-branch-guard.mjs +16 -10
  130. package/scripts/loop/pre-flight-gate.mjs +9 -9
  131. package/scripts/loop/pre-pr-ready-gate.mjs +8 -4
  132. package/scripts/loop/pre-write-remote-freshness-guard.mjs +13 -4
  133. package/scripts/loop/provision-worktree.mjs +74 -3
  134. package/scripts/loop/resolve-dev-loop-startup.mjs +216 -10
  135. package/scripts/loop/resolve-gate-dispatch.mjs +134 -0
  136. package/scripts/loop/resolve-pr-conflicts.mjs +14 -7
  137. package/scripts/loop/run-conductor-cycle.mjs +8 -2
  138. package/scripts/loop/run-queue.mjs +18 -9
  139. package/scripts/loop/run-refinement-audit.mjs +8 -9
  140. package/scripts/loop/run-watch-cycle.mjs +2 -9
  141. package/scripts/loop/sanctioned-commands.mjs +106 -0
  142. package/scripts/loop/steer-loop.mjs +29 -16
  143. package/scripts/loop/validate-pr-body-spec.mjs +223 -0
  144. package/scripts/loop/watch-initial-copilot-pr.mjs +8 -3
  145. package/scripts/pages/build-site.mjs +59 -8
  146. package/scripts/pages/build-state-atlas.mjs +443 -0
  147. package/scripts/projects/_resolve-project.mjs +1 -0
  148. package/scripts/projects/add-queue-item.mjs +60 -54
  149. package/scripts/projects/archive-done-items.mjs +49 -84
  150. package/scripts/projects/ensure-queue-board.mjs +10 -36
  151. package/scripts/projects/list-queue-items.mjs +59 -382
  152. package/scripts/projects/move-queue-item.mjs +21 -449
  153. package/scripts/projects/reconcile-queue.mjs +253 -0
  154. package/scripts/projects/reorder-queue-item.mjs +43 -68
  155. package/scripts/projects/resolve-active-board-item.mjs +108 -46
  156. package/scripts/projects/sync-item-status.mjs +15 -10
  157. package/scripts/refine/_refine-helpers.mjs +21 -5
  158. package/scripts/refine/exit-spike.mjs +18 -8
  159. package/scripts/refine/promote-plan.mjs +22 -16
  160. package/scripts/refine/prose-linkage-detector.mjs +1 -1
  161. package/scripts/refine/refine-plan-file.mjs +13 -4
  162. package/scripts/refine/refinement-completeness-checker.mjs +1 -1
  163. package/scripts/refine/scaffold-spike-file.mjs +4 -8
  164. package/scripts/refine/scope-boundary-cross-checker.mjs +1 -1
  165. package/scripts/refine/tree-integrity-validator.mjs +1 -1
  166. package/scripts/refine/validate-plan-file.mjs +1 -1
  167. package/scripts/refine/validate-spike-file.mjs +1 -1
  168. package/scripts/refine/verify.mjs +11 -6
  169. package/scripts/release/assert-core-dependency-version.mjs +123 -0
  170. package/scripts/release/extract-changelog-section.mjs +16 -2
  171. package/skills/copilot-pr-followup/SKILL.md +60 -34
  172. package/skills/dev-loop/SKILL.md +11 -7
  173. package/skills/docs/acceptance-criteria-verification.md +8 -3
  174. package/skills/docs/anti-patterns.md +5 -3
  175. package/skills/docs/artifact-authority-contract.md +30 -11
  176. package/skills/docs/confirmation-rules.md +1 -0
  177. package/skills/docs/contract-style-guide.md +37 -0
  178. package/skills/docs/copilot-loop-operations.md +19 -15
  179. package/skills/docs/cross-harness-regression-contract.md +60 -0
  180. package/skills/docs/debt-remediation-contract.md +1 -1
  181. package/skills/docs/epic-tree-refinement-procedure.md +25 -22
  182. package/skills/docs/issue-intake-procedure.md +26 -19
  183. package/skills/docs/local-planning-flow.md +1 -1
  184. package/skills/docs/local-planning-worked-example.md +1 -1
  185. package/skills/docs/merge-preconditions.md +30 -12
  186. package/skills/docs/plan-file-contract.md +1 -1
  187. package/skills/docs/pr-lifecycle-contract.md +44 -35
  188. package/skills/docs/public-dev-loop-contract.md +60 -44
  189. package/skills/docs/required-rules.json +149 -0
  190. package/skills/docs/retrospective-checkpoint-contract.md +106 -79
  191. package/skills/docs/spike-mode-contract.md +14 -7
  192. package/skills/docs/stop-conditions.md +30 -15
  193. package/skills/docs/tracker-first-loop-state.md +13 -8
  194. package/skills/docs/ui-e2e-scoping-step.md +32 -0
  195. package/skills/docs/validation-policy.md +4 -5
  196. package/skills/docs/workflow-handoff-contract.md +39 -0
  197. package/skills/local-implementation/SKILL.md +62 -145
  198. package/skills/loop-grill/SKILL.md +165 -0
  199. package/.claude/commands/continue.md +0 -15
  200. package/scripts/docs/validate-no-duplicate-rules.mjs +0 -250
  201. package/scripts/loop/conductor.mjs +0 -233
  202. package/scripts/loop/detect-stale-runner.mjs +0 -265
  203. package/scripts/loop/pre-push-main-guard.mjs +0 -117
@@ -8,9 +8,10 @@ Other repo docs may summarize or link this contract, but they should not redefin
8
8
 
9
9
  ## Two-tier model
10
10
 
11
- dev-loops supports two mutually exclusive artifact authority modes. Every work item originates from exactly one authoritative artifact: a GitHub issue or a persisted markdown plan file. Work originates from a PR or a direct local change only when explicitly requested.
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
13
 
13
- 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 github-first code-level fallback in `BUILT_IN_DEFAULTS`, which the shipped extension layer overrides to local-first.
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`.
14
15
 
15
16
  ### Tracker-first
16
17
 
@@ -19,12 +20,13 @@ The shipped extension default selects local-planning; see [Shipped default postu
19
20
  Artifacts:
20
21
  - **Planning artifact:** GitHub issue (title, body, labels, assignees, acceptance criteria)
21
22
  - **Execution artifact:** GitHub PR (linked to issue; created during implementation)
22
- - **No local duplicate:** Do not create `docs/phases/phase-<n>.md` for the same session when a GitHub issue is the canonical spec
23
+ - <!-- rule: ARTIFACT-TRACKER-FIRST-NO-DUP --> **No local duplicate:** A tracker-first session MUST NOT create `docs/phases/phase-<n>.md` for the same session when a GitHub issue is the canonical spec
23
24
 
24
25
  Key contract:
25
26
  - GitHub issue state is authoritative — not local notes or chat context
26
27
  - A linked PR is the single canonical follow-up artifact for the issue
27
28
  - When an open linked PR exists, reuse it rather than opening another
29
+ - Follow-ups discovered while working a PR/loop are noted on the originating issue or PR body by default; a standalone issue is filed only when the follow-up is genuinely independent of the PR and outlives it (see [Sub-Issue Tree Contract](../../docs/sub-issue-tree-contract.md))
28
30
  - Implementation may proceed through either the GitHub-first routed path or the local implementation strategy (see [Public Dev Loop Contract](public-dev-loop-contract.md) `targetPreference`)
29
31
 
30
32
  ### Local-planning
@@ -39,7 +41,22 @@ Artifacts:
39
41
  Key contract:
40
42
  - The markdown plan file is the canonical spec — not a duplicate of a tracker issue
41
43
  - GitHub issues may still be used for tracking or linking, but the plan file is authoritative for scope and acceptance criteria
42
- - A tracker-backed local implementation session (GitHub issue as canonical spec) must not also maintain a duplicate `docs/phases/phase-<n>.md` — see [Public Dev Loop Contract](public-dev-loop-contract.md) "Tracker-backed local implementation input-source contract"
44
+ - A tracker-backed local implementation session (GitHub issue as canonical spec) is bound by [ARTIFACT-TRACKER-FIRST-NO-DUP](#tracker-first) above — see [Public Dev Loop Contract](public-dev-loop-contract.md) "Tracker-backed local implementation input-source contract"
45
+
46
+ ### Lightweight (PR-body-as-spec)
47
+
48
+ **The PR description itself is the authoritative artifact store — no committed plan artifact.** This is a lightweight modifier on the local `--issue` path (`resolve-dev-loop-startup.mjs --issue <n> --lightweight`, `canonicalSpecSource: pr_body`), not a settings-level mode. No phase/plan doc is minted or committed; the PR body carries the spec-of-record invariants directly. The gate sequence is identical to the phase-doc path (draft → pre-approval fanout → detect-evidence → human merge); only the backing artifact differs (PR body vs phase doc).
49
+
50
+ Artifacts:
51
+ - **Planning + execution artifact:** the GitHub PR — its description is the spec, its diff is the execution
52
+ - **No committed plan doc:** no `docs/phases/*.md` is created for the session
53
+
54
+ Key contract:
55
+ - <!-- rule: ARTIFACT-LIGHTWEIGHT-BODY-INVARIANTS --> The PR body MUST carry the same invariants a durable spec would: **Objective/why, in-scope + explicit non-goals, testable acceptance criteria, definition of done, open questions/risks** — unconditionally, whether or not the work is tracker-backed. The `Closes #N` linkage (GitHub's other closing keywords count too) is conditional on artifact backing (operator ruling, issue #1210): REQUIRED when the work originates from a GitHub issue (`--issue --lightweight`), ABSENT BY DESIGN when the PR is the sole artifact with no backing issue (`--lightweight` alone, issue-less PR-first) — an issue-less PR body MUST NOT carry a closing reference to an issue that doesn't back it. `scripts/loop/validate-pr-body-spec.mjs` (reusing the generic markdown logic of `@dev-loops/core/loop/issue-refinement-artifact`, `validatePrBodySpec`) validates these and fails closed with a distinct reason per violated invariant — `missing_closing_issue_reference` without the linkage in tracker-backed mode, `closes_wrong_issue` when an `--expected-issue` is given and doesn't match, `unexpected_closing_issue_reference` when a closing reference is present under explicit issue-less mode (`--no-issue`) — so the lightweight path's issue-tracking state never silently diverges from PR state (issue #1181).
56
+ - This flips the promotion invariant below (P4, "the PR body carries the committed plan-doc **path**"): under lightweight there is no committed plan doc — the PR body **is** the spec, not a pointer to one.
57
+ - The explicit `--lightweight` flag is the primary, deterministic trigger. The secondary heuristic (chore/fix commit type + no `--plan-file` + small change) is a documented manual signal for when to reach for the flag; it is not an automatic selector.
58
+ - <!-- rule: ARTIFACT-LIGHTWEIGHT-PLAN-FILE-EXCLUSIVE --> `--lightweight` MUST be rejected when combined with `--plan-file` (they are opposites: `--plan-file` commits a durable plan doc as the spec, `--lightweight` makes the PR body the spec). It composes with `--issue` (tracker-backed) or stands alone (issue-less PR-first, #1210 — gated on `localImplementation.lightMode` being enabled and the change scope staying within its threshold); it MUST be rejected when combined with any other mode flag (`--pr`, `--input`, `--spike`).
59
+ - Pre-approval acceptance-criteria verification reads the AC/DoD/invariants directly from the PR body rather than a linked issue body; see [Acceptance Criteria Verification](acceptance-criteria-verification.md).
43
60
 
44
61
  ### Mode selection table
45
62
 
@@ -47,6 +64,7 @@ Key contract:
47
64
  |---|---|---|---|
48
65
  | Tracker-first | GitHub issue | Yes | `strategy.default: github-first` |
49
66
  | Local-planning (shipped default) | Markdown plan file | No | `strategy.default: local-first` |
67
+ | Lightweight (PR-body-as-spec) | GitHub PR description | Conditional — `--issue` when tracker-backed; absent for issue-less PR-first (#1210), gated on `localImplementation.lightMode` + change-scope threshold | modifier: `--lightweight` (`canonicalSpecSource: pr_body`) |
50
68
 
51
69
  `inputSource.default` further disambiguates local-first startup:
52
70
  | inputSource | Meaning |
@@ -81,21 +99,22 @@ The `inputSource.default` key disambiguates local-first startup:
81
99
 
82
100
  The effective default for a consumer comes from the config-merge layering in `packages/core/src/config/config.mjs`. Precedence, low to high:
83
101
 
84
- 1. `BUILT_IN_DEFAULTS` (frozen in `config.mjs`) — `strategy.default: github-first`. This is the code-level fallback when no other layer sets the key.
102
+ 1. `BUILT_IN_DEFAULTS` (frozen in `config.mjs`) — `strategy.default: local-first`. This is the code-level fallback when no other layer sets the key.
85
103
  2. Extension-packaged defaults (`packages/core/src/config/extension-defaults.yaml`, loaded as the `extensionDefaults` layer) — `strategy.default: local-first`. This is the opinion the package ships and the layer that wins over the built-in fallback.
86
104
  3. Repo-local `.pi/dev-loop/defaults.*` (legacy) — applied when present.
87
105
  4. Repo `.devloops` at repo root — the per-repo override, highest precedence. When `.devloops` is absent, the legacy `.pi/dev-loop/settings.*` / `overrides.*` apply at this position instead.
88
106
 
89
107
  With nothing but the shipped package in place, the extension layer resolves `strategy.default` to `local-first`, so the shipped default posture is local-planning (epic #947, decision #7). A repo opts back into tracker-first by setting `strategy.default: github-first` in its own `.devloops`.
90
108
 
91
- Two legacy repo-local layers also exist under `.pi/dev-loop/` (the package no longer ships a `.pi/dev-loop/defaults.yaml`). They differ in how they load: `.pi/dev-loop/defaults.*` is always applied when present, between the extension defaults and `.devloops`; `.pi/dev-loop/settings.*` (and the older `overrides.*`) load only when no `.devloops` is present — when `.devloops` exists it is authoritative and those files are ignored (with a deprecation warning). The full precedence, low to high, is: `BUILT_IN_DEFAULTS` < extension defaults < `.pi/dev-loop/defaults.*` < repo `.devloops` (or, when `.devloops` is absent, `.pi/dev-loop/settings.*` / `overrides.*`).
109
+ Two legacy repo-local layers also exist under `.pi/dev-loop/` (the package no longer ships a `.pi/dev-loop/defaults.yaml`). They differ in how they load, per the precedence list above: `.pi/dev-loop/defaults.*` is always applied when present, between the extension defaults and `.devloops`; `.pi/dev-loop/settings.*` (and the older `overrides.*`) load only when no `.devloops` is present — when `.devloops` exists it is authoritative and those files are ignored (with a deprecation warning).
92
110
 
93
111
  ### Explicit non-knobs
94
112
 
95
- These are not valid artifact authority mode selectors:
96
- - `strategy.default: copilot` not a valid mode; the enum accepts only `github-first` or `local-first` (`packages/core/src/config/config.mjs`)
97
- - Free-form string values fail closed
98
- - Omitting `strategy.default` from every layer resolves to `github-first` from `BUILT_IN_DEFAULTS`; with the shipped extension layer present it resolves to `local-first`
113
+ <!-- rule: ARTIFACT-STRATEGY-ENUM-FAIL-CLOSED -->
114
+ `ARTIFACT-STRATEGY-ENUM-FAIL-CLOSED`: The strategy enum MUST accept only `github-first` or `local-first` and MUST fail closed on any other value (`packages/core/src/config/config.mjs`). These are not valid artifact authority mode selectors:
115
+ - `strategy.default: copilot`not a valid mode
116
+ - Free-form string valuesMUST fail closed
117
+ - Omitting `strategy.default` from every layer — resolves to `local-first` from `BUILT_IN_DEFAULTS`
99
118
 
100
119
  ## Local-first plan-file flow end to end
101
120
 
@@ -103,7 +122,7 @@ Under local-planning, one plan file moves through four stages. Each stage has a
103
122
 
104
123
  ### P1 — Plan-file artifact + config (#949)
105
124
 
106
- The plan file is a phase-doc-format markdown document. Its directory is `localPlanning.plansDir`, which defaults to `docs/phases/` (built-in default in `config.mjs`, mirrored in `extension-defaults.yaml`; `resolvePlansDir(config)` resolves it). Its required base authoring sections — `## Status`, `## Objective`, `## In scope`, `## Explicit non-goals` — and the validator `scripts/refine/validate-plan-file.mjs` (`validatePlanFile`, distinct `missing_*` codes per absent or empty section) are defined in the [Plan-file Contract](plan-file-contract.md).
125
+ The plan file is a phase-doc-format markdown document. It lives under `docs/phases/`, the existing phase-docs directory. Its required base authoring sections — `## Status`, `## Objective`, `## In scope`, `## Explicit non-goals` — and the validator `scripts/refine/validate-plan-file.mjs` (`validatePlanFile`, distinct `missing_*` codes per absent or empty section) are defined in the [Plan-file Contract](plan-file-contract.md).
107
126
 
108
127
  ### P2 — Intake (#950)
109
128
 
@@ -4,6 +4,7 @@ Canonical owner for agent confirmation / authorization rules across all workflow
4
4
 
5
5
  ## Core rule
6
6
 
7
+ <!-- rule: CONFIRM-CORE-EXPLICIT -->
7
8
  Before any state-changing action, get explicit confirmation unless the latest user message already clearly authorizes that exact action.
8
9
 
9
10
  ## What counts as confirmation
@@ -0,0 +1,37 @@
1
+ # Contract style guide
2
+
3
+ Canonical owner for contract style, rule IDs, and definitional discipline.
4
+
5
+ A short orientation paragraph introduces the document; normative rules live only in the rule table.
6
+
7
+ ## Terms
8
+
9
+ | Term | Definition |
10
+ |---|---|
11
+ | <!-- term: gate:pre_approval_gate --> `pre_approval_gate` | Review gate that checks changed contract rules against the registry before approval. |
12
+
13
+ ## Rule ownership
14
+
15
+ | Rule ID | Rule |
16
+ |---|---|
17
+ | <!-- rule: STYLE-RFC2119-KEYWORDS --> `STYLE-RFC2119-KEYWORDS` | Normative contract text MUST use RFC-2119 keywords (`MUST`, `MUST NOT`, `SHALL`, `SHALL NOT`, `SHOULD`, `MAY`) when declaring requirements. |
18
+ | <!-- rule: STYLE-SINGLE-OWNER --> `STYLE-SINGLE-OWNER` | Each normative rule MUST have one single owner: exactly one source document defines the rule and every other document references that rule by ID instead of restating it. |
19
+ | <!-- rule: STYLE-RULE-MARKER --> `STYLE-RULE-MARKER` | A rule definition MUST place an HTML marker immediately before the rule body using `&lt;!-- rule: AREA-TOPIC-NNN --&gt;`; markers are invisible in rendered Markdown and MUST pass through mirror generation verbatim. |
20
+ | <!-- rule: STYLE-ID-SCHEME --> `STYLE-ID-SCHEME` | Rule IDs MUST be stable uppercase identifiers using the descriptive `AREA-TOPIC-NNN` shape or a short descriptive suffix when clearer. |
21
+ | <!-- rule: STYLE-ID-STABILITY --> `STYLE-ID-STABILITY` | A rule ID is stable forever: semantic changes MUST retire the old ID and mint a new ID through a dedicated semantic-change issue, never silent mutation. |
22
+ | <!-- rule: STYLE-REFERENCE-BY-ID --> `STYLE-REFERENCE-BY-ID` | A non-owner document MUST reference an owned rule by ID, preferably with a link to the owner, and MUST NOT duplicate the owned rule text. |
23
+ | <!-- rule: STYLE-DEFINED-TERMS --> `STYLE-DEFINED-TERMS` | State tokens, reason codes, and gate names MUST have exactly one canonical definition before annotated rule text uses them. |
24
+ | <!-- rule: STYLE-TERM-MARKER --> `STYLE-TERM-MARKER` | A canonical term definition MUST use `&lt;!-- term: kind:value --&gt;`, where `kind` is `state`, `reason`, or `gate`. |
25
+ | <!-- rule: STYLE-PRECEDENCE-CONFLICT --> `STYLE-PRECEDENCE-CONFLICT` | When two contract statements conflict, the document that owns the referenced rule ID wins until a semantic-change issue retires or replaces the rule. |
26
+ | <!-- rule: STYLE-CANONICAL-OPENER --> `STYLE-CANONICAL-OPENER` | A contract document SHOULD open with `Canonical owner for X` and SHOULD end with a Cross-references section. |
27
+ | <!-- rule: STYLE-CONTRADICTION-LENS --> `STYLE-CONTRADICTION-LENS` | A contract-touching PR MUST include an RFC-2119 contradiction check in `pre_approval_gate`: every added or changed rule is checked against the rule registry for opposing modality or a weaker restatement. |
28
+ | <!-- rule: STYLE-LEXICAL-SCAN-LIMIT --> `STYLE-LEXICAL-SCAN-LIMIT` | The ownership validator's contradiction scan is lexical only; behavioral contradictions belong to the L2/L3 harness and semantic contradictions belong to the gate contradiction lens. |
29
+
30
+ ## Reference syntax
31
+
32
+ Use `&lt;!-- rule-ref: RULE-ID --&gt;` next to a cross-reference when a machine-checkable reference is useful. Markdown links may also use the rule ID as link text.
33
+
34
+ ## Cross-references
35
+
36
+ - [Stop conditions](stop-conditions.md)
37
+ - State-machine conformance + invariant harness (`scripts/docs/validate-state-machine-conformance.mjs`) — L2 (doc ↔ code) conformance and L3 (completeness/safety/liveness) graph invariants; its header comment documents the registration path for adding a machine
@@ -25,18 +25,17 @@ Use the machines to answer:
25
25
  - what the next required action is
26
26
  - when to stop instead of guessing
27
27
 
28
- Each machine captures an observable snapshot from GitHub facts (plus explicit bounded local loop
29
- metadata when required) and interprets it into exactly one current state plus allowed next
30
- transitions. In the `dev-loops` source repository, the supporting source-authority references are [Copilot Loop State Graph](../../docs/copilot-loop-state-graph.md) and [Reviewer Loop State Graph](../../docs/reviewer-loop-state-graph.md) under `../../docs/` relative to `skills/copilot-pr-followup/SKILL.md`. Treat those links as source-repo references, not bundled installed-skill docs.
28
+ Each machine's snapshot-to-state mapping is defined in its owner doc (see the Overview of
29
+ [Copilot Loop State Graph](../../docs/copilot-loop-state-graph.md) and
30
+ [Reviewer Loop State Graph](../../docs/reviewer-loop-state-graph.md)), under `../../docs/`
31
+ relative to `skills/copilot-pr-followup/SKILL.md` in the `dev-loops` source repository. Treat
32
+ those links as source-repo references, not bundled installed-skill docs.
31
33
 
32
34
  For tracker-first MVP `story -> PR -> tracker sync` work, the source-repo reference is [Tracker-First Story-to-PR Contract](../../docs/tracker-story-pr-contract.md). That source doc inherits source-of-truth ownership, the required work item <-> PR link, and reverse-sync semantics from `#21`; it only adds the mutually exclusive workflow-family states and post-merge sync-verification states for this narrower MVP slice.
33
35
 
34
36
  ## Key guarantees from the state machine
35
37
 
36
- - `unresolvedThreadCount > 0` always routes to fix/reply-resolve never to a wait/watch state
37
- - `snapshot.copilotReviewRequestStatus === "unavailable"` or `snapshot.copilotReviewRequestStatus === "failed"` routes to a terminal stop state — never to sleep or watch
38
- - `agentFixStatus === "applied"` with unresolved threads routes to `already_fixed_needs_reply_resolve` — reply/resolve on GitHub is required before re-requesting review
39
- - Copilot being in `requested_reviewers` (`"requested"` or `"already-requested"`) routes to `waiting_for_copilot_review`
38
+ These routing guarantees are owned by [Copilot Loop State Graph](../../docs/copilot-loop-state-graph.md); see `COPILOT-STATE-UNRESOLVED-PRIORITY`, `COPILOT-STATE-TERMINAL-STOP`, `COPILOT-STATE-REPLY-BEFORE-REREQUEST`, and `COPILOT-STATE-ACTIVE-REQUEST-WAIT` there for the routing rules driving `unresolvedThreadCount`, `copilotReviewRequestStatus`, and `agentFixStatus`.
40
39
 
41
40
  ## How to use the state machine in practice
42
41
 
@@ -51,7 +50,7 @@ For tracker-first MVP `story -> PR -> tracker sync` work, the source-repo refere
51
50
 
52
51
  4. Branch on the detector output instead of inventing a polling loop:
53
52
  - `state=waiting_for_copilot_review` with `snapshot.copilotReviewOnCurrentHead=false`: do **not** poll manually; either run `node <resolved-skill-scripts>/loop/run-watch-cycle.mjs --repo <owner/name> --pr <number>` for persistent async waiting or report the wait state and resume later after the single detector call
54
- - `state=waiting_for_ci` with `snapshot.ciStatus` in `{ "pending", "none" }`: do **not** poll manually by default; use `dev-loops loop watch-ci --repo <owner/name> --pr <number>` to block-wait on the combined check/status state (provider-agnostic — CircleCI, GitHub Actions, and any external commit-status / check-run). `gh run watch <run-id>` is an Actions-only fallback when the current-head run id is already known and you know the gating checks are GitHub Actions. Either way, on `timeout`/`changed` report pending CI and resume later after the single detector refresh. Bounded exception: if GitHub created zero current-head check suites/statuses, the previous head rollup was green, and local `npm run verify` already passed for the same current head, rerun `detect-copilot-loop-state.mjs` with `--local-validation-head-sha <current-head-sha>` so the detector can promote that exact zero-suite case to `snapshot.ciStatus="crediblyGreen"` instead of waiting forever on raw `none`.
53
+ - `state=waiting_for_ci` with `snapshot.ciStatus` in `{ "pending", "none" }`: do **not** poll manually by default; use `dev-loops loop watch-ci --repo <owner/name> --pr <number>` or `node <resolved-skill-scripts>/github/wait-pr-checks.mjs --repo <owner/name> --pr <number>` (settle-blocking with a 0/1/2 exit contract) to block-wait on the combined check/status state (provider-agnostic — CircleCI, GitHub Actions, and any external commit-status / check-run). `gh run watch <run-id>` is an Actions-only fallback when the current-head run id is already known and you know the gating checks are GitHub Actions. Either way, on `timeout`/`changed` report pending CI and resume later after the single detector refresh. Bounded exception: if GitHub created zero current-head check suites/statuses, the previous head rollup was green, and local `npm run verify` already passed for the same current head, rerun `detect-copilot-loop-state.mjs` with `--local-validation-head-sha <current-head-sha>` so the detector can promote that exact zero-suite case to `snapshot.ciStatus="crediblyGreen"` instead of waiting forever on raw `none`.
55
54
  - `snapshot.ciStatus="failure"` remains a stop/fix state, never a wait loop
56
55
 
57
56
  5. For reviewer-side draft-review work, run `node <resolved-skill-scripts>/loop/detect-reviewer-loop-state.mjs --repo <owner/name> --pr <number> [--reviewer-login <login>] [--local-state <path>]`.
@@ -64,10 +63,7 @@ For tracker-first MVP `story -> PR -> tracker sync` work, the source-repo refere
64
63
 
65
64
  ## Judgment calls that remain in the agent layer
66
65
 
67
- - Whether a comment should be accepted, deferred, or disagreed with
68
- - Whether the code change is already sufficient (→ sets `agentFixStatus: "applied"`)
69
- - What the narrowest valid fix is
70
- - Whether another Copilot pass is desired (→ triggers re-request or selects `done`)
66
+ See [Copilot Loop State Graph](../../docs/copilot-loop-state-graph.md)'s "Agent judgment boundary" for the bounded list of decisions the state machine leaves to the agent.
71
67
 
72
68
  ## Workflow overview
73
69
 
@@ -102,7 +98,7 @@ Useful checks:
102
98
 
103
99
  If the user asks for status/progress/readiness/merge-state/next-step (including “what is next”):
104
100
  - resolve authoritative active artifact identity first (issue/PR, plus branch/head SHA when useful)
105
- - for issue targets, do not assert "no open PR" until authoritative issue↔PR linkage is resolved via the startup resolver (`dev-loops loop startup --issue <number>`, run inside the `dev-loop` async subagent) do not run `detect-linked-issue-pr.mjs` manually
101
+ - for issue targets, resolve authoritative issue↔PR linkage via the startup resolver (`dev-loops loop startup --issue <number>`, run inside the `dev-loop` async subagent) rather than running `detect-linked-issue-pr.mjs` manually; the fail-closed-until-resolved requirement is owned by `FACADE-STATUS-AUTHORITATIVE-FAIL-CLOSED` in [Public Dev Loop Contract](./public-dev-loop-contract.md)
106
102
  - resolve artifact state (`open`/`closed`/`merged`/`not_applicable`)
107
103
  - resolve current loop state and next action from deterministic helper/state output
108
104
  - include explicit resolved artifact identity in the answer
@@ -170,7 +166,8 @@ Follow the PR description contract (see [Agent Instructions](../../AGENTS.md) if
170
166
 
171
167
  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 `- `.
172
168
 
173
- 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. 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 even eligible.
169
+ <!-- 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.
174
171
 
175
172
  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`).
176
173
 
@@ -188,7 +185,7 @@ Preferred defaults for this repo:
188
185
  - parent/subagent no-activity threshold for watcher-style runs: at least **15 minutes**
189
186
  - active-long-running notice threshold for watcher-style runs: about **30 minutes**
190
187
 
191
- These are the defaults built into `probe-copilot-review.mjs`, `run-watch-cycle.mjs`, and the `watchArgs` emitted by `copilot-pr-handoff.mjs`. Do not pass removed CLI policy flags (`--poll-interval-ms`, `--timeout-ms`, `--probe-only`) helpers hard-error when they are provided. Timeouts and intervals are derived from `packages/core/src/loop/policy-constants.mjs`.
188
+ These are the defaults built into `probe-copilot-review.mjs`, `run-watch-cycle.mjs`, and the `watchArgs` emitted by `copilot-pr-handoff.mjs`. Do not pass removed CLI policy flags (`--poll-interval-ms`, `--probe-only`): the scripts reject them as usage errors, but do not rely on that as a guaranteed hard stop in the `dev-loops gate …` path — the CLI retry-wrapper (`buildCorrectedArgs`) may strip an unrecognized/removed flag and silently retry with defaults, so passing one can quietly fall back to default behavior rather than failing. `probe-copilot-review.mjs` does accept `--timeout-ms` (watch budget in ms; `0` = single immediate idle check, no watch); timeouts and intervals are otherwise derived from `packages/core/src/loop/policy-constants.mjs`.
192
189
 
193
190
  ### Outer-loop checkpoint: canonical re-attachment artifact
194
191
 
@@ -230,4 +227,11 @@ Helper-owned sleep inside `run-watch-cycle.mjs`, `probe-copilot-review.mjs`, or
230
227
 
231
228
  A watcher sleeping between polls is expected behavior, not a blocker.
232
229
 
230
+ ## Hard rule: no inline interpreters
231
+
232
+ <!-- rule: OPS-NO-INLINE-INTERPRETER -->
233
+ `OPS-NO-INLINE-INTERPRETER`: Coordinator and agent flows MUST NOT invoke an inline interpreter (`node -e`/`node --eval`, `python3 -c`, `python3 - <<EOF` heredocs, or equivalent) to (a) parse any dev-loops tool output, or (b) mutate repository files. Sanctioned paths: a tool's `--jq`/`--silent` output flags (or `gh ... --jq`) for (a); the editor/patch tools, or `gh ... --body-file` composed from a `--jq` read, for (b). This rule applies to ALL repos using the dev-loop workflow, not just the source repo.
234
+
235
+ Post-watch read pattern: after a probe/watch settles, do not parse its output — re-read state via `detect-copilot-loop-state.mjs --repo <owner/name> --pr <number>` and, for thread bodies/ids, `list-review-threads.mjs --repo <owner/name> --pr <number> --unresolved-only` (see [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md) Step 6).
236
+
233
237
  If the polling interval is 1 minute, do not treat silence shorter than one full poll interval as suspicious, and do not configure needs-attention thresholds close to a few seconds for this loop.
@@ -0,0 +1,60 @@
1
+ # Cross-Harness Regression Contract
2
+
3
+ Canonical owner for harness-agnostic non-regression across Pi and Claude Code.
4
+
5
+ ## What counts as a harness-specific change
6
+
7
+ A change is harness-specific when it touches a seam that branches by runtime harness (Pi vs Claude Code), including:
8
+
9
+ - Runner coordination (`scripts/loop/pr-runner-coordination.mjs`, `scripts/loop/_pr-runner-coordination.mjs`)
10
+ - Run-id injection / async-start semantics (`packages/core/src/loop/async-start-contract.mjs`, `packages/core/src/loop/run-context.mjs`)
11
+ - Subagent / Task fan-out tool provisioning
12
+ - Harness-specific environment variables (`PI_SUBAGENT_RUN_ID`, `DEVLOOPS_RUN_ID`)
13
+ - Session/intercom wiring
14
+ - TUI behavior
15
+ - Any capability detection or `process.env` branch whose outcome differs per harness
16
+
17
+ ## Required cross-harness test coverage
18
+
19
+ Every harness-specific change MUST add or keep a test that exercises the path of the harness it does not directly target. A pull request that alters behavior on one harness without any test evidence for the other harness fails this contract.
20
+
21
+ ## The additive/no-op bar
22
+
23
+ State the bar symmetrically, in both directions:
24
+
25
+ - A change additive on Pi MUST be a no-op on Claude Code, or its effect on Claude Code MUST be explicitly validated by a test.
26
+ - A change additive on Claude Code MUST be a no-op on Pi, or its effect on Pi MUST be explicitly validated by a test.
27
+
28
+ Definitions:
29
+
30
+ - **No-op**: the existing test suite for the other harness passes unmodified — no assertion needed to change, no new test required, because the seam does not fire for that harness.
31
+ - **Explicitly validated**: a new or updated test directly asserts the other harness's resulting behavior (not merely "did not throw"), covering both the case where the seam is inert and the case where it participates.
32
+
33
+ ## Claude-Code-path test inventory
34
+
35
+ When a change targets a Claude-Code-specific seam, the following suites are the baseline that MUST stay green, and are the first place to add coverage for a Pi-originated change that MUST NOT regress Claude Code:
36
+
37
+ - `npm run test:assets` — runs `test/contracts/*.test.mjs`, including `test/contracts/claude-assets-reproducible.test.mjs`, `test/contracts/claude-headless-smoke.test.mjs`, `test/contracts/claude-plugin-manifest.test.mjs`, `test/contracts/claude-plugin-marketplace.test.mjs`, `test/contracts/claude-hooks-settings.test.mjs`, and `test/contracts/cli-harness-agnostic.test.mjs`
38
+ - `npm run smoke:headless` — runs `scripts/claude/headless-info-smoke.mjs`; this is a manual/local-only check, not part of `npm test` or `npm run verify` and not run by CI — run it directly when a change touches Claude Code headless-info behavior
39
+ - `npm run test:core` — runs `packages/core/test/*.test.mjs`, including `packages/core/test/claude-headless-entry.test.mjs`, `packages/core/test/claude-hook-decisions.test.mjs`, `packages/core/test/run-context.test.mjs`, `packages/core/test/async-start-contract.test.mjs`
40
+
41
+ Symmetrically, for a change targeting the Claude-Code-specific seam that MUST NOT regress Pi, `npm run test:extension` (which runs `test/extension-*.test.mjs`, including `test/extension-pi-adapter.test.mjs`, the Pi-side adapter inventory) is the baseline suite to check and extend.
42
+
43
+ ## How to add coverage
44
+
45
+ 1. Identify the harness-specific seam the change touches (see the surface list above).
46
+ 2. Locate the existing test file that already covers that seam for the harness being changed, and its counterpart for the other harness (from the inventory above, or the nearest analogous file).
47
+ 3. Add or extend a test asserting the other harness's behavior directly — do not rely on an untested assumption that the change is inert there.
48
+ 4. Run the relevant suite locally (`npm run test:assets`, `npm run test:core`, `npm run test:extension`, or `npm run smoke:headless` as applicable) before opening the pull request.
49
+
50
+ ## Non-goals
51
+
52
+ - No new or expanded CI matrix: `npm run verify` (which CI runs on every pull request) already covers `test:assets`, `test:extension`, `test:scripts`, `test:core`, `test:docs`, and `test:dev-loop` — every suite named above except `npm run smoke:headless`, which is manual/local-only by design (see the test inventory above).
53
+ - No bespoke cross-harness enforcement script: naming the required suites here, combined with CI already running them on every pull request, satisfies the coverage bar.
54
+ - No rewriting of existing harness-specific code as part of adopting this contract.
55
+
56
+ ## Cross-references
57
+
58
+ - [AGENTS.md](../../AGENTS.md)
59
+ - [Dev Loop Skill](../dev-loop/SKILL.md)
60
+ - [Public Dev Loop Contract](public-dev-loop-contract.md)
@@ -1,6 +1,6 @@
1
1
  # Debt remediation contract
2
2
 
3
- Canonical authority for the debt remediation pipeline and its integration with the `dev-loop` execution path.
3
+ Canonical owner for the debt remediation pipeline and its integration with the `dev-loop` execution path.
4
4
 
5
5
  ## Pipeline
6
6
 
@@ -1,7 +1,7 @@
1
1
  # Epic tree refinement procedure
2
2
 
3
- This document is the canonical procedure for depth-first, top-down-then-bottom-up refinement of
4
- an existing GitHub sub-issue tree (parent → children → grandchildren).
3
+ Canonical owner for depth-first, top-down-then-bottom-up refinement of an existing GitHub
4
+ sub-issue tree (parent → children → grandchildren).
5
5
 
6
6
  Use it together with:
7
7
  - [Issue Intake Procedure](./issue-intake-procedure.md) — Phase 3b calls this procedure for epic decomposition
@@ -67,7 +67,8 @@ For the root issue:
67
67
  9. Show the diff and obtain confirmation before mutating GitHub
68
68
  10. Apply: `gh issue edit <root> --repo <repo> --body-file tmp/issues/<root>/refinement/root-body.md`
69
69
 
70
- **Gate:** Phase B must not start until Phase A is complete and the root body is updated on GitHub.
70
+ <!-- rule: EPIC-REFINEMENT-SERIAL-PHASE-GATE -->
71
+ **Gate:** Phase B MUST NOT start until Phase A is complete and the root body is updated on GitHub. The same serial-gate discipline applies at every phase boundary below: a level/phase MUST fully complete (siblings may run in parallel within it) before the next one starts.
71
72
 
72
73
  ---
73
74
 
@@ -96,7 +97,7 @@ parent's updated body, not each other's output.
96
97
  **Parallelism rule:** All siblings at the same level can be refined concurrently. No child needs
97
98
  another child's output; each child only reads the parent's contract and its own current body.
98
99
 
99
- **Serial gate between levels:** All children at level N must complete before descending to level N+1.
100
+ **Serial gate between levels** ([EPIC-REFINEMENT-SERIAL-PHASE-GATE](#phase-a--root-refinement-serial)): all children at level N must complete before descending to level N+1.
100
101
 
101
102
  Repeat Phase B until all leaves are refined.
102
103
 
@@ -122,7 +123,7 @@ updated bodies, not sibling parents).
122
123
  6. Show the diff and obtain confirmation before mutating
123
124
  7. Apply: `gh issue edit <parent> --repo <repo> --body-file tmp/issues/<parent>/refinement/parent-reconciled-body.md`
124
125
 
125
- **Serial gate between levels:** All parents at depth N must complete reconciliation before ascending to depth N-1.
126
+ **Serial gate between levels** ([EPIC-REFINEMENT-SERIAL-PHASE-GATE](#phase-a--root-refinement-serial)): all parents at depth N must complete reconciliation before ascending to depth N-1.
126
127
 
127
128
  ---
128
129
 
@@ -146,21 +147,28 @@ After all immediate children of the root have been reconciled:
146
147
  --repo <repo> --issue <root> --expected <n1,n2,...> --ordered
147
148
  ```
148
149
 
149
- **Gate:** Root final reconcile completes the procedure. All issues in the tree must now have:
150
- - AC checklist, DoD checklist, AC/DoD matrix, non-goals, explicit scope boundary
150
+ **Gate:** Root final reconcile completes the procedure. Every issue in the tree must now satisfy
151
+ [EPIC-REFINEMENT-REQUIRED-CONTRACTS](#rules).
151
152
 
152
153
  ---
153
154
 
154
155
  ## Rules
155
156
 
156
- - **No implementation, no PRs, no Copilot assignment** — this procedure is refinement-only
157
- - **Use `gh issue edit`** to apply changes directly do not create new issues or PRs
158
- - **No prose parent/child links in bodies**GitHub sub-issues API handles hierarchy
159
- - **Each issue must have:** AC checklist, DoD checklist, non-goals, AC/DoD matrix, scope boundary
160
- - **Scope boundary format:** `"This issue owns X. It does NOT own Y (#NNN) or Z (#MMM)."`
161
- - **Show the diff** and get confirmation before each `gh issue edit` mutation (unless unattended with explicit authorization)
162
- - **Write tmp artifacts** under `tmp/issues/<number>/refinement/` before applying
163
- - **Do not duplicate** the child list in parent bodies the sub-issue tree API owns hierarchy
157
+ <!-- rule: EPIC-REFINEMENT-SCOPE-BOUNDARY -->
158
+ This procedure MUST stay refinement-only: no implementation, no PRs, no Copilot assignment.
159
+ Apply changes directly with `gh issue edit`never create new issues or PRs. Hierarchy MUST
160
+ stay in the GitHub sub-issues API, not prose parent/child links or a duplicated child-list
161
+ checklist in parent bodies.
162
+
163
+ <!-- rule: EPIC-REFINEMENT-REQUIRED-CONTRACTS -->
164
+ Each issue in the tree MUST carry: an AC checklist, a DoD checklist, a non-goals section, an
165
+ AC/DoD matrix, and an explicit scope boundary in the format
166
+ `"This issue owns X. It does NOT own Y (#NNN) or Z (#MMM)."`
167
+
168
+ <!-- rule: EPIC-REFINEMENT-CONFIRM-BEFORE-MUTATE -->
169
+ `EPIC-REFINEMENT-CONFIRM-BEFORE-MUTATE`: The procedure MUST write the refined body to
170
+ `tmp/issues/<number>/refinement/` first and MUST show the diff and get confirmation before
171
+ each `gh issue edit` mutation, unless running unattended with explicit authorization.
164
172
 
165
173
  ---
166
174
 
@@ -179,18 +187,13 @@ Phase D: [root] serial (1 step)
179
187
 
180
188
  Total serial steps for a tree with depth D: `1 + (D-1) + (D-1) + 1 = 2D`
181
189
 
182
- **Fan-out rule:** At any level, when a parent is refined, ALL its children can be refined in parallel.
183
-
184
- **Serial gates:**
185
- - Root refinement must complete before any child starts
186
- - A parent's reconciliation must wait for ALL its children to finish
187
- - Root reconciliation must wait for all immediate children to reconcile
190
+ **Fan-out rule:** At any level, when a parent is refined, ALL its children can be refined in parallel. Root/leaf/child ordering follows [EPIC-REFINEMENT-SERIAL-PHASE-GATE](#phase-a--root-refinement-serial) throughout.
188
191
 
189
192
  ---
190
193
 
191
194
  ## Completion criteria
192
195
 
193
- The procedure is complete when all issues in the tree satisfy:
196
+ The procedure is complete when all issues in the tree satisfy [EPIC-REFINEMENT-REQUIRED-CONTRACTS](#rules), verified as:
194
197
 
195
198
  | Check | How to verify |
196
199
  |---|---|
@@ -1,17 +1,20 @@
1
1
  # Issue intake procedure
2
2
 
3
- This document is the canonical owner of the routed `issue_intake` procedure behind the public `dev-loop` façade.
3
+ Canonical owner for the routed `issue_intake` procedure behind the public `dev-loop` façade: preflight, normalization, async refinement, epic decomposition, and Copilot handoff.
4
4
 
5
5
  Use it together with:
6
6
  - [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md)
7
- - [Public Dev Loop Contract](./public-dev-loop-contract.md)
7
+ - [Public Dev Loop Contract](./public-dev-loop-contract.md) — owner of `FACADE-LINKED-PR-SINGLE-ARTIFACT`, `FACADE-BOOTSTRAP-WATCH-ROUTE`, `FACADE-BOOTSTRAP-ISOLATED-WORKTREE-CONTINUATION`
8
8
  - [Retrospective Checkpoint Contract](./retrospective-checkpoint-contract.md) when the current step depends on async start/resume/status or retrospective enforcement
9
+ - [Stop Conditions](./stop-conditions.md) — owner of `STOP-INITIAL-COPILOT-001` and the other strategy-wide stop/wait rules this procedure operationalizes
10
+ - [Merge Preconditions](./merge-preconditions.md) — the merge gate this procedure defers to before `gh pr merge`
9
11
 
10
12
  When routed work is issue-first rather than already in active PR follow-up, use the procedure below before entering the shared post-PR loop. Treat this document as the issue-refinement specialist procedure for the routed `issue_intake` seam.
11
13
 
12
14
  ## New-idea safety layer (default contract in this repo)
13
15
 
14
- For **all new ideas** that are not already anchored to an existing issue (including abstract ideas such as plain-language requests without an issue number or plan-doc path), apply this procedure-owned intake contract before any GitHub mutation:
16
+ <!-- rule: INTAKE-NEW-IDEA-SAFETY -->
17
+ For **all new ideas** that are not already anchored to an existing issue (including abstract ideas such as plain-language requests without an issue number or plan-doc path), the procedure MUST apply this intake contract before any GitHub mutation:
15
18
 
16
19
  - procedure owns classification; human operator gates all mutations
17
20
  - run classification in fresh context by default
@@ -50,9 +53,11 @@ Proposal artifact contract:
50
53
  - machine-readable JSON snapshot
51
54
  - write temporary artifacts under `Proposal` (`tmp/new-idea-intake/<run-id>/proposal.md`) and `tmp/new-idea-intake/<run-id>/proposal.json`
52
55
 
53
- If the Phase 1 preflight verdict is `pause_for_clarification`, stop and ask.
54
- If the intake state machine stops at `stopped_overlap_needs_decision` or `stopped_low_confidence`, stop and ask.
55
- If the intake state machine stops at `stopped_explicit_reject`, stop and record that the proposal was rejected; do not mutate GitHub.
56
+ <!-- rule: INTAKE-STOP-STATES -->
57
+ If the Phase 1 preflight verdict is `pause_for_clarification`, the procedure MUST stop and ask.
58
+ If the intake state machine stops at `stopped_overlap_needs_decision` or `stopped_low_confidence`, the procedure MUST stop and ask.
59
+ If the intake state machine stops at `stopped_explicit_reject`, the procedure MUST stop and record that the proposal was rejected; it MUST NOT mutate GitHub.
60
+
56
61
  After approval, start a separate async mutation pass (dispatched via the procedure) that consumes the approved proposal and emits a post-mutation verification artifact. Emit a concise post-mutation verification artifact and record what the mutation pass actually changed and verify the resulting issue/artifact state.
57
62
 
58
63
  ## Unattended issue-first execution and automatic re-entry
@@ -67,7 +72,7 @@ Under that unattended execution contract:
67
72
  - If a PR already exists, classify the post-assignment seam before follow-up
68
73
  - `waiting_for_initial_copilot_implementation`: keep waiting
69
74
  - `linked_pr_ready_for_followup`: route to the existing PR follow-up path immediately; resume from that PR
70
- - when routing leaves bootstrap wait for `linked_pr_ready_for_followup`, do not stop only because local isolation is required; re-enter the same PR follow-up from a safe isolated checkout/worktree
75
+ - when routing leaves bootstrap wait for `linked_pr_ready_for_followup`, follow [FACADE-BOOTSTRAP-ISOLATED-WORKTREE-CONTINUATION](public-dev-loop-contract.md): re-enter the same PR follow-up from a safe isolated checkout/worktree instead of stopping solely because local isolation is required
71
76
  - When the draft PR appears, classify whether it is still the bootstrap-only Copilot draft before entering normal follow-up
72
77
  - if a child async run exits while the deterministic state is still non-terminal (for example `waiting_for_copilot_review`), automatically resume/restart follow-up when continuation is feasible instead of requiring manual operator restart
73
78
  - continue unattended until the human approval checkpoint unless a genuine stop condition is reached
@@ -107,23 +112,23 @@ Preflight verdicts:
107
112
  - detect an existing linked PR with the deterministic linked-PR helper:
108
113
  `node <resolved-skill-scripts>/github/detect-linked-issue-pr.mjs --repo <resolved-repo> --issue <number>`
109
114
  - treat the helper output as authoritative for linked-PR detection/selection
110
- - do not re-implement linked-event query behavior, pagination, repo filtering, or tie-break logic
111
- - do not rely only on PR title/body containing a literal issue number
115
+ - <!-- rule: INTAKE-LINKED-PR-HELPER-DELEGATION --> `INTAKE-LINKED-PR-HELPER-DELEGATION`: Agents MUST NOT re-implement linked-event query behavior, pagination, repo filtering, or tie-break logic, and MUST NOT rely only on PR title/body containing a literal issue number.
112
116
  - treat an open linked PR as the active implementation for this issue
113
- - once an open linked PR exists, that PR is the only canonical follow-up artifact for the issue; attach follow-up work to it and do not open another PR unless the prior PR was explicitly superseded and reconciled first
117
+ - once an open linked PR exists, attach follow-up work to it per `FACADE-LINKED-PR-SINGLE-ARTIFACT` (owned in [Public Dev Loop Contract](public-dev-loop-contract.md)) for the `issue_intake` strategy
118
+ - **follow-up-capture rule:** when a follow-up is discovered while working a PR/loop, note it on the originating issue (or the PR body); file a standalone issue only if the follow-up is genuinely independent of the PR **and** outlives it (a real separate bug/feature that would be lost as a note on a soon-closed issue). Prefer noting PR-scoped follow-ups on the originating artifact over spinning up standalone tracker items for work that is part of the same effort or will be resolved imminently. See [Sub-Issue Tree Contract](../../docs/sub-issue-tree-contract.md).
114
119
  - if a PR already exists, classify bootstrap-wait versus follow-up:
115
120
  `node <resolved-skill-scripts>/loop/detect-initial-copilot-pr-state.mjs --repo <resolved-repo> --issue <number>`
116
121
  - `waiting_for_initial_copilot_implementation`: keep waiting; in durable-auto mode use:
117
122
  ```sh
118
123
  node <resolved-skill-scripts>/loop/watch-initial-copilot-pr.mjs --repo <resolved-repo> --issue <number>
119
124
  ```
120
- - must use the dedicated `watch-initial-copilot-pr.mjs` watcher and its default 1-hour watch budget
125
+ - durable-auto ownership of this seam (the dedicated watcher + its default 1-hour watch budget) is owned by [FACADE-BOOTSTRAP-WATCH-ROUTE](public-dev-loop-contract.md); this procedure does not restate that rule
121
126
  - quiet/no-activity watch observations alone are non-terminal
122
127
  - `ready_for_followup`: linked PR has become substantive; resume from that PR
123
128
  - `timed_out`: observational first; refresh authoritative state
124
129
  - if refreshed state is still `waiting_for_initial_copilot_implementation`, remain attached to the same durable wait seam and continue waiting
125
130
  - if the refreshed state exits this seam, route based on that refreshed state instead of surfacing timeout attention
126
- - when the refreshed state is `linked_pr_ready_for_followup`, re-enter normal PR follow-up; if the follow-up handoff carries `conductorRouting.handoffEnvelope.requiresLocalIsolation=true`, perform the expected isolated-checkout/worktree handoff and continue
131
+ - 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
127
132
  - only surface timeout attention when the seam's durable watch budget is actually exhausted
128
133
  - for explicit inspect/status requests, report the still-waiting state and exit normally
129
134
  - carry that resolved repo slug through every later GitHub issue/PR command
@@ -198,8 +203,10 @@ node <resolved-skill-scripts>/github/manage-sub-issues.mjs list \
198
203
  --repo <resolved-repo> --issue <parent-number>
199
204
  ```
200
205
 
201
- Do **not** re-implement sub-issue management ad hoc or bypass `manage-sub-issues.mjs`.
202
- Do **not** maintain a body checklist that duplicates the sub-issue tree.
206
+ Ad-hoc bypass of `manage-sub-issues.mjs` and duplicating the tree in the parent body are owned
207
+ by `SUBISSUE-NO-ADHOC-BYPASS` and `SUBISSUE-LEAN-BODY-NO-DUPLICATE` in
208
+ [Sub-Issue Tree Contract](../../docs/sub-issue-tree-contract.md); this procedure does not
209
+ restate those rules.
203
210
 
204
211
  For the full `manage-sub-issues.mjs` contract, use [Sub-Issue Tree Contract](../../docs/sub-issue-tree-contract.md) when working in the `dev-loops` source repository. That contract is a source-repo reference, not part of the bundled installed skill-doc surface. For installed or normalized skill copies, inspect the target repository docs/source directly instead of assuming a bundled shared-doc copy exists.
205
212
 
@@ -231,9 +238,9 @@ The merge itself is gated, not implied by the lines above. Before any `gh pr mer
231
238
  gh pr merge <pr-number> --repo <resolved-repo> --squash --delete-branch
232
239
  ```
233
240
 
234
- Bootstrap-wait interpretation remains fail-closed and observational-first:
235
- - `ready_for_followup`: linked PR has become substantive; resume from that PR
236
- - `timed_out`: observational first; refresh authoritative state
237
- - if refreshed state is still `waiting_for_initial_copilot_implementation`, remain attached to the same durable wait seam and continue waiting
238
- - if refreshed state exits that seam, route based on refreshed state instead of surfacing timeout attention
241
+ Bootstrap-wait interpretation remains fail-closed and observational-first — same seam as Phase 2's `waiting_for_initial_copilot_implementation` handling above: `ready_for_followup` resumes from the now-substantive linked PR; `timed_out` is observational first; refresh authoritative state and re-apply the Phase 2 branching (still-waiting vs. seam-exit) rather than re-deriving it here.
242
+
243
+ ## Cross-references
244
+
245
+ See the "Use it together with" list at the top; additionally, [Epic Tree Refinement Procedure](./epic-tree-refinement-procedure.md) refines an existing sub-issue tree, while this procedure creates it (Phase 3b).
239
246
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  The operator sequence for the local-first plan-file flow. The [Artifact Authority Contract](artifact-authority-contract.md) owns the model and the per-phase contract details; this doc names the shipped helper scripts in the order they run for one plan file. The [Local-Planning Worked Example](local-planning-worked-example.md) shows a single plan file evolving through these steps.
4
4
 
5
- The flow applies under local-planning mode (`strategy.default: local-first`, the shipped default — see the contract's [Shipped default posture](artifact-authority-contract.md#shipped-default-posture)). The plan file lives under `localPlanning.plansDir`, which defaults to `docs/phases/`.
5
+ The flow applies under local-planning mode (`strategy.default: local-first`, the shipped default — see the contract's [Shipped default posture](artifact-authority-contract.md#shipped-default-posture)). The plan file lives under `docs/phases/`.
6
6
 
7
7
  ## Stages
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  One plan file, `docs/phases/phase-42.md`, carried through every stage of the [Local-Planning Flow](local-planning-flow.md): authored, validated, refined in place, held at the local human-review checkpoint, then promoted to a draft PR. Each stage below shows the same file's content as it evolves. The contract details live in the [Artifact Authority Contract](artifact-authority-contract.md); this is a concrete trace of one file.
4
4
 
5
- The repo runs local-planning by default (`strategy.default: local-first`), and the plan lives under `localPlanning.plansDir` (`docs/phases/`).
5
+ The repo runs local-planning by default (`strategy.default: local-first`), and the plan lives under `docs/phases/`.
6
6
 
7
7
  ## Stage 1 — Authored
8
8