dev-loops 0.7.1 → 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 (86) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +7 -7
  3. package/.claude/agents/fixer.md +1 -0
  4. package/.claude/agents/refiner.md +2 -2
  5. package/.claude/agents/review.md +2 -2
  6. package/.claude/commands/loop-enqueue.md +1 -1
  7. package/.claude/commands/loop-info.md +1 -1
  8. package/.claude/commands/loop-start-spike.md +1 -1
  9. package/.claude/skills/copilot-pr-followup/SKILL.md +37 -23
  10. package/.claude/skills/dev-loop/SKILL.md +6 -6
  11. package/.claude/skills/docs/acceptance-criteria-verification.md +3 -2
  12. package/.claude/skills/docs/anti-patterns.md +3 -2
  13. package/.claude/skills/docs/artifact-authority-contract.md +12 -11
  14. package/.claude/skills/docs/confirmation-rules.md +1 -0
  15. package/.claude/skills/docs/contract-style-guide.md +37 -0
  16. package/.claude/skills/docs/copilot-loop-operations.md +18 -14
  17. package/.claude/skills/docs/cross-harness-regression-contract.md +2 -2
  18. package/.claude/skills/docs/debt-remediation-contract.md +1 -1
  19. package/.claude/skills/docs/epic-tree-refinement-procedure.md +25 -22
  20. package/.claude/skills/docs/issue-intake-procedure.md +25 -19
  21. package/.claude/skills/docs/merge-preconditions.md +13 -11
  22. package/.claude/skills/docs/pr-lifecycle-contract.md +44 -35
  23. package/.claude/skills/docs/public-dev-loop-contract.md +59 -43
  24. package/.claude/skills/docs/retrospective-checkpoint-contract.md +18 -5
  25. package/.claude/skills/docs/spike-mode-contract.md +14 -7
  26. package/.claude/skills/docs/stop-conditions.md +30 -15
  27. package/.claude/skills/docs/tracker-first-loop-state.md +13 -8
  28. package/.claude/skills/docs/validation-policy.md +4 -5
  29. package/.claude/skills/local-implementation/SKILL.md +48 -142
  30. package/CHANGELOG.md +6 -0
  31. package/agents/dev-loop.agent.md +8 -8
  32. package/agents/fixer.agent.md +1 -0
  33. package/agents/refiner.agent.md +2 -2
  34. package/agents/review.agent.md +2 -2
  35. package/package.json +5 -4
  36. package/scripts/_core-helpers.mjs +2 -0
  37. package/scripts/claude/headless-dev-loop.mjs +1 -1
  38. package/scripts/docs/validate-rule-ownership.mjs +442 -0
  39. package/scripts/docs/validate-state-machine-conformance.mjs +1092 -0
  40. package/scripts/github/_review-thread-mutations.mjs +5 -2
  41. package/scripts/github/create-pr.mjs +119 -10
  42. package/scripts/github/detect-checkpoint-evidence.mjs +102 -17
  43. package/scripts/github/list-review-threads.mjs +277 -0
  44. package/scripts/github/post-gate-findings.mjs +6 -3
  45. package/scripts/github/request-copilot-review.mjs +92 -26
  46. package/scripts/github/upsert-checkpoint-verdict.mjs +74 -5
  47. package/scripts/github/verify-briefing-prefixes.mjs +191 -0
  48. package/scripts/github/verify-fresh-review-context.mjs +78 -3
  49. package/scripts/github/wait-pr-checks.mjs +171 -0
  50. package/scripts/github/write-gate-context.mjs +213 -7
  51. package/scripts/github/write-gate-findings-log.mjs +54 -2
  52. package/scripts/loop/copilot-pr-handoff.mjs +29 -2
  53. package/scripts/loop/detect-change-scope.mjs +2 -2
  54. package/scripts/loop/detect-copilot-loop-state.mjs +23 -3
  55. package/scripts/loop/detect-pr-gate-coordination-state.mjs +35 -3
  56. package/scripts/loop/resolve-dev-loop-startup.mjs +145 -8
  57. package/scripts/loop/sanctioned-commands.mjs +2 -0
  58. package/scripts/loop/validate-pr-body-spec.mjs +21 -5
  59. package/scripts/pages/build-state-atlas.mjs +50 -48
  60. package/scripts/projects/_resolve-project.mjs +1 -148
  61. package/scripts/projects/list-queue-items.mjs +3 -377
  62. package/scripts/projects/move-queue-item.mjs +3 -410
  63. package/scripts/projects/reorder-queue-item.mjs +3 -22
  64. package/skills/copilot-pr-followup/SKILL.md +37 -23
  65. package/skills/dev-loop/SKILL.md +1 -1
  66. package/skills/docs/acceptance-criteria-verification.md +3 -2
  67. package/skills/docs/anti-patterns.md +3 -2
  68. package/skills/docs/artifact-authority-contract.md +12 -11
  69. package/skills/docs/confirmation-rules.md +1 -0
  70. package/skills/docs/contract-style-guide.md +37 -0
  71. package/skills/docs/copilot-loop-operations.md +18 -14
  72. package/skills/docs/cross-harness-regression-contract.md +2 -2
  73. package/skills/docs/debt-remediation-contract.md +1 -1
  74. package/skills/docs/epic-tree-refinement-procedure.md +25 -22
  75. package/skills/docs/issue-intake-procedure.md +25 -19
  76. package/skills/docs/merge-preconditions.md +13 -11
  77. package/skills/docs/pr-lifecycle-contract.md +44 -35
  78. package/skills/docs/public-dev-loop-contract.md +59 -43
  79. package/skills/docs/required-rules.json +149 -0
  80. package/skills/docs/retrospective-checkpoint-contract.md +18 -5
  81. package/skills/docs/spike-mode-contract.md +14 -7
  82. package/skills/docs/stop-conditions.md +30 -15
  83. package/skills/docs/tracker-first-loop-state.md +13 -8
  84. package/skills/docs/validation-policy.md +4 -5
  85. package/skills/local-implementation/SKILL.md +48 -142
  86. package/scripts/docs/validate-no-duplicate-rules.mjs +0 -250
@@ -1,10 +1,10 @@
1
1
  # Public dev-loop contract
2
2
 
3
- This document is the canonical authority for the public `dev-loop` entrypoint, its routed semantics, accepted shorthand, and the rule that internal strategy names stay behind that public façade.
3
+ Canonical owner for the public `dev-loop` entrypoint: routed semantics, accepted shorthand, and the rule that internal strategy names stay behind the façade.
4
4
 
5
5
  This canonical owner lives in the shipped `skills/docs/` surface because installed skill/runtime consumers reliably own the skills subtree. In installed layouts, read the same contract via [Public Dev Loop Contract](../docs/public-dev-loop-contract.md) from the installed skill directory.
6
6
 
7
- Other repo docs may summarize or link this contract, but they should not redefine it.
7
+ Other repo docs MAY summarize or link this contract; they MUST NOT redefine it.
8
8
 
9
9
  ## Public surface
10
10
 
@@ -12,7 +12,7 @@ The single public entrypoint is:
12
12
 
13
13
  - `dev-loop`
14
14
 
15
- It should be callable from the user-facing workflow surfaces, including:
15
+ `dev-loop` MUST be callable from the user-facing workflow surfaces, including:
16
16
 
17
17
  - `subagent dev-loop`
18
18
  - `/skill:dev-loop`
@@ -28,8 +28,6 @@ Day-one user-intent forms:
28
28
  - auto dev loop on issue `<n>`
29
29
  - what state is the dev loop in?
30
30
 
31
- Users should not have to choose `dev-loop` vs internal seam names up front.
32
-
33
31
  ## Issue-based shorthand auto trigger contract
34
32
 
35
33
  This shorthand form is explicitly accepted and resolves to the same bounded public `dev-loop` intent:
@@ -44,7 +42,7 @@ Canonical mapping:
44
42
 
45
43
  Stop-boundary contract for this shorthand:
46
44
 
47
- 1. continue through the normal GitHub/Copilot loop (assignment, PR watch, draft review/fix, Copilot review/fix, and final pre-approval work) unless a genuine stop condition is reached
45
+ 1. continue through the normal GitHub/Copilot loop (assignment, PR watch, draft/Copilot review-fix, final pre-approval) unless a genuine stop condition is reached
48
46
  2. stop at the final human approval decision by default
49
47
  3. after formal approval, stop again in `waiting_for_merge_authorization` unless merge authorization is explicitly granted for the active issue/PR scope
50
48
  4. merge only after explicit merge authorization for the active issue/PR scope
@@ -76,7 +74,8 @@ Use this taxonomy consistently across docs, discovery surfaces, and tests:
76
74
 
77
75
  Any remaining specialized Copilot behavior stays internal-only behind `dev-loop`.
78
76
 
79
- Regression tests must fail if this taxonomy drifts in wording or surfaced entrypoint assets.
77
+ <!-- rule: FACADE-TAXONOMY-DRIFT-TEST -->
78
+ `FACADE-TAXONOMY-DRIFT-TEST`: Regression tests MUST fail if this taxonomy drifts in wording or surfaced entrypoint assets.
80
79
 
81
80
  ## Canonical current state
82
81
 
@@ -90,17 +89,7 @@ The public router consumes one canonical current state with these top-level dime
90
89
  | `status` | `active` \| `waiting` \| `blocked` \| `approval_ready` \| `merge_ready` \| `done` |
91
90
  | `authorization` | `authorized` \| `needs_confirmation` \| `not_authorized` |
92
91
 
93
- The authoritative first-slice evaluator is:
94
-
95
- - `packages/core/src/loop/public-dev-loop-routing.mjs`
96
-
97
- Authoritative status-report helper:
98
-
99
- - `resolveAuthoritativeDevLoopStatus()` in `packages/core/src/loop/public-dev-loop-routing.mjs`
100
-
101
- Authoritative startup/resume bundle helper:
102
-
103
- - `resolveAuthoritativeStartupResumeBundle()` in `packages/core/src/loop/public-dev-loop-routing.mjs`
92
+ The authoritative first-slice evaluator, status-report helper (`resolveAuthoritativeDevLoopStatus()`), and startup/resume bundle helper (`resolveAuthoritativeStartupResumeBundle()`) all live in `packages/core/src/loop/public-dev-loop-routing.mjs`.
104
93
 
105
94
  Its tests are:
106
95
 
@@ -118,7 +107,8 @@ Before answering status/progress/readiness/merge-state/next-step questions, cons
118
107
 
119
108
  Prior chat context is only a hint, never state authority.
120
109
 
121
- If authoritative identity/state (including issue↔PR linkage when relevant) cannot be resolved confidently, fail closed to reconcile/unknown instead of guessing.
110
+ <!-- rule: FACADE-STATUS-AUTHORITATIVE-FAIL-CLOSED -->
111
+ `FACADE-STATUS-AUTHORITATIVE-FAIL-CLOSED`: If authoritative identity/state (including issue↔PR linkage when relevant) cannot be resolved confidently, consumers MUST fail closed to reconcile/unknown instead of guessing.
122
112
  For async/durable-auto flows, do not claim that `dev-loop` has started or is running unless a visible Pi-managed async run id has also been resolved.
123
113
 
124
114
  When the routed next step requires confirmation for a mutation, the status/startup next action should name that concrete pending mutation (for example issue assignment to `copilot-swe-agent`) instead of generic "approval gate" wording.
@@ -140,10 +130,8 @@ Required authoritative inputs:
140
130
  - `auto_continue_current` always resolves to `durable_auto`
141
131
  - `issueLinkageResolution` (`resolved_linked_pr` \| `resolved_no_open_pr` \| `not_applicable`)
142
132
  - required when `currentState.target.kind === issue`
143
- - `issueReadiness` (`ready` \| `needs_clarification` \| `not_applicable`)
144
- - required for Copilot-first issue targets with `issueLinkageResolution=resolved_no_open_pr`
145
- - `issueAssignmentState` (`unassigned` \| `assigned_to_copilot` \| `not_applicable`)
146
- - required for Copilot-first issue targets with `issueLinkageResolution=resolved_no_open_pr`
133
+ - `issueReadiness` (`ready` \| `needs_clarification` \| `not_applicable`) and `issueAssignmentState` (`unassigned` \| `assigned_to_copilot` \| `not_applicable`)
134
+ - both required for Copilot-first issue targets with `issueLinkageResolution=resolved_no_open_pr`
147
135
  - `artifactState` (`open` \| `closed` \| `merged` \| `not_applicable`)
148
136
  - explicit resolved `loopState` (`unknown` is not authoritative input)
149
137
  - required for async/durable-auto startup or status paths: `asyncRun`
@@ -151,8 +139,8 @@ Required authoritative inputs:
151
139
  - durable-auto success requires `kind=pi_managed_run`, a non-empty visible `runId`, and `visible=true`
152
140
  - when `inspectionState` is provided as `hidden`, `stale`, or `uninspectable`, durable-auto must fail closed with that state surfaced in diagnostics
153
141
  - detached local processes are diagnostic-only evidence and must fail closed instead of being treated as a successful async start
154
- - when refreshed loop state is `linked_pr_ready_for_followup` for an issue target with a resolved linked PR, startup/resume and status resolution must promote stale bootstrap waiting to the linked PR follow-up path (or fail closed if the linked-PR facts are incomplete/contradictory) instead of preserving the old bootstrap wait route
155
- - when refreshed loop state is `prior_linked_pr_closed_unmerged` for a Copilot-owned issue target with `issueLinkageResolution=resolved_no_open_pr`, startup/resume and status resolution must fail closed to reconcile instead of treating the issue as a healthy bootstrap wait or fresh issue-intake path
142
+ - refreshed `linked_pr_ready_for_followup` for an issue target with a resolved linked PR: promote stale bootstrap waiting to the linked PR follow-up path, or fail closed if the linked-PR facts are incomplete/contradictory, instead of preserving the old bootstrap wait route
143
+ - refreshed `prior_linked_pr_closed_unmerged` for a Copilot-owned issue target with `issueLinkageResolution=resolved_no_open_pr`: fail closed to reconcile instead of treating the issue as a healthy bootstrap wait or fresh issue-intake path
156
144
 
157
145
  Resolved bundle output shape:
158
146
 
@@ -222,8 +210,8 @@ Resolved bundle output shape:
222
210
 
223
211
  Dev-mode observability requirement:
224
212
 
225
- - saved artifacts must preserve the `contractTrace` decision, wait strategy, state-refresh boundary, and stop classification so a fresh session can explain why a healthy wait re-attached, stopped, or failed closed without replaying the whole run
226
- - wait/watch artifacts must record the effective timeout budget in force and whether the seam ran as `persistent_watch` or `one_shot_probe`; when a surface does not own a poll interval directly it may record `effectivePollIntervalMs=null` rather than inventing a value
213
+ - saved artifacts MUST preserve the `contractTrace` decision, wait strategy, state-refresh boundary, and stop classification so a fresh session can explain a re-attach, stop, or fail-closed without replaying the run
214
+ - wait/watch artifacts MUST record the effective timeout budget and whether the seam ran as `persistent_watch` or `one_shot_probe`; a surface without a poll interval MAY record `effectivePollIntervalMs=null` instead of inventing one
227
215
 
228
216
  Fail-closed semantics:
229
217
 
@@ -271,7 +259,7 @@ Internal strategy naming is implementation detail; normal orchestration always s
271
259
 
272
260
  Tracker-backed local implementation is an input-source addition to the existing `local_implementation` strategy. It does **not** create a new routing mode, strategy family, or public workflow entrypoint.
273
261
 
274
- For tracker-backed local sessions, the tracker issue is canonical. `docs/phases/phase-<n>.md` must not exist for that same session, and local execution must not maintain a second durable phase-doc copy of the same spec.
262
+ For tracker-backed local sessions, the tracker issue is canonical; the no-duplicate-phase-doc rule is owned by `ARTIFACT-TRACKER-FIRST-NO-DUP` in [Artifact Authority Contract](artifact-authority-contract.md).
275
263
 
276
264
  Deterministic GitHub-backed spec resolution:
277
265
 
@@ -284,13 +272,13 @@ Deterministic GitHub-backed spec resolution:
284
272
  State-sync expectations for this slice:
285
273
 
286
274
  - local branch state and `tmp/` artifacts remain local execution state
287
- - durable scope / acceptance / status changes discovered during local execution should sync back to the tracker issue, because the tracker issue remains the canonical spec source for tracker-backed sessions
275
+ - durable scope / acceptance / status changes discovered during local execution SHOULD sync back to the tracker issue
288
276
  - this slice does **not** introduce full bidirectional tracker sync or tracker-provider adapters beyond the bounded GitHub-backed helper path above
289
277
 
290
278
  Non-duplication rule:
291
279
 
292
280
  - do not create, read, or update `docs/phases/phase-<n>.md` for the same tracker-backed session
293
- - if a duplicate local phase doc already exists for the same tracker-backed session, reconcile that conflict explicitly before continuing; do not silently keep two durable spec surfaces alive
281
+ - if a duplicate local phase doc already exists, reconcile explicitly before continuing rather than keeping two durable spec surfaces alive
294
282
 
295
283
  ## Copilot-first issue-assignment seam (unassigned issues)
296
284
 
@@ -330,7 +318,7 @@ For issue targets, authoritative issue↔PR linkage resolution remains part of s
330
318
 
331
319
  - when canonical issue state includes `linkedPr`, route selection first uses that linked PR as the authoritative routable artifact
332
320
  - when canonical issue state does **not** include `linkedPr`, status/reporting consumers must still require explicit authoritative linkage resolution before asserting there is no open linked PR
333
- - when authoritative linkage resolves an already-open linked PR, that PR is the only canonical active artifact for the issue during follow-up; startup/status/follow-up must reuse it and fail closed against opening another PR until the prior state is explicitly reconciled
321
+ - <!-- rule: FACADE-LINKED-PR-SINGLE-ARTIFACT --> `FACADE-LINKED-PR-SINGLE-ARTIFACT`: when authoritative linkage resolves an already-open linked PR, that PR is the only canonical active artifact for the issue during follow-up; startup/status/follow-up MUST reuse it and MUST fail closed against opening another PR until the prior state is explicitly reconciled
334
322
 
335
323
  ## Deterministic routing order
336
324
 
@@ -349,6 +337,28 @@ First-match-wins routing posture:
349
337
  11. PR owned by Copilot -> `copilot_pr_followup`
350
338
  12. anything else -> fail closed to `needs_reconcile`
351
339
 
340
+ ## Required transitions
341
+
342
+ The gate graph (`DEV_LOOP_GATE` / `PUBLIC_DEV_LOOP_GATE_CONTRACT` in
343
+ `public-dev-loop-routing.mjs`) is stateless per cycle: each `evaluatePublicDevLoopRouting`
344
+ call re-derives one gate from fresh authoritative state, so every non-terminal (route/wait)
345
+ gate can be followed, on the next cycle, by any of the gates.
346
+
347
+ - `final_approval` -> any dev-loop gate
348
+ - `wait_watch` -> any dev-loop gate
349
+ - `local_implementation` -> any dev-loop gate
350
+ - `issue_intake` -> any dev-loop gate
351
+ - `external_pr_followup` -> any dev-loop gate
352
+ - `reviewer_fixer` -> any dev-loop gate
353
+ - `copilot_pr_followup` -> any dev-loop gate
354
+
355
+ Terminal gates (`stop_blocked_or_not_authorized`, `stop_done_terminal`,
356
+ `waiting_for_merge_authorization`, `fail_closed_reconcile`) have no outgoing transitions —
357
+ reaching one ends the current evaluation cycle. Their route-kind classification
358
+ (`stop` / `needs_reconcile`) is the authoritative terminal marker; the route/wait gates above
359
+ are the authoritative non-terminal set. This machine is wired into the L2/L3 state-machine
360
+ conformance harness (`validate-state-machine-conformance.mjs`).
361
+
352
362
  ## Conflict reconciliation path (`CONFLICTING` / `DIRTY`)
353
363
 
354
364
  When an open linked PR reports merge conflict against `main`, treat this as an explicit bounded local-agent reconciliation path, not as a blind merge/update step:
@@ -360,27 +370,33 @@ When an open linked PR reports merge conflict against `main`, treat this as an e
360
370
  - issue/PR scope and acceptance criteria
361
371
  - current-head gate evidence and relevant unresolved review feedback
362
372
  - local validation surface for the touched conflict slice
363
- 3. if required authoritative context is missing, stale for the current head, or contradictory, fail closed to reconcile
373
+ 3. <!-- rule: FACADE-CONFLICT-CONTEXT-FAIL-CLOSED --> `FACADE-CONFLICT-CONTEXT-FAIL-CLOSED`: if required authoritative context is missing, stale for the current head, or contradictory, orchestration MUST fail closed to reconcile
364
374
  4. only when that context is complete for one current head, resolve the conflict locally on the PR branch
365
- 5. after conflict resolution, rerun required local validation, gate checks, and required CI checks for the new head before approval/merge evaluation
375
+ 5. <!-- rule: FACADE-CONFLICT-REVALIDATE-NEW-HEAD --> `FACADE-CONFLICT-REVALIDATE-NEW-HEAD`: after conflict resolution, orchestration MUST rerun required local validation, gate checks, and required CI checks for the new head before approval/merge evaluation
366
376
 
367
377
  ## `auto dev loop` durable auto contract
368
378
 
369
- When the public intent is `auto dev loop`, the router must:
379
+ When the public intent is `auto dev loop`, the router MUST:
370
380
 
371
381
  1. require canonical current state resolution first
372
382
  2. route to the same detected internal strategy as normal state-based routing
373
383
  3. mark execution mode as durable auto ownership (`durable_auto`)
374
384
  4. keep waiting/watch states in healthy-wait semantics (`auto_healthy_wait`)
375
385
 
376
- In healthy waiting states, quiet watcher observations (for example `timeout` or `idle`) are observational only and must not be surfaced as attention by themselves. Escalation is still expected for true blocked/authorization/reconcile states.
386
+ In healthy waiting states, quiet watcher observations (for example `timeout` or `idle`) are observational only and MUST NOT be surfaced as attention by themselves. Escalation is still expected for true blocked/authorization/reconcile states.
377
387
 
378
- For the Copilot-first bootstrap seam (`waiting_for_initial_copilot_implementation`), durable-auto ownership must route to the dedicated `watch-initial-copilot-pr.mjs` watcher with its default 1-hour watch budget. Quiet/no-activity observations alone do not eject durable ownership while refreshed authoritative state still resolves `waiting_for_initial_copilot_implementation`; inspect/status intents may still summarize that state and exit normally. This seam has a bootstrap-only exception to the general blocked escalation rule: when the linked PR is still bootstrap-only, approval-gated Actions/Copilot runs in `action_required` (GitHub Actions run conclusion, not a lifecycle state term) are treated as non-blocking observational signals (surfacing as concluded session activity) and do not by themselves force stop/escalation.
379
- When refreshed authoritative bootstrap state instead resolves `prior_linked_pr_closed_unmerged`, that is not a healthy wait seam: the routed outcome must fail closed to reconcile so status/startup answers surface the prior closed-unmerged PR decision rather than implying normal watch continuity.
388
+ Bootstrap-only exception to the general blocked-escalation rule for `waiting_for_initial_copilot_implementation`:
380
389
 
381
- When that refreshed seam state advances to `linked_pr_ready_for_followup`, durable-auto continuation must re-enter the same linked PR follow-up path. If the follow-up handoff carries `conductorRouting.handoffEnvelope.requiresLocalIsolation=true`, orchestration should continue through an isolated checkout/worktree transition instead of treating that boundary as final completion.
390
+ | Rule ID | Condition | Outcome |
391
+ |---|---|---|
392
+ | <!-- rule: FACADE-BOOTSTRAP-WATCH-ROUTE --> `FACADE-BOOTSTRAP-WATCH-ROUTE` | durable-auto ownership of this seam | ownership MUST route to the dedicated `watch-initial-copilot-pr.mjs` watcher with its default 1-hour watch budget |
393
+ | <!-- rule: FACADE-BOOTSTRAP-QUIET-NO-EJECT --> `FACADE-BOOTSTRAP-QUIET-NO-EJECT` | quiet/no-activity observation while refreshed state still resolves this seam | durable ownership MUST NOT be ejected; inspect/status intents MAY still summarize that state and exit normally |
394
+ | <!-- rule: FACADE-BOOTSTRAP-ACTION-REQUIRED-NONBLOCKING --> `FACADE-BOOTSTRAP-ACTION-REQUIRED-NONBLOCKING` | bootstrap-only linked PR; approval-gated Actions/Copilot run reports `action_required` (a run conclusion, not a lifecycle state) | treated as a non-blocking observational signal (concluded session activity); MUST NOT by itself force stop/escalation |
395
+ | <!-- rule: FACADE-BOOTSTRAP-CLOSED-UNMERGED-RECONCILE --> `FACADE-BOOTSTRAP-CLOSED-UNMERGED-RECONCILE` | refreshed bootstrap state resolves `prior_linked_pr_closed_unmerged` | MUST fail closed to reconcile so status/startup surfaces the prior closed-unmerged decision instead of normal watch continuity |
396
+ | <!-- rule: FACADE-BOOTSTRAP-FOLLOWUP-REENTRY --> `FACADE-BOOTSTRAP-FOLLOWUP-REENTRY` | refreshed seam state advances to `linked_pr_ready_for_followup` | durable-auto continuation MUST re-enter the same linked PR follow-up path |
397
+ | <!-- rule: FACADE-BOOTSTRAP-ISOLATED-WORKTREE-CONTINUATION --> `FACADE-BOOTSTRAP-ISOLATED-WORKTREE-CONTINUATION` | follow-up handoff carries `conductorRouting.handoffEnvelope.requiresLocalIsolation=true` | orchestration SHOULD continue through an isolated checkout/worktree transition rather than treat that boundary as final completion (the runtime surfaces the flag; it does not enforce re-entry) |
382
398
 
383
- Main conductor orchestration must treat non-terminal follow-up/wait states (for example `waiting_for_copilot_review`) as continuation boundaries rather than clean completion. If an async child exits before the requested stop boundary and continuation is feasible, re-dispatch via the main session driver (the subagent exits on external wait; the main session re-dispatches); otherwise surface the concrete blocker.
399
+ Main conductor orchestration MUST treat non-terminal follow-up/wait states (for example `waiting_for_copilot_review`) as continuation boundaries rather than clean completion. If an async child exits before the requested stop boundary and continuation is feasible, re-dispatch via the main session driver (the subagent exits on external wait; the main session re-dispatches); otherwise surface the concrete blocker.
384
400
 
385
401
  ## Internal / external model
386
402
 
@@ -413,9 +429,9 @@ flowchart TD
413
429
 
414
430
  - `dev-loop` is the only intended public workflow entrypoint.
415
431
  - any remaining specialized Copilot behavior is internal-only and non-user-invocable behind the canonical internal route-pack surface (`issue-intake`, `copilot-pr-followup`, `local-implementation`, `final-approval`).
416
- - Documentation and examples should lead with `dev-loop` and explain routed behavior.
417
- - Almost all workflow branching should converge into deterministic state-machine/tooling surfaces behind `dev-loop`.
418
- - User-visible variation should be expressed through the external `dev-loop` API / bounded parameters or settings, not by preserving multiple public workflow names or legacy compatibility seams.
432
+ - Documentation and examples SHOULD lead with `dev-loop` and explain routed behavior.
433
+ - Workflow branching SHOULD converge into deterministic state-machine/tooling surfaces behind `dev-loop`.
434
+ - User-visible variation SHOULD be expressed through the `dev-loop` API/parameters or settings, not multiple public workflow names or legacy compatibility seams.
419
435
 
420
436
  ## Bounded variation parameter contract
421
437
 
@@ -461,7 +477,7 @@ The following parameter/state combinations fail closed to `needs_reconcile` inst
461
477
  | `mode=bounded_handoff` + `intent=auto_continue_current` | `auto_continue_current` always requires durable auto execution mode |
462
478
  | Unrecognized `mode` value | Value not on the bounded allow-list |
463
479
  | Unrecognized `targetPreference` value | Value not on the bounded allow-list |
464
- | `watch=true` when an otherwise-successful routed result is not wait/watch-eligible | Watch semantics require a routed wait result (`routeKind=wait`), not just `selectedGate=wait_watch`; existing `stop` and `needs_reconcile` outcomes stay authoritative |
480
+ | `watch=true` when an otherwise-successful routed result is not wait/watch-eligible | Watch semantics require a routed wait result (`routeKind=wait`), not just `selectedGate=wait_watch`; `stop`/`needs_reconcile` outcomes stay authoritative |
465
481
  | Non-boolean `watch` value | Value is outside the bounded boolean allow-list and must fail closed |
466
482
  | `targetPreference=prefer_local` when authoritative state has a linked PR or active PR artifact | Preference must not override authoritative PR/linked-PR active artifact truth |
467
483
  | `mode=durable_auto` without authoritative current state | Durable auto requires authoritative current state to route from |
@@ -1,6 +1,9 @@
1
1
  # Retrospective checkpoint contract
2
2
 
3
- This document defines the enforcement seam for the post-run behavioral retrospective checkpoint after qualifying async `dev-loop` completions in this repository. Whether a missing checkpoint blocks the next qualifying start/resume is controlled by `.devloops` at repo root `workflow.requireRetrospective`; shipped defaults remain permissive and this repo opts in.
3
+ Canonical owner for the enforcement seam of the post-run behavioral retrospective checkpoint after qualifying async `dev-loop` completions in this repository.
4
+
5
+ <!-- rule: RETRO-ENFORCEMENT-CONFIG-GATED -->
6
+ Whether a missing checkpoint blocks the next qualifying start/resume MUST be controlled by `.devloops` at repo root `workflow.requireRetrospective`; shipped defaults remain permissive and this repo opts in.
4
7
 
5
8
  ## Relationship to formal dev mode
6
9
 
@@ -85,21 +88,24 @@ Callers have two supported integration options:
85
88
  4. Call `evaluateRetrospectiveGate({ checkpointState, proposedRouting })`.
86
89
  5. Use the gate result (not the raw routing result) as the effective routing decision when enforcement is enabled; otherwise keep the raw routing result and treat the checkpoint artifact as advisory context only.
87
90
 
88
- If the gate result is `needs_reconcile`, the caller must not proceed with the proposed routing. The `nextAction` field instructs the operator to complete or explicitly skip the retrospective.
91
+ <!-- rule: RETRO-GATE-FAIL-CLOSED -->
92
+ If the gate result is `needs_reconcile`, the caller MUST NOT proceed with the proposed routing. The `nextAction` field instructs the operator to complete or explicitly skip the retrospective.
89
93
 
90
94
  ## Advisory findings — never a merge gate (issue #1077, Reading B)
91
95
 
96
+ <!-- rule: RETRO-ADVISORY-NEVER-GATE -->
92
97
  The retrospective is **advisory**: it runs, records flagged raw-call / discipline
93
98
  observations honestly, and passes them back to the conductor (main agent) to
94
- **decide** what to do with them — but it **NEVER blocks a merge or any lifecycle
95
- transition**. The pre-merge retrospective gate (`evaluateRetrospectiveMergeApproval`
99
+ **decide** what to do with them — but it MUST NOT block a merge or any lifecycle
100
+ transition. The pre-merge retrospective gate (`evaluateRetrospectiveMergeApproval`
96
101
  and the `requireRetrospectiveGate` / `requireRetrospectiveInternalTooling` config
97
102
  keys) has been **removed**. There is no `retrospective_gate_pending` / `blocked`
98
103
  disposition on account of the internal-tooling raw-call record.
99
104
 
100
105
  ### How findings travel (Reading B)
101
106
 
102
- 1. **Deterministic return contract.** The loop subagent's handoff envelope carries
107
+ <!-- rule: RETRO-FINDINGS-ENVELOPE-CARRY -->
108
+ 1. **Deterministic return contract.** The loop subagent's handoff envelope MUST carry
103
109
  the retrospective findings as a structured `retrospectiveFindings` field — the
104
110
  `check-retro-tooling.mjs` JSON output (`{ internalToolingOnly, rawCallViolations,
105
111
  allowedWriteOps }`), not prose. The conductor reads that field. This is a hard
@@ -134,6 +140,13 @@ verifier records these as `allowedWriteOps` rather than violations so they are
134
140
  surfaced distinctly, not as breaches. Close the gap with a wrapper to remove an
135
141
  entry. None of these block anything.
136
142
 
143
+ **Inline-interpreter check item:** the raw-call scan below mechanically catches
144
+ `node -e`/`python3 -c`/heredoc calls as a `rawCallViolations` entry — the same
145
+ class barred by `OPS-NO-INLINE-INTERPRETER` in
146
+ [Copilot loop operations](copilot-loop-operations.md). This is an addition to
147
+ what the retrospective records, not a new gate: `RETRO-ADVISORY-NEVER-GATE`
148
+ semantics are unchanged.
149
+
137
150
  ### Deterministic verifier (findings-producer)
138
151
 
139
152
  `node scripts/loop/check-retro-tooling.mjs [--transcript <path>] [--json]` reads a
@@ -1,6 +1,6 @@
1
1
  # Spike-mode contract
2
2
 
3
- A spike is a time-boxed, exploratory dev-loop run started from a local question with no GitHub issue. Its deliverable is a findings document. When the exploration reaches a recommendation, the operator concludes it with one of two exits: **discard** (drop it with zero tracker artifacts) or **graduate** (emit a #947-style local-first plan file that enters the existing plan→PR promotion path).
3
+ Canonical owner for spike mode: the time-boxed, exploratory dev-loop run started from a local question with no GitHub issue. Its deliverable is a findings document. When the exploration reaches a recommendation, the operator concludes it with one of two exits: **discard** (drop it with zero tracker artifacts) or **graduate** (emit a #947-style local-first plan file that enters the existing plan→PR promotion path).
4
4
 
5
5
  This doc is the canonical operator sequence for spike mode and carries a worked example showing both exits. The spike artifact lives outside the tracker; it is exempt from the production-gate ceremony at entry and runs under a relaxed gate profile (`gates.spike`). The [Artifact Authority Contract](artifact-authority-contract.md) owns the local-planning model a graduated spike feeds into.
6
6
 
@@ -53,7 +53,10 @@ Hand the artifact to startup with `--spike` (mutually exclusive with `--issue`,
53
53
  node scripts/loop/resolve-dev-loop-startup.mjs --spike <spike.md>
54
54
  ```
55
55
 
56
- `buildSpikeInput` (`scripts/loop/resolve-dev-loop-startup.mjs`) reads the file read-only — no tracker mutation, no GitHub call, no issue or PR number. It requires the exploration scaffold; a missing/unreadable file, or one failing `validateSpikeExplorationSections`, throws and startup fails closed (exit 1, no readiness bundle). On success it builds a `local_phase` startup input with the resolved spike path as the target `phase` and threads `spikeIntakeState` onto the output. A spike with no Recommendation reports `spike_in_progress`. The spike path is exempt from the worktree-isolation guard because there is no issue to key a worktree on (`planFileExempt: true`).
56
+ `buildSpikeInput` (`scripts/loop/resolve-dev-loop-startup.mjs`) reads the file read-only — no tracker mutation, no GitHub call, no issue or PR number.
57
+
58
+ <!-- rule: SPIKE-STARTUP-SCAFFOLD-GATE -->
59
+ Startup MUST require the exploration scaffold: a missing/unreadable file, or one failing `validateSpikeExplorationSections`, MUST throw, and startup MUST fail closed (exit 1, no readiness bundle). On success it builds a `local_phase` startup input with the resolved spike path as the target `phase` and threads `spikeIntakeState` onto the output. A spike with no Recommendation reports `spike_in_progress`. The spike path is exempt from the worktree-isolation guard because there is no issue to key a worktree on (`planFileExempt: true`).
57
60
 
58
61
  ### 3. Run under the relaxed gate profile
59
62
 
@@ -67,7 +70,8 @@ When the exploration concludes, fill in `## Recommendation`. The intake state is
67
70
  node scripts/refine/exit-spike.mjs --spike-file <spike.md> --disposition <discard|graduate> [--plan-file <path>] [--json]
68
71
  ```
69
72
 
70
- `evaluateSpikeExit` (`packages/core/src/loop/spike-exit-contract.mjs`) is eligible only from `spike_ready_for_exit`. From any other state it fails closed with `not_ready_for_exit`; an unrecognized disposition fails closed with `unknown_disposition`. On a fail-closed path the CLI makes zero tracker mutation, writes no plan file, and exits 1.
73
+ <!-- rule: SPIKE-EXIT-ELIGIBILITY -->
74
+ `evaluateSpikeExit` (`packages/core/src/loop/spike-exit-contract.mjs`) MUST be eligible only from `spike_ready_for_exit`: from any other state it MUST fail closed with `not_ready_for_exit`, and an unrecognized disposition MUST fail closed with `unknown_disposition`. On a fail-closed path the CLI MUST make zero tracker mutation, write no plan file, and exit 1.
71
75
 
72
76
  ## Exit dispositions
73
77
 
@@ -75,22 +79,25 @@ The two dispositions are `SPIKE_EXIT_DISPOSITION.DISCARD` (`"discard"`) and `SPI
75
79
 
76
80
  ### discard
77
81
 
78
- The recommendation is "don't pursue". The spike is dropped with zero tracker artifacts; the findings document on disk is the whole record. The CLI writes nothing and creates no GitHub artifact. `--plan-file` is irrelevant for a discard.
82
+ <!-- rule: SPIKE-DISCARD-ZERO-MUTATION -->
83
+ The recommendation is "don't pursue". Discard MUST drop the spike with zero tracker artifacts — the findings document on disk is the whole record. The CLI MUST write nothing and MUST create no GitHub artifact. `--plan-file` is irrelevant for a discard.
79
84
 
80
85
  ### graduate
81
86
 
82
- The recommendation is "pursue this". `buildGraduatedPlanBody` builds a #947-consumable plan-file body from the spike's `Question`, `Approach`, `Findings`, and `Recommendation`, and `exit-spike.mjs` writes it to the required `--plan-file` path. The mapping:
87
+ <!-- rule: SPIKE-GRADUATE-PLAN-FILE-REQUIRED -->
88
+ The recommendation is "pursue this". `buildGraduatedPlanBody` builds a #947-consumable plan-file body from the spike's `Question`, `Approach`, `Findings`, and `Recommendation`, and `exit-spike.mjs` writes it to the required `--plan-file` path — `--plan-file` MUST be present for a graduate exit; the CLI MUST NOT guess an output path. The mapping:
83
89
 
84
90
  - `Question` + `Approach` become the Objective's context.
85
91
  - `Recommendation` becomes the In-scope work.
86
92
  - `Findings` are recorded as supporting evidence.
87
93
  - A fixed Explicit non-goals block keeps the plan from re-opening the concluded exploration.
88
94
 
89
- The emitted body carries the four plan-file base sections (`## Status`, `## Objective`, `## In scope`, `## Explicit non-goals` — see [Plan-file Contract](plan-file-contract.md)), so it passes `validatePlanFile` and enters the local-first plan→PR promotion path (#952) unchanged: refine it, hold the local human-review checkpoint, then `promote-plan.mjs`. Graduation is idempotent — `buildGraduatedPlanBody` is pure, so re-running reproduces the same plan file. `buildGraduatedPlanBody` fails closed (throws) on an empty required section, so a graduate exit cannot emit a plan the validator would reject. `--plan-file` is required for graduate (the CLI never guesses an output path).
95
+ The emitted body carries the four plan-file base sections (`## Status`, `## Objective`, `## In scope`, `## Explicit non-goals` — see [Plan-file Contract](plan-file-contract.md)), so it passes `validatePlanFile` and enters the local-first plan→PR promotion path (#952) unchanged: refine it, hold the local human-review checkpoint, then `promote-plan.mjs`. Graduation is idempotent — `buildGraduatedPlanBody` is pure, so re-running reproduces the same plan file, and it fails closed (throws) on an empty required section, so a graduate exit cannot emit a plan the validator would reject.
90
96
 
91
97
  ## Relaxed gate profile
92
98
 
93
- A spike's deliverable is a findings doc, so it does not carry the full production draft → pre-approval → Copilot gate set. `gates.spike` is a relaxed profile, shipped in `packages/core/src/config/extension-defaults.yaml`:
99
+ <!-- rule: SPIKE-RELAXED-GATE-PROFILE -->
100
+ A spike's deliverable is a findings doc, so it MUST NOT carry the full production draft → pre-approval → Copilot gate set; it runs under `gates.spike`, a relaxed profile shipped in `packages/core/src/config/extension-defaults.yaml`:
94
101
 
95
102
  ```yaml
96
103
  gates:
@@ -2,29 +2,44 @@
2
2
 
3
3
  Canonical owner for agent stop / wait / block conditions across all workflow families.
4
4
 
5
+ ## Terms
6
+
7
+ | Term | Definition |
8
+ |---|---|
9
+ | <!-- term: state:blocked --> `blocked` | Lifecycle state requiring a human decision before the loop continues. |
10
+ | <!-- term: state:done --> `done` | Terminal lifecycle state. |
11
+ | <!-- term: state:approval_ready --> `approval_ready` | Lifecycle state where approval evidence is ready but merge authorization is not implied. |
12
+ | <!-- term: state:merge_ready --> `merge_ready` | Lifecycle state where merge preconditions are ready to evaluate. |
13
+ | <!-- term: state:waiting --> `waiting` | Lifecycle state for healthy external waits. |
14
+ | <!-- term: state:waiting_for_initial_copilot_implementation --> `waiting_for_initial_copilot_implementation` | Bootstrap wait state for the first Copilot PR. |
15
+ | <!-- term: state:waiting_for_copilot_review --> `waiting_for_copilot_review` | Copilot review-settle wait state for the current head. |
16
+ | <!-- term: state:waiting_for_merge_authorization --> `waiting_for_merge_authorization` | Stop state for merge-ready work without explicit merge authorization. |
17
+ | <!-- term: reason:needs_reconcile --> `needs_reconcile` | Reconcile reason for ambiguous, contradictory, or unsupported state. |
18
+
5
19
  ## Genuine stop conditions
6
20
 
7
- | Condition | Strategy | Behavior |
8
- |---|---|---|
9
- | `blocked` lifecycle state | all | Stop for human decision |
10
- | `done` lifecycle state | all | Terminal stop |
11
- | `approval_ready` without explicit merge auth | `final_approval` | Stop at approval gate |
12
- | `merge_ready` without explicit merge auth | all | Stop at `waiting_for_merge_authorization` |
13
- | `autonomy.humanMergeOnly: true` | all | Always stop at merge for human action, even with explicit merge auth agent never runs `gh pr merge` (see [Merge preconditions](merge-preconditions.md)) |
14
- | Ambiguous / contradictory state | all | Fail closed to `needs_reconcile` |
15
- | Missing authoritative startup inputs | `dev-loop` | Fail closed |
21
+ | Rule ID | Condition | Strategy | Behavior |
22
+ |---|---|---|---|
23
+ | <!-- rule: STOP-BLOCKED-001 --> `STOP-BLOCKED-001` | `blocked` lifecycle state | all | The loop MUST stop for a human decision. |
24
+ | <!-- rule: STOP-DONE-001 --> `STOP-DONE-001` | `done` lifecycle state | all | The loop MUST treat the state as terminal. |
25
+ | <!-- rule: STOP-APPROVAL-001 --> `STOP-APPROVAL-001` | `approval_ready` without explicit merge authorization | `final_approval` | The loop MUST stop at the approval gate. |
26
+ | <!-- rule: STOP-MERGE-AUTH-001 --> `STOP-MERGE-AUTH-001` | `merge_ready` without explicit merge authorization | all | The loop MUST stop at `waiting_for_merge_authorization`. |
27
+ | <!-- rule: STOP-HUMAN-MERGE-001 --> `STOP-HUMAN-MERGE-001` | `autonomy.humanMergeOnly: true` | all | The loop MUST stop at merge for human action even with explicit merge authorization; the agent MUST NOT run `gh pr merge` (see [Merge preconditions](merge-preconditions.md)). |
28
+ | <!-- rule: STOP-RECONCILE-001 --> `STOP-RECONCILE-001` | Ambiguous or contradictory state | all | The loop MUST fail closed to `needs_reconcile`. |
29
+ | <!-- rule: STOP-STARTUP-INPUTS-001 --> `STOP-STARTUP-INPUTS-001` | Missing authoritative startup inputs | `dev-loop` | The loop MUST fail closed. |
16
30
 
17
31
  ## Non-stop conditions
18
32
 
19
- | Condition | Strategy | Behavior |
20
- |---|---|---|
21
- | `waiting` lifecycle state | `wait_watch` | Healthy wait; re-dispatch from main session |
22
- | `waiting_for_initial_copilot_implementation` | `issue_intake` | Bootstrap wait with 1h watch budget |
23
- | `waiting_for_copilot_review` | `copilot_pr_followup` | Continuation boundary, not completion |
24
- | Quiet watcher observations | `wait_watch` | Observational only, do not surface |
33
+ | Rule ID | Condition | Strategy | Behavior |
34
+ |---|---|---|---|
35
+ | <!-- rule: STOP-WAIT-001 --> `STOP-WAIT-001` | `waiting` lifecycle state | `wait_watch` | The loop MUST treat this as a healthy wait and re-dispatch from the main session. |
36
+ | <!-- rule: STOP-INITIAL-COPILOT-001 --> `STOP-INITIAL-COPILOT-001` | `waiting_for_initial_copilot_implementation` | `issue_intake` | The loop MUST use the bootstrap wait with a one-hour watch budget. |
37
+ | <!-- rule: STOP-COPILOT-REVIEW-001 --> `STOP-COPILOT-REVIEW-001` | `waiting_for_copilot_review` | `copilot_pr_followup` | The loop MUST treat this as a continuation boundary, not completion. |
38
+ | <!-- rule: STOP-QUIET-WATCHER-001 --> `STOP-QUIET-WATCHER-001` | Quiet watcher observations | `wait_watch` | The loop MUST treat quiet observations as observational only and MUST NOT surface them as stops by themselves. |
25
39
 
26
40
  ## Cross-references
27
41
 
28
42
  - [Confirmation rules](confirmation-rules.md)
29
43
  - [Merge preconditions](merge-preconditions.md)
30
44
  - [Public Dev Loop Contract](public-dev-loop-contract.md)
45
+ - [Contract style guide](contract-style-guide.md)
@@ -1,7 +1,8 @@
1
1
  # Tracker-First Story-to-PR Contract
2
2
 
3
- This document defines the adapter-agnostic MVP contract for the tracker-first
4
- story-to-PR workflow in `dev-loops`.
3
+ Canonical owner for the adapter-agnostic MVP contract for the tracker-first
4
+ story-to-PR workflow in `dev-loops`, and for the tracker-first loop state
5
+ machine (#449).
5
6
 
6
7
  **MVP invariant: one tracker work item → one GitHub PR.**
7
8
 
@@ -30,7 +31,8 @@ Implementation for each:
30
31
  | Epic / PRD reference | Optional linked metadata only; no roll-up or automation in this slice |
31
32
  | ADR / RFC reference | Optional linked metadata only; no decision-sync in this slice |
32
33
 
33
- **Invariant:** For any single tracker work item in scope, there is at most
34
+ <!-- rule: TRACKER-ONE-ACTIVE-PR -->
35
+ For any single tracker work item in scope, there MUST be at most
34
36
  one active GitHub PR at a time. Multi-PR workflows and roll-up automation
35
37
  are out of scope for this slice.
36
38
 
@@ -57,6 +59,9 @@ already-correct PR leaves it unchanged.
57
59
 
58
60
  ### 3.1 Required PR Metadata
59
61
 
62
+ <!-- rule: TRACKER-PROJECTION-REQUIRED-METADATA -->
63
+ Every projected PR MUST carry the following fields:
64
+
60
65
  | Field | Rule |
61
66
  |---|---|
62
67
  | PR title | `[{TRACKER_ID}] {tracker item title}` — bracketed tracker identifier followed by the item title verbatim |
@@ -64,7 +69,7 @@ already-correct PR leaves it unchanged.
64
69
  | PR body — Summary section | Required. Brief description of the change implemented in this PR. |
65
70
  | PR body — Acceptance Criteria section | Required. Copied or linked from the tracker item's acceptance criteria. |
66
71
  | Labels | Required: `tracker:{TRACKER_ID}`. Optional reference labels: `epic:{EPIC_ID}`, `prd:{PRD_ID}` when references are present. |
67
- | Draft state | PR must start as a draft. It must not be marked ready-for-review until development work is complete. |
72
+ | Draft state | PR MUST start as a draft. It MUST NOT be marked ready-for-review until development work is complete. |
68
73
 
69
74
  ### 3.2 Optional Reference Metadata
70
75
 
@@ -85,9 +90,9 @@ They are read-only metadata in this slice and do not trigger any automation:
85
90
  2. **On tracker item field change**: Re-apply projection rules to PR title,
86
91
  body, and labels. Leave any sections not covered by projection rules
87
92
  (e.g. reviewer-added review comments) intact.
88
- 3. **Idempotent regeneration**: If the PR already has the correct title, body
89
- sections, and labels, no mutation is performed. Projection is a no-op
90
- when the current state already matches the target.
93
+ 3. <!-- rule: TRACKER-PROJECTION-IDEMPOTENT --> **Idempotent regeneration**: Projection MUST be a no-op when the PR already has the correct
94
+ title, body sections, and labels re-applying the rules to an already-correct PR MUST NOT
95
+ mutate it.
91
96
 
92
97
  ### 3.4 PR Body Template
93
98
 
@@ -120,7 +125,7 @@ implementations map canonical action names to tracker-native field updates.
120
125
  | `pr_merged` — PR merged | `set_done` | Tracker moves to done/completed terminal state |
121
126
  | `pr_closed_unmerged` — PR closed without merge | `none` | No automatic terminal transition; human decision required |
122
127
  | `no_tracker_item` | `none` | No tracker item to update |
123
- | `blocked_needs_user_decision` | `none` | Stop and report; no automatic tracker update |
128
+ | <!-- rule: TRACKER-BLOCKED-FAIL-CLOSED --> `blocked_needs_user_decision` | `none` | An unexpected or contradictory snapshot MUST stop and report; the helper MUST NOT apply an automatic tracker update |
124
129
 
125
130
  ### 4.2 Event Triggers
126
131
 
@@ -4,9 +4,8 @@ Canonical owner for validation requirements across all workflow families.
4
4
 
5
5
  ## Default validation
6
6
 
7
- - `npm run verify` is the default repo-level local validation path
8
- - Must pass before: PR creation, gate entry, merge
9
- - At minimum: `npm test && npm run test:dev-loop`
7
+ <!-- rule: VALIDATE-VERIFY-BEFORE-GATE -->
8
+ `VALIDATE-VERIFY-BEFORE-GATE`: `npm run verify` is the default repo-level local validation path and MUST pass before PR creation, gate entry, and merge; at minimum this means `npm test && npm run test:dev-loop`.
10
9
 
11
10
  ## Gate-specific requirements
12
11
 
@@ -17,8 +16,8 @@ Canonical owner for validation requirements across all workflow families.
17
16
 
18
17
  ## Coverage requirements
19
18
 
20
- - ≥90% coverage for lines, statements, functions, and branches on changed files
21
- - Test-first for all non-trivial logic
19
+ <!-- rule: VALIDATE-COVERAGE-THRESHOLD -->
20
+ `VALIDATE-COVERAGE-THRESHOLD`: Changed files MUST have ≥90% coverage for lines, statements, functions, and branches, and non-trivial logic MUST be test-first.
22
21
 
23
22
  ## Cross-references
24
23