dev-loops 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +7 -7
  3. package/.claude/agents/fixer.md +1 -0
  4. package/.claude/agents/refiner.md +4 -4
  5. package/.claude/agents/review.md +10 -13
  6. package/.claude/commands/loop-continue.md +2 -1
  7. package/.claude/commands/loop-enqueue.md +9 -2
  8. package/.claude/commands/loop-info.md +1 -1
  9. package/.claude/commands/loop-start-spike.md +1 -1
  10. package/.claude/hooks/_bash-command-classify.mjs +7 -6
  11. package/.claude/hooks/_hook-decisions.mjs +5 -4
  12. package/.claude/skills/copilot-pr-followup/SKILL.md +42 -28
  13. package/.claude/skills/dev-loop/SKILL.md +8 -8
  14. package/.claude/skills/docs/acceptance-criteria-verification.md +13 -4
  15. package/.claude/skills/docs/anti-patterns.md +6 -5
  16. package/.claude/skills/docs/artifact-authority-contract.md +17 -14
  17. package/.claude/skills/docs/confirmation-rules.md +2 -1
  18. package/.claude/skills/docs/contract-style-guide.md +37 -0
  19. package/.claude/skills/docs/copilot-loop-operations.md +21 -15
  20. package/.claude/skills/docs/cross-harness-regression-contract.md +2 -2
  21. package/.claude/skills/docs/debt-remediation-contract.md +1 -1
  22. package/.claude/skills/docs/epic-tree-refinement-procedure.md +25 -22
  23. package/.claude/skills/docs/issue-intake-procedure.md +37 -22
  24. package/.claude/skills/docs/merge-preconditions.md +17 -13
  25. package/.claude/skills/docs/pr-lifecycle-contract.md +45 -36
  26. package/.claude/skills/docs/public-dev-loop-contract.md +61 -44
  27. package/.claude/skills/docs/retrospective-checkpoint-contract.md +25 -9
  28. package/.claude/skills/docs/spike-mode-contract.md +14 -7
  29. package/.claude/skills/docs/stop-conditions.md +30 -15
  30. package/.claude/skills/docs/tracker-first-loop-state.md +13 -8
  31. package/.claude/skills/docs/validation-policy.md +4 -5
  32. package/.claude/skills/local-implementation/SKILL.md +49 -143
  33. package/.claude/skills/loop-grill/SKILL.md +34 -14
  34. package/AGENTS.md +1 -1
  35. package/CHANGELOG.md +36 -0
  36. package/README.md +95 -189
  37. package/agents/dev-loop.agent.md +8 -8
  38. package/agents/fixer.agent.md +1 -0
  39. package/agents/refiner.agent.md +4 -4
  40. package/agents/review.agent.md +10 -13
  41. package/extension/README.md +5 -4
  42. package/package.json +7 -5
  43. package/scripts/_core-helpers.mjs +2 -0
  44. package/scripts/claude/headless-dev-loop.mjs +1 -1
  45. package/scripts/docs/validate-rule-ownership.mjs +442 -0
  46. package/scripts/docs/validate-state-machine-conformance.mjs +1169 -0
  47. package/scripts/github/_gate-names.mjs +5 -0
  48. package/scripts/github/_review-thread-mutations.mjs +5 -2
  49. package/scripts/github/capture-review-threads.mjs +2 -2
  50. package/scripts/github/create-pr.mjs +119 -10
  51. package/scripts/github/detect-checkpoint-evidence.mjs +109 -24
  52. package/scripts/github/edit-issue.mjs +259 -0
  53. package/scripts/github/list-review-threads.mjs +277 -0
  54. package/scripts/github/post-gate-findings.mjs +6 -3
  55. package/scripts/github/probe-ci-status.mjs +18 -0
  56. package/scripts/github/probe-copilot-review.mjs +24 -3
  57. package/scripts/github/reconcile-draft-gate.mjs +13 -13
  58. package/scripts/github/request-copilot-review.mjs +109 -42
  59. package/scripts/github/upsert-checkpoint-verdict.mjs +98 -27
  60. package/scripts/github/verify-briefing-prefixes.mjs +382 -0
  61. package/scripts/github/verify-fresh-review-context.mjs +78 -3
  62. package/scripts/github/wait-pr-checks.mjs +171 -0
  63. package/scripts/github/write-gate-context.mjs +220 -10
  64. package/scripts/github/write-gate-findings-log.mjs +54 -2
  65. package/scripts/loop/_post-convergence-change.mjs +2 -2
  66. package/scripts/loop/_pr-runner-coordination.mjs +112 -13
  67. package/scripts/loop/check-retro-tooling.mjs +14 -9
  68. package/scripts/loop/copilot-pr-handoff.mjs +47 -16
  69. package/scripts/loop/detect-change-scope.mjs +2 -2
  70. package/scripts/loop/detect-copilot-loop-state.mjs +34 -14
  71. package/scripts/loop/detect-internal-only-pr.mjs +6 -6
  72. package/scripts/loop/detect-pr-gate-coordination-state.mjs +152 -18
  73. package/scripts/loop/detect-refinement-grill-state.mjs +136 -0
  74. package/scripts/loop/resolve-dev-loop-startup.mjs +145 -8
  75. package/scripts/loop/run-watch-cycle.mjs +42 -7
  76. package/scripts/loop/sanctioned-commands.mjs +3 -0
  77. package/scripts/loop/validate-pr-body-spec.mjs +21 -5
  78. package/scripts/pages/build-state-atlas.mjs +65 -48
  79. package/scripts/projects/_resolve-project.mjs +1 -148
  80. package/scripts/projects/add-queue-item.mjs +87 -4
  81. package/scripts/projects/list-queue-items.mjs +3 -377
  82. package/scripts/projects/move-queue-item.mjs +3 -410
  83. package/scripts/projects/reorder-queue-item.mjs +3 -22
  84. package/skills/copilot-pr-followup/SKILL.md +42 -28
  85. package/skills/dev-loop/SKILL.md +3 -3
  86. package/skills/docs/acceptance-criteria-verification.md +13 -4
  87. package/skills/docs/anti-patterns.md +6 -5
  88. package/skills/docs/artifact-authority-contract.md +17 -14
  89. package/skills/docs/confirmation-rules.md +2 -1
  90. package/skills/docs/contract-style-guide.md +37 -0
  91. package/skills/docs/copilot-loop-operations.md +21 -15
  92. package/skills/docs/cross-harness-regression-contract.md +2 -2
  93. package/skills/docs/debt-remediation-contract.md +1 -1
  94. package/skills/docs/epic-tree-refinement-procedure.md +25 -22
  95. package/skills/docs/issue-intake-procedure.md +37 -22
  96. package/skills/docs/merge-preconditions.md +17 -13
  97. package/skills/docs/pr-lifecycle-contract.md +45 -36
  98. package/skills/docs/public-dev-loop-contract.md +61 -44
  99. package/skills/docs/required-rules.json +165 -0
  100. package/skills/docs/retrospective-checkpoint-contract.md +25 -9
  101. package/skills/docs/spike-mode-contract.md +14 -7
  102. package/skills/docs/stop-conditions.md +30 -15
  103. package/skills/docs/tracker-first-loop-state.md +13 -8
  104. package/skills/docs/validation-policy.md +4 -5
  105. package/skills/local-implementation/SKILL.md +49 -143
  106. package/skills/loop-grill/SKILL.md +38 -17
  107. package/scripts/docs/validate-no-duplicate-rules.mjs +0 -250
@@ -25,7 +25,7 @@ Required installed runtime contract docs are shared bundled copies under `../doc
25
25
 
26
26
  > Under the Claude Code harness the dev-loop runs as a single agent: run these steps directly — no read-only boundary and no separate async-subagent dispatch. See [Main Agent Contract](../docs/main-agent-contract.md).
27
27
 
28
- Resolve authoritative state via the startup resolver (`npx dev-loops@0.7.1 loop startup --issue <n>` for issues, `npx dev-loops@0.7.1 loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops@0.7.1 loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
28
+ Resolve authoritative state via the startup resolver (`npx dev-loops@0.8.0 loop startup --issue <n>` for issues, `npx dev-loops@0.8.0 loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops@0.8.0 loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
29
29
 
30
30
  **Retrospective checkpoint gate:** the resolver reads `.pi/dev-loop-retrospective-checkpoint.json` and injects the state. When the checkpoint is `missing` and the repo config `workflow.requireRetrospective` (set via `.devloops` at repo root) is `true`, the resolver returns `needs_reconcile`. Complete or explicitly skip the retrospective before starting.
31
31
 
@@ -99,17 +99,17 @@ When `@dev-loops/core` is available again, switch back to the full helper. The f
99
99
 
100
100
  ## Read-only info shortcut
101
101
 
102
- Info/handoff requests can be served directly via `npx dev-loops@0.7.1 loop info` (read-only; no full dev-loop run required):
103
- - `npx dev-loops@0.7.1 loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
104
- - `npx dev-loops@0.7.1 loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
105
- - `npx dev-loops@0.7.1 loop info --issue <n> --json` — machine-readable JSON output
102
+ Info/handoff requests can be served directly via `npx dev-loops@0.8.0 loop info` (read-only; no full dev-loop run required):
103
+ - `npx dev-loops@0.8.0 loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
104
+ - `npx dev-loops@0.8.0 loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
105
+ - `npx dev-loops@0.8.0 loop info --issue <n> --json` — machine-readable JSON output
106
106
 
107
107
  ## Reading tool output (token-economical convention)
108
108
 
109
109
  When you need a fact from a dev-loops JSON-emitting script, climb this ladder and stop at the first rung that answers the question — never read more output than you need:
110
110
 
111
111
  1. **Prefer the dev-loops subcommand / concise mode.** Use `loop info` or a script's `--concise`/`--summary` mode (e.g. `run-watch-cycle.mjs --concise`, `probe-copilot-review.mjs --concise`) for a human-readable digest. The concise modes surface loop state, Copilot round count, unresolved/actionable thread counts, round-cap-clean eligibility, CI status, next action, and the current round's new Copilot comment bodies.
112
- 2. **`--silent` / `-s` for a yes/no check.** Reads ZERO output: `… --jq '<predicate>' --silent; echo $?` exits `0` for true / `1` for false. Without `--jq`, `--silent` maps the script's success (`ok:true`) to exit `0`, failure to `1`. Example: `probe-copilot-review.mjs --repo o/r --pr N --jq '.status=="idle"' -s`.
112
+ 2. **`--silent` / `-s` for a yes/no check.** Reads ZERO output: `… --jq '<predicate>' --silent; echo $?` exits `0` for true / `1` for false. Without `--jq`, `--silent` maps the script's success (`ok:true`) to exit `0`, failure to `1` — unless the tool documents a stricter `--silent` contract in its own usage text (e.g. `request-copilot-review.mjs` exits `0` only for `status: "requested"`). Example: `probe-copilot-review.mjs --repo o/r --pr N --jq '.status=="idle"' -s`.
113
113
  3. **`--jq <filter>` to extract a single field.** `--jq`/`--silent` are a BASE-CLI GUARANTEE across every operator-facing JSON-result dev-loops command — each accepts a gh-style `--jq` filter (jq subset: field access, `.[]`/`.[N]`, `|`, `select(...)`, `==`/`!=`/`<`/`<=`/`>`/`>=`, `length`, `keys`), not just a named subset. (A few scripts are out of scope — e.g. build/smoke tooling, dashboard servers, dormant/unwired adapters, and scripts that write JSON to a file rather than stdout — each carried with its reason in the contract test's exclusion list.) It prints only the filtered value. An invalid filter fails closed (stderr + exit `2`), distinct from a clean predicate-false (silent exit `1`). This is enforced by a contract test (`test/contracts/jq-output-base-guarantee-contract.test.mjs`) that fails the build if a new JSON-emitting command ships without wiring the shared `scripts/lib/jq-output.mjs` emit path.
114
114
  4. **Use a dev-loops wrapper for `gh` reads — never an agent-level raw `gh`.** A raw `gh` call is a recorded advisory retro violation (issue #1077: reported, never blocking). If no script covers the read you need, treat it as a tooling gap: file/build a thin wrapper (reuse `scripts/lib/jq-output.mjs`, like the ones below), don't shell out. The reads/edits that already have wrappers:
115
115
  - CI run-log tail (a failing PR's job log) → `scripts/github/fetch-ci-logs.mjs --repo <o/r> --pr <n> [--failed-only] [--tail <n>]`, **never raw `gh run view --log`/`--log-failed`**. (`probe-ci-status.mjs` names the failed checks; this returns the LOG.)
@@ -130,9 +130,9 @@ When you need a fact from a dev-loops JSON-emitting script, climb this ladder an
130
130
 
131
131
  **Inline-first rule:** Prefer inline commands over nested async delegation when managing a single PR. Use nested delegation only for parallel fan-out or when the parent needs to continue other work.
132
132
 
133
- **Bounded async task contract:** Break work into discrete tasks with clear inputs, explicit outputs, bounded scope. No shell polling — use `run-watch-cycle.mjs` or `gh run watch`. For fan-out reviewer waits (gate sub-loops or any Agent-tool fan-out), await completion via the harness completion notification or the reviewer's findings artifact at its deterministic path, then join via `consolidateFanin` — never tail/parse a subagent transcript, never `node -e`/`python3`-parse tool JSON, never `sleep`-poll (see the [fan-in wait anti-pattern](../docs/anti-patterns.md) and [Checkpoint Review Chain Contract](../../docs/gate-review-sub-loop-contract.md) Phase 2).
133
+ **Bounded async task contract:** Break work into discrete tasks with clear inputs, explicit outputs, bounded scope. No shell polling — use `run-watch-cycle.mjs` or `gh run watch`. Fan-out reviewer waits (gate sub-loops or any Agent-tool fan-out) follow `ANTIPATTERN-FANIN-WAIT` in [Anti-patterns](../docs/anti-patterns.md): await completion via the harness notification or the reviewer's findings artifact at its deterministic path and join via `consolidateFanin` — never transcript-tail, `node -e`/`python3`-parse tool JSON, or `sleep`-poll.
134
134
 
135
- **Round-cap budget check (enforced):** After every watch cycle, fix pass, or reply-resolve, check whether completed Copilot review rounds have reached the maximum (default: 5). Stop re-requesting Copilot review when the limit is reached **within that review cycle**. Exception: if the loop already converged and then significant post-convergence changes land on a newer head (new/changed product or test logic, not doc/message/comment-only edits), open a new Copilot review cycle and re-request review when regular rounds are already > 0, even if the previous cycle hit the cap. Read these gate-cadence facts via the token-economical convention above (`run-watch-cycle.mjs --concise`, or `--jq`/`--silent` for a single field/predicate) — never `| python3` or `node -e`.
135
+ **Round-cap budget check (enforced):** After every watch cycle, fix pass, or reply-resolve, check whether completed Copilot review rounds have reached the resolved round cap (`refinement.maxCopilotRounds`, default 5; light-dispatched PRs resolve the lower `resolveEffectiveCopilotRoundCap`, default 1 — owned by `COPILOT-FOLLOWUP-ROUND-CAP`). Stop re-requesting Copilot review when the limit is reached **within that review cycle**. Exception: the post-convergence new-cycle re-request carve-out (a converged loop that later takes significant post-convergence changes on a newer head opens a new cycle even if the previous one hit the cap) is owned by `COPILOT-FOLLOWUP-ROUND-CAP`. Read these gate-cadence facts via the token-economical convention above (`run-watch-cycle.mjs --concise`, or `--jq`/`--silent` for a single field/predicate) — never `| python3` or `node -e`.
136
136
 
137
137
  ## Shorthand issue-based auto trigger contract
138
138
 
@@ -1,12 +1,21 @@
1
1
  # Acceptance Criteria Verification
2
2
 
3
- Extracted procedure for verifying issue acceptance criteria during the `pre_approval_gate`. Referenced from [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md#pre-approval-gate-contract).
3
+ Canonical owner for the acceptance-criteria verification procedure run during the `pre_approval_gate`. Referenced from [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md#pre-approval-gate-contract).
4
4
 
5
5
  ## Procedure
6
6
 
7
- Before posting the `pre_approval_gate` comment, verify every acceptance criteria checklist item in the issue linked to this PR, and reflect the verified items back into both the linked issue body and the PR body (exception: under the lightweight path — `specSource: pr_body` — the step 5 issue-body mirroring is skipped, since the PR body, not the issue body, is the canonical spec surface; see the lightweight fork below):
7
+ <!-- rule: ACCEPT-CRITERIA-VERIFY-AND-REFLECT -->
8
+ `ACCEPT-CRITERIA-VERIFY-AND-REFLECT`: Before posting the `pre_approval_gate` comment, the agent MUST verify every acceptance criteria checklist item in the issue linked to this PR, and MUST reflect the verified items back into both the linked issue body and the PR body (exception: when the linked issue body is not the spec-of-record — under a lightweight session (tracker-backed or issue-less; the PR body is the spec) or a plan-file promotion session (the committed plan doc the PR body points to is the spec) — the step 5 issue-body mirroring is skipped; see the fork below):
8
9
 
9
- > **Lightweight (PR-body-as-spec) fork:** when the session is lightweight — the resolver output / handoff envelope carries `specSource: pr_body` (`canonicalSpecSource: pr_body`) — a linked issue still exists (`--lightweight` runs on the `--issue` path, so keep the `Closes #N` linkage and all issue-level tracking), but the **PR body**, not the issue body, is the canonical spec-of-record read for AC/DoD. In that case, still resolve the linked issue for tracking, but read the AC/DoD/invariants from the PR body rather than the issue body (skip step 2's issue-body read for spec purposes). Run `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <pr-number> --expected-issue <issue-number>` (reuses `@dev-loops/core/loop/issue-refinement-artifact`) to confirm the body carries the required invariants — it fails closed with a per-section `missing_*` reason if any is absent, in which case post the gate comment with verdict `blocked`. The PR body MUST carry the `Closes #N` linkage (or GitHub's other closing-keyword forms): `validate-pr-body-spec` fails closed with `missing_closing_issue_reference` without it, and with `closes_wrong_issue` when `--expected-issue` doesn't match. Then continue at step 3, extracting the **Acceptance criteria** checklist items from the PR body itself; step 5's issue-body AC-tick update is unnecessary for lightweight since the issue body is not the spec surface (the PR body is ticked in step 6 as usual). The default issue-backed procedure below is unchanged.
10
+ > **Non-tracker spec-source fork:** when the linked issue body is not the spec-of-record, the fork covers three arms lightweight (the PR body itself is the spec; splits on whether a backing issue exists) and plan-file promotion (a distinct local-planning origin whose committed plan doc, pointed to from the PR body, is the spec). Handle each arm as below.
11
+ >
12
+ > **Tracker-backed lightweight** (`--issue --lightweight`): a linked issue exists, so keep the `Closes #N` linkage and all issue-level tracking, but the **PR body**, not the issue body, is the canonical spec-of-record read for AC/DoD. Still resolve the linked issue for tracking, but read the AC/DoD/invariants from the PR body rather than the issue body (skip step 2's issue-body read for spec purposes). Run `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <pr-number> --expected-issue <issue-number>` (reuses `@dev-loops/core/loop/issue-refinement-artifact`) to confirm the body carries the required invariants — it fails closed with a per-section `missing_*` reason if any is absent, in which case post the gate comment with verdict `blocked`. The PR body MUST carry the `Closes #N` linkage (or GitHub's other closing-keyword forms): `validate-pr-body-spec` fails closed with `missing_closing_issue_reference` without it, and with `closes_wrong_issue` when `--expected-issue` doesn't match. Then continue at step 3, extracting the **Acceptance criteria** checklist items from the PR body itself; step 5's issue-body AC-tick update is unnecessary since the issue body is not the spec surface (the PR body is ticked in step 6 as usual).
13
+ >
14
+ > **Issue-less lightweight** (`--lightweight` alone): no linked issue exists — by design, not a gap. There is nothing to resolve at step 1 and nothing to read or mirror at steps 2 and 5; skip all three. Read AC/DoD/invariants directly from the PR body and run `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <pr-number> --no-issue`, which fails closed with a per-section `missing_*` reason for any absent invariant and with `unexpected_closing_issue_reference` if the body carries a closing reference to an issue that doesn't back it (a closing reference MUST NOT be present). On any validation failure, post the gate comment with verdict `blocked`. Step 1's "zero candidates → blocked" rule is scoped to tracker-backed sessions; for a deliberately issue-less session, zero closing references is the expected state, not a blocked condition. Continue at step 3 against the PR body, then step 4 and 6 as usual.
15
+ >
16
+ > **Plan-file promotion (P4) — a distinct local-planning origin, not a lightweight sub-arm:** no linked issue exists — similar to the issue-less lightweight arm, there is nothing to resolve at step 1 and nothing to read or mirror at steps 2 and 5; skip all three. AC/DoD live in the PR body, copied from the committed plan doc that is the spec-of-record; read them from the PR body as usual.
17
+ >
18
+ > The default issue-backed procedure below is unchanged.
10
19
 
11
20
  1. **Resolve the linked issue number deterministically:** use `gh pr view <pr-number> --repo <owner/name> --json closingIssuesReferences,body` and apply this decision tree: if there is exactly one closing issue reference, use it; else if there is exactly one PR-body `Closes #N` / `Fixes #N` pattern, use it; otherwise (zero or multiple candidates), post the gate comment with verdict `blocked` (gate cannot complete deterministically) rather than guessing.
12
21
 
@@ -16,7 +25,7 @@ Before posting the `pre_approval_gate` comment, verify every acceptance criteria
16
25
 
17
26
  4. **Verify each AC item** against the proposed changes on the current PR head.
18
27
 
19
- 5. **Update the issue body once:** compute the fully-updated issue body by replacing each verified item's `- [ ]` with `- [x]`, write it to a temporary file, and perform a single `gh issue edit <issue-number> --body-file <tmp-file> --repo <owner/name>`. Do not issue one edit per item; prefer `--body-file` over inline `--body` to avoid shell quoting/escaping hazards.
28
+ 5. **Update the issue body once:** compute the fully-updated issue body by replacing each verified item's `- [ ]` with `- [x]`, write it to a temporary file, and perform a single `node scripts/github/edit-issue.mjs --repo <owner/name> --issue <issue-number> --body-file <tmp-file>`. Do not issue one edit per item; prefer `--body-file` over inline `--body` to avoid shell quoting/escaping hazards.
20
29
 
21
30
  6. **Tick the PR body once:** after the verification is clean, flip each verified item's `- [ ]` to `- [x]` in the PR body via a single `gh pr edit --body-file` update, so the merged PR shows checked AC/DoD. Run `node scripts/github/tick-verified-checkboxes.mjs --repo <owner/name> --pr <pr-number> --verified <exact label>...` (one edit; exact-label match; only items actually verified are ticked; unverified or deferred items stay `- [ ]`; fail closed). This runs automatically as part of the `pre_approval_gate`, not by memory.
22
31
 
@@ -6,17 +6,18 @@ Canonical owner for anti-pattern guidance across all workflow families.
6
6
 
7
7
  1. **Skipping fan-out/fan-in for non-trivial changes**: Do not implement directly without refinement when scope exceeds light-mode thresholds.
8
8
  2. **Routing review-only work through local_implementation**: Review-only comparison, synthesis, or consolidation must use `refiner` agent, not `dev-loop` + `local_implementation`.
9
- 3. **Thin placeholder PR descriptions**: Always include change summary, scope/context, acceptance criteria, definition of done, non-goals, and `Closes #N`.
9
+ 3. **Thin placeholder PR descriptions**: Always include change summary, scope/context, acceptance criteria, definition of done, non-goals, and `Closes #N` (tracker-backed; issue-less lightweight and plan-file promotion PRs instead follow [merge precondition 7](merge-preconditions.md#required-before-merge)).
10
10
  4. **Merging directly to main without PR**: Use PR-based remote loop when practical.
11
- 5. **Duplicate worktree paths**: Check `git worktree list` before creating new worktrees; reuse existing matching worktrees.
12
- 6. **Main-checkout mutation**: Reserve main checkout for inspection/control; use `tmp/worktrees/` paths for mutation work.
11
+ 5. **Duplicate worktree paths**: See `WORKTREE-DEDUPE` in [Worktree usage guidance](../../docs/worktree-guidance.md#coordination-and-collision-checks).
12
+ 6. **Main-checkout mutation**: See `WORKTREE-DEFAULT-USE` in [Worktree usage guidance](../../docs/worktree-guidance.md#default-rule-use-a-worktree-for-mutating-local-work).
13
13
  7. **Spelunking tooling internals instead of using the public surface**: Do not read installed package internals, scan tooling source, or run ad-hoc scripts to understand a tool's behavior. Use the CLI, its `--help` subcommands, and `skills/docs/`. Read tool source only when the task is to inspect or change that tool, or when a concrete failure path is inside it and no public CLI/docs path exists. Once a failure is concrete, search changed files for the exact pattern — don't run duplicate broad searches.
14
14
  8. **Hand-editing the queue file when a board is configured**: When a GitHub Projects board is configured (`queue.projectNumber`/`queue.boardTitle` in `.devloops`), the board is the authoritative queue MEMBERSHIP and ordering source — not just status. Add work via the board (`dev-loops queue add ... --next-up` to land directly in the normative `Next Up` pickup queue; default lands in Backlog, which is unprioritized intake and never auto-picked), not by hand-editing `.pi/dev-loop-queue.json`; the queue runner reconciles board `Next Up` items into queue entries before each run. See the operator guides under `docs/` (queue board usage/setup) for the workflow.
15
- 9. **Hand-rolling a fan-in wait by polling a subagent transcript or parsing tool JSON with `node -e`/`python3`**: When awaiting fan-out reviewers (gate `draft_gate`/`pre_approval_gate` sub-loops, or any Agent-tool fan-out), NEVER tail/parse another agent's JSONL transcript, NEVER use `node -e`/`python3` to parse tool/subagent JSON, and NEVER `sleep`-poll in a shell loop for completion. These improvisations breach the [internal-tooling-only rule (issue #982)](retrospective-checkpoint-contract.md#internal-tooling-only-rule-issue-982--now-advisory) (no `node -e`/`python3` parsing of tool JSON) and the no-shell-polling rule, and a single improvised wait adds a non-empty entry to the advisory `retrospectiveFindings.rawCallViolations` array (issue #1077: reported to the conductor, **never blocking**). **Sanctioned wait:** rely on the harness completion notification, or read each reviewer's findings artifact at its deterministic output path (see [Checkpoint Review Chain Contract](../../docs/gate-review-sub-loop-contract.md) Phase 2), then join via `consolidateFanin` from `@dev-loops/core/loop/gate-fanin` (Phase 3). See also the dev-loop SKILL "Bounded async task contract".
15
+ 9. <!-- rule: ANTIPATTERN-FANIN-WAIT --> `ANTIPATTERN-FANIN-WAIT` — **Hand-rolling a fan-in wait by polling a subagent transcript or parsing tool JSON with `node -e`/`python3`**: When awaiting fan-out reviewers (gate `draft_gate`/`pre_approval_gate` sub-loops, or any Agent-tool fan-out), NEVER tail/parse another agent's JSONL transcript, NEVER use `node -e`/`python3` to parse tool/subagent JSON, and NEVER `sleep`-poll in a shell loop for completion. These improvisations breach the [internal-tooling-only rule (issue #982)](retrospective-checkpoint-contract.md#internal-tooling-only-rule-issue-982--now-advisory) (no `node -e`/`python3` parsing of tool JSON) and the no-shell-polling rule, and a single improvised wait adds a non-empty entry to the advisory `retrospectiveFindings.rawCallViolations` array (issue #1077: reported to the conductor, **never blocking**). **Sanctioned wait:** rely on the harness completion notification, or read each reviewer's findings artifact at its deterministic output path (see [Checkpoint Review Chain Contract](../../docs/gate-review-sub-loop-contract.md) Phase 2), then join via `consolidateFanin` from `@dev-loops/core/loop/gate-fanin` (Phase 3). See also the dev-loop SKILL "Bounded async task contract".
16
+ 10. **Ad hoc `gh api graphql` review-thread queries or `gh pr checks`/shell-pipe CI polling**: Do not hand-write a GraphQL query to enumerate review threads, and do not wrap `gh pr checks` in an `awk`/`grep`/`until`-based shell loop to wait for CI. Enumerate threads (with the ids `reply-resolve-review-thread.mjs` needs) via `scripts/github/list-review-threads.mjs`; block on current-head CI settling via `scripts/github/wait-pr-checks.mjs` (or `dev-loops loop watch-ci` for the JSON-status variant); read the aggregate loop/CI/thread state via `detect-copilot-loop-state.mjs` instead of re-deriving it from raw `gh` output.
16
17
 
17
18
  ## Light mode exception
18
19
 
19
- Small scoped changes (≤3 files, ≤200 lines) may skip fan-out/fan-in but must still run validation and a single review pass. See [Local Implementation](../local-implementation/SKILL.md) for details.
20
+ Small scoped changes under the configured `localImplementation.lightMode` thresholds (owned by `LOCAL-LIGHT-MODE-CONFIG-SURFACE` in [Local Implementation](../local-implementation/SKILL.md)) may skip fan-out/fan-in but must still run validation and a single review pass.
20
21
 
21
22
  ## Cross-references
22
23
 
@@ -1,16 +1,19 @@
1
1
  # Artifact authority contract
2
2
 
3
- This document is the canonical authority for the artifact-selection model: whether a work item originates from a GitHub issue (tracker-first) or from a persisted markdown plan file (local-planning).
3
+ Canonical owner for the artifact-selection model: whether a work item originates from a GitHub issue (tracker-first), a persisted markdown plan file (local-planning), or the sanctioned lightweight PR-body-as-spec path.
4
4
 
5
5
  This canonical owner lives in the shipped `skills/docs/` surface because installed skill/runtime consumers reliably own the skills subtree. In installed layouts, read the same contract via [Artifact Authority Contract](../docs/artifact-authority-contract.md) from the installed skill directory.
6
6
 
7
7
  Other repo docs may summarize or link this contract, but they should not redefine it.
8
8
 
9
- ## Two-tier model
9
+ ## Three-origin model
10
10
 
11
- 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
+ <!-- The rule ID predates the three-origin wording below (it was written when the
13
+ model was two-tier); retained as-is for ID stability, not renamed. -->
14
+ dev-loops supports three mutually exclusive artifact authority origins. Every work item MUST originate from exactly one authoritative artifact: a GitHub issue, a persisted markdown plan file, or — on the sanctioned lightweight path — the PR description itself as the spec-of-record (no committed plan artifact). Work MUST NOT originate from a PR (other than the sanctioned lightweight PR-body-as-spec path) or a direct local change unless explicitly requested.
12
15
 
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 local-first code-level default in `BUILT_IN_DEFAULTS`.
16
+ The shipped extension default selects local-planning; see [Shipped default posture](#shipped-default-posture) below. The mode names that follow describe the origins; "default" in their headings refers to the local-first code-level default in `BUILT_IN_DEFAULTS`.
14
17
 
15
18
  ### Tracker-first
16
19
 
@@ -19,7 +22,7 @@ The shipped extension default selects local-planning; see [Shipped default postu
19
22
  Artifacts:
20
23
  - **Planning artifact:** GitHub issue (title, body, labels, assignees, acceptance criteria)
21
24
  - **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
25
+ - <!-- 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
26
 
24
27
  Key contract:
25
28
  - GitHub issue state is authoritative — not local notes or chat context
@@ -40,7 +43,7 @@ Artifacts:
40
43
  Key contract:
41
44
  - The markdown plan file is the canonical spec — not a duplicate of a tracker issue
42
45
  - GitHub issues may still be used for tracking or linking, but the plan file is authoritative for scope and acceptance criteria
43
- - 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"
46
+ - 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"
44
47
 
45
48
  ### Lightweight (PR-body-as-spec)
46
49
 
@@ -51,11 +54,10 @@ Artifacts:
51
54
  - **No committed plan doc:** no `docs/phases/*.md` is created for the session
52
55
 
53
56
  Key contract:
54
- - 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**. `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 `missing_*` reason per absent invariant.
55
- - The PR body MUST also carry the `Closes #N` linkage (GitHub's other closing keywords count too); `validate-pr-body-spec` fails closed with `missing_closing_issue_reference` without it, and with `closes_wrong_issue` when an `--expected-issue` is given and doesn't match — the lightweight path's issue-tracking state must never silently diverge from PR state (issue #1181).
57
+ - <!-- 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
58
  - 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
59
  - 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
- - `--lightweight` is 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) and only composes with `--issue`.
60
+ - <!-- 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
61
  - 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).
60
62
 
61
63
  ### Mode selection table
@@ -64,7 +66,7 @@ Key contract:
64
66
  |---|---|---|---|
65
67
  | Tracker-first | GitHub issue | Yes | `strategy.default: github-first` |
66
68
  | Local-planning (shipped default) | Markdown plan file | No | `strategy.default: local-first` |
67
- | Lightweight (PR-body-as-spec) | GitHub PR description | Yes (`--issue`) | modifier: `--lightweight` (`canonicalSpecSource: pr_body`) |
69
+ | 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`) |
68
70
 
69
71
  `inputSource.default` further disambiguates local-first startup:
70
72
  | inputSource | Meaning |
@@ -106,13 +108,14 @@ The effective default for a consumer comes from the config-merge layering in `pa
106
108
 
107
109
  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`.
108
110
 
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: `.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.*`).
111
+ 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).
110
112
 
111
113
  ### Explicit non-knobs
112
114
 
113
- These are not valid artifact authority mode selectors:
114
- - `strategy.default: copilot` not a valid mode; the enum accepts only `github-first` or `local-first` (`packages/core/src/config/config.mjs`)
115
- - Free-form string values fail closed
115
+ <!-- rule: ARTIFACT-STRATEGY-ENUM-FAIL-CLOSED -->
116
+ `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:
117
+ - `strategy.default: copilot`not a valid mode
118
+ - Free-form string values — MUST fail closed
116
119
  - Omitting `strategy.default` from every layer — resolves to `local-first` from `BUILT_IN_DEFAULTS`
117
120
 
118
121
  ## Local-first plan-file flow end to end
@@ -4,7 +4,8 @@ Canonical owner for agent confirmation / authorization rules across all workflow
4
4
 
5
5
  ## Core rule
6
6
 
7
- Before any state-changing action, get explicit confirmation unless the latest user message already clearly authorizes that exact action.
7
+ <!-- rule: CONFIRM-CORE-EXPLICIT -->
8
+ `CONFIRM-CORE-EXPLICIT`: Before any state-changing action, the agent MUST obtain explicit confirmation unless the latest user message already clearly authorizes that exact action.
8
9
 
9
10
  ## What counts as confirmation
10
11
 
@@ -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
@@ -1,6 +1,6 @@
1
1
  # Copilot loop operations
2
2
 
3
- This document is the canonical operational reference for the deterministic Copilot PR follow-up state machine used by the routed `copilot_pr_followup`, `wait_watch`, `reviewer_fixer`, and `final_approval` paths behind `dev-loop`.
3
+ Canonical owner for operating the deterministic Copilot PR follow-up state machine the operational reference for the routed `copilot_pr_followup`, `wait_watch`, `reviewer_fixer`, and `final_approval` paths behind `dev-loop`. The machine's states and transitions are defined by [Copilot Loop State Graph](../../docs/copilot-loop-state-graph.md).
4
4
 
5
5
  Use it together with:
6
6
  - [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md)
@@ -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
@@ -166,11 +162,14 @@ When you do hand work to Copilot:
166
162
 
167
163
  ## PR description contract
168
164
 
165
+ This section governs tracker-backed (Copilot-handoff) PRs; issue-less/plan-file-backed PRs are governed by merge precondition 7 in merge-preconditions.md instead.
166
+
169
167
  Follow the PR description contract (see [Agent Instructions](../../AGENTS.md) if present; otherwise use the structure below): detailed structured descriptions, not thin placeholders. At minimum include change summary, scope/context, explicit acceptance criteria, explicit definition of done, and explicit non-goals, and `Closes #N` (or `Fixes #N`) for the linked issue so GitHub auto-closes it on merge.
170
168
 
171
169
  Checkbox rule: acceptance criteria, definition-of-done items, and any task list must be rendered as real GitHub markdown checkboxes inside list items (`- [ ]` / `- [x]`, also `* [ ]` / `* [x]`). Do not wrap checkbox markers (e.g. `[x]`) in backticks. Do not place checkbox markers inside table cells — task lists are not interactive there even with a leading `- `.
172
170
 
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.
171
+ <!-- rule: OPS-DRAFT-FIRST-PR -->
172
+ `OPS-DRAFT-FIRST-PR`: New PRs in this workflow MUST always be opened as **draft** PRs — mechanically enforced by the `create-pr.mjs` wrapper below, which is unconditionally draft-only and rejects `--ready`. Agents MUST NOT create a fresh PR directly in ready-for-review state; `gh pr ready` (via the `ready-for-review.mjs` wrapper) is the only path out of draft, gated on clean draft-gate evidence. This rule owns the draft-first mechanism itself; [TRACKER-PROJECTION-REQUIRED-METADATA](./tracker-first-loop-state.md#31-required-pr-metadata) owns the tracker-facing "PR MUST start as a draft" metadata expectation. The draft gate inspection is a real workflow boundary, so a new PR must exist in draft before `gh pr ready` is even eligible.
174
173
 
175
174
  Only use `node <resolved-skill-scripts>/github/create-pr.mjs` when authoritative issue↔PR resolution says there is no already-open linked PR. If a PR already exists, reuse/update that canonical PR instead of opening another one. This wrapper preserves the underlying `gh pr create` output contract while enforcing draft-first mechanically and self-assigning the PR by default (`--assignee @me`).
176
175
 
@@ -230,4 +229,11 @@ Helper-owned sleep inside `run-watch-cycle.mjs`, `probe-copilot-review.mjs`, or
230
229
 
231
230
  A watcher sleeping between polls is expected behavior, not a blocker.
232
231
 
232
+ ## Hard rule: no inline interpreters
233
+
234
+ <!-- rule: OPS-NO-INLINE-INTERPRETER -->
235
+ `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.
236
+
237
+ 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).
238
+
233
239
  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.
@@ -32,13 +32,13 @@ Definitions:
32
32
 
33
33
  ## Claude-Code-path test inventory
34
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:
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
36
 
37
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
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
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
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.
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
42
 
43
43
  ## How to add coverage
44
44
 
@@ -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
  |---|---|