dev-loops 0.1.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 (156) hide show
  1. package/.pi/dev-loop/defaults.yaml +477 -0
  2. package/AGENTS.md +25 -0
  3. package/CHANGELOG.md +18 -0
  4. package/LICENSE +21 -0
  5. package/README.md +178 -0
  6. package/agents/dev-loop.agent.md +82 -0
  7. package/agents/developer.agent.md +37 -0
  8. package/agents/docs.agent.md +33 -0
  9. package/agents/fixer.agent.md +53 -0
  10. package/agents/quality.agent.md +28 -0
  11. package/agents/refiner.agent.md +87 -0
  12. package/agents/review.agent.md +64 -0
  13. package/cli/index.mjs +424 -0
  14. package/extension/README.md +233 -0
  15. package/extension/checks.ts +94 -0
  16. package/extension/index.ts +131 -0
  17. package/extension/post-merge-update.ts +512 -0
  18. package/extension/presentation.ts +107 -0
  19. package/lib/dev-loops-core.mjs +284 -0
  20. package/package.json +103 -0
  21. package/scripts/README.md +1007 -0
  22. package/scripts/_cli-primitives.mjs +10 -0
  23. package/scripts/_core-helpers.mjs +30 -0
  24. package/scripts/docs/validate-links.mjs +567 -0
  25. package/scripts/docs/validate-no-duplicate-rules.mjs +250 -0
  26. package/scripts/github/_review-thread-mutations.mjs +214 -0
  27. package/scripts/github/capture-review-threads.mjs +180 -0
  28. package/scripts/github/create-draft-pr.mjs +108 -0
  29. package/scripts/github/detect-checkpoint-evidence.mjs +393 -0
  30. package/scripts/github/detect-linked-issue-pr.mjs +331 -0
  31. package/scripts/github/manage-sub-issues.mjs +394 -0
  32. package/scripts/github/probe-copilot-review.mjs +323 -0
  33. package/scripts/github/ready-for-review.mjs +93 -0
  34. package/scripts/github/reconcile-draft-gate.mjs +328 -0
  35. package/scripts/github/reply-resolve-review-thread.mjs +42 -0
  36. package/scripts/github/reply-resolve-review-threads.mjs +329 -0
  37. package/scripts/github/request-copilot-review.mjs +551 -0
  38. package/scripts/github/resolve-tracker-local-spec.mjs +205 -0
  39. package/scripts/github/stage-reviewer-draft.mjs +191 -0
  40. package/scripts/github/upsert-checkpoint-verdict.mjs +694 -0
  41. package/scripts/github/verify-fresh-review-context.mjs +125 -0
  42. package/scripts/github/write-gate-findings-log.mjs +212 -0
  43. package/scripts/loop/_checkpoint-io.mjs +55 -0
  44. package/scripts/loop/_checkpoint-paths.mjs +28 -0
  45. package/scripts/loop/_handoff-contract.mjs +230 -0
  46. package/scripts/loop/_inspect-run-viewer-adapter.mjs +345 -0
  47. package/scripts/loop/_loop-evidence.mjs +32 -0
  48. package/scripts/loop/_pr-runner-coordination.mjs +611 -0
  49. package/scripts/loop/_stale-runner-detection.mjs +145 -0
  50. package/scripts/loop/_steering-state-file.mjs +134 -0
  51. package/scripts/loop/build-handoff-envelope.mjs +181 -0
  52. package/scripts/loop/checkpoint-contract.mjs +49 -0
  53. package/scripts/loop/conductor-monitor.mjs +1850 -0
  54. package/scripts/loop/conductor.mjs +214 -0
  55. package/scripts/loop/copilot-pr-handoff.mjs +493 -0
  56. package/scripts/loop/debt-remediate.mjs +304 -0
  57. package/scripts/loop/detect-change-scope.mjs +102 -0
  58. package/scripts/loop/detect-copilot-loop-state.mjs +454 -0
  59. package/scripts/loop/detect-copilot-session-activity.mjs +186 -0
  60. package/scripts/loop/detect-initial-copilot-pr-state.mjs +318 -0
  61. package/scripts/loop/detect-internal-only-pr.mjs +270 -0
  62. package/scripts/loop/detect-issue-refinement-artifact.mjs +163 -0
  63. package/scripts/loop/detect-pr-gate-coordination-state.mjs +509 -0
  64. package/scripts/loop/detect-reviewer-loop-state.mjs +231 -0
  65. package/scripts/loop/detect-stale-runner.mjs +250 -0
  66. package/scripts/loop/detect-tracker-first-loop-state.mjs +76 -0
  67. package/scripts/loop/detect-tracker-pr-state.mjs +102 -0
  68. package/scripts/loop/info.mjs +267 -0
  69. package/scripts/loop/inspect-run-viewer/cli.mjs +117 -0
  70. package/scripts/loop/inspect-run-viewer/constants.mjs +80 -0
  71. package/scripts/loop/inspect-run-viewer/graph.mjs +757 -0
  72. package/scripts/loop/inspect-run-viewer/handoff-envelope-renderer.mjs +398 -0
  73. package/scripts/loop/inspect-run-viewer/inbox.mjs +308 -0
  74. package/scripts/loop/inspect-run-viewer/managed-instance.mjs +750 -0
  75. package/scripts/loop/inspect-run-viewer/rendering.mjs +411 -0
  76. package/scripts/loop/inspect-run-viewer/server.mjs +638 -0
  77. package/scripts/loop/inspect-run-viewer/shared.mjs +103 -0
  78. package/scripts/loop/inspect-run-viewer/status.mjs +715 -0
  79. package/scripts/loop/inspect-run-viewer-ci-changes.mjs +77 -0
  80. package/scripts/loop/inspect-run-viewer.mjs +82 -0
  81. package/scripts/loop/inspect-run.mjs +382 -0
  82. package/scripts/loop/outer-loop.mjs +419 -0
  83. package/scripts/loop/pr-runner-coordination.mjs +143 -0
  84. package/scripts/loop/pre-commit-branch-guard.mjs +68 -0
  85. package/scripts/loop/pre-flight-gate.mjs +236 -0
  86. package/scripts/loop/pre-pr-ready-gate.mjs +183 -0
  87. package/scripts/loop/pre-push-main-guard.mjs +103 -0
  88. package/scripts/loop/pre-write-remote-freshness-guard.mjs +32 -0
  89. package/scripts/loop/print-gates.mjs +42 -0
  90. package/scripts/loop/resolve-dev-loop-startup.mjs +533 -0
  91. package/scripts/loop/run-conductor-cycle.mjs +322 -0
  92. package/scripts/loop/run-queue.mjs +124 -0
  93. package/scripts/loop/run-refinement-audit.mjs +513 -0
  94. package/scripts/loop/run-watch-cycle.mjs +358 -0
  95. package/scripts/loop/steer-loop.mjs +841 -0
  96. package/scripts/loop/ui-designer-review-contract.mjs +76 -0
  97. package/scripts/loop/watch-initial-copilot-pr.mjs +253 -0
  98. package/scripts/projects/add-queue-item.mjs +528 -0
  99. package/scripts/projects/ensure-queue-board.mjs +837 -0
  100. package/scripts/projects/list-queue-items.mjs +489 -0
  101. package/scripts/projects/move-queue-item.mjs +549 -0
  102. package/scripts/projects/reorder-queue-item.mjs +518 -0
  103. package/scripts/refine/_refine-helpers.mjs +258 -0
  104. package/scripts/refine/prose-linkage-detector.mjs +92 -0
  105. package/scripts/refine/refinement-completeness-checker.mjs +88 -0
  106. package/scripts/refine/scope-boundary-cross-checker.mjs +163 -0
  107. package/scripts/refine/tree-integrity-validator.mjs +211 -0
  108. package/scripts/refine/verify.mjs +178 -0
  109. package/scripts/repo-wiki-local.mjs +156 -0
  110. package/scripts/repo-wiki.mjs +119 -0
  111. package/skills/copilot-pr-followup/SKILL.md +380 -0
  112. package/skills/dev-loop/SKILL.md +141 -0
  113. package/skills/dev-loop/scripts/dev-mode-context.mjs +152 -0
  114. package/skills/dev-loop/scripts/dev-mode-context.test.mjs +80 -0
  115. package/skills/dev-loop/scripts/init-phase.mjs +71 -0
  116. package/skills/dev-loop/scripts/log-bash-exit-1.mjs +25 -0
  117. package/skills/dev-loop/scripts/phase-files.mjs +29 -0
  118. package/skills/dev-loop/scripts/post-gate-verdict-fallback.mjs +480 -0
  119. package/skills/dev-loop/scripts/post-gate-verdict-fallback.test.mjs +732 -0
  120. package/skills/dev-loop/scripts/render-template.mjs +82 -0
  121. package/skills/dev-loop/scripts/render-template.test.mjs +63 -0
  122. package/skills/dev-loop/templates/bootstrap-agents.md +26 -0
  123. package/skills/dev-loop/templates/bootstrap-implementation-state.md +31 -0
  124. package/skills/dev-loop/templates/bootstrap-implementation-workflow.md +17 -0
  125. package/skills/dev-loop/templates/dev-mode-retrospective.md +15 -0
  126. package/skills/dev-loop/templates/dev-mode-review.md +17 -0
  127. package/skills/dev-loop/templates/dev-mode-skill-changes.md +11 -0
  128. package/skills/dev-loop/templates/merged-phase-plan.md +19 -0
  129. package/skills/dev-loop/templates/phase-doc.md +27 -0
  130. package/skills/dev-loop/templates/phase-summary.md +13 -0
  131. package/skills/dev-loop/templates/phase-variant.md +15 -0
  132. package/skills/dev-loop/templates/retrospective.md +11 -0
  133. package/skills/dev-loop/templates/review.md +32 -0
  134. package/skills/dev-loop/templates/ui-vision-review.md +55 -0
  135. package/skills/docs/acceptance-criteria-verification.md +21 -0
  136. package/skills/docs/anti-patterns.md +21 -0
  137. package/skills/docs/artifact-authority-contract.md +119 -0
  138. package/skills/docs/confirmation-rules.md +28 -0
  139. package/skills/docs/copilot-ci-status-contract.md +52 -0
  140. package/skills/docs/copilot-loop-operations.md +233 -0
  141. package/skills/docs/debt-remediation-contract.md +107 -0
  142. package/skills/docs/entrypoint-strategies.md +115 -0
  143. package/skills/docs/epic-tree-refinement-procedure.md +234 -0
  144. package/skills/docs/issue-intake-procedure.md +235 -0
  145. package/skills/docs/main-agent-contract.md +72 -0
  146. package/skills/docs/merge-preconditions.md +29 -0
  147. package/skills/docs/pr-lifecycle-contract.md +209 -0
  148. package/skills/docs/public-dev-loop-contract.md +497 -0
  149. package/skills/docs/retrospective-checkpoint-contract.md +159 -0
  150. package/skills/docs/stop-conditions.md +29 -0
  151. package/skills/docs/structural-quality.md +42 -0
  152. package/skills/docs/tracker-first-loop-state.md +281 -0
  153. package/skills/docs/validation-policy.md +27 -0
  154. package/skills/docs/workflow-handoff-contract.md +135 -0
  155. package/skills/final-approval/SKILL.md +19 -0
  156. package/skills/local-implementation/SKILL.md +640 -0
@@ -0,0 +1,29 @@
1
+ # Stop conditions
2
+
3
+ Canonical owner for agent stop / wait / block conditions across all workflow families.
4
+
5
+ ## Genuine stop conditions
6
+
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
+ | Ambiguous / contradictory state | all | Fail closed to `needs_reconcile` |
14
+ | Missing authoritative startup inputs | `dev-loop` | Fail closed |
15
+
16
+ ## Non-stop conditions
17
+
18
+ | Condition | Strategy | Behavior |
19
+ |---|---|---|
20
+ | `waiting` lifecycle state | `wait_watch` | Healthy wait; re-dispatch from main session |
21
+ | `waiting_for_initial_copilot_implementation` | `issue_intake` | Bootstrap wait with 1h watch budget |
22
+ | `waiting_for_copilot_review` | `copilot_pr_followup` | Continuation boundary, not completion |
23
+ | Quiet watcher observations | `wait_watch` | Observational only, do not surface |
24
+
25
+ ## Cross-references
26
+
27
+ - [Confirmation rules](confirmation-rules.md)
28
+ - [Merge preconditions](merge-preconditions.md)
29
+ - [Public Dev Loop Contract](public-dev-loop-contract.md)
@@ -0,0 +1,42 @@
1
+ # Structural quality
2
+
3
+ Canonical owner for structural quality standards across all workflow families.
4
+
5
+ ## Core principles
6
+
7
+ - **KISS**: Keep implementations simple; prefer thin glue over thick abstraction
8
+ - **SRP**: Single Responsibility Principle — one reason to change per module
9
+ - **YAGNI**: Don't add speculative features, compatibility shims, or unused abstractions
10
+ - **Strict TypeScript**: No `any`, no implicit coercion, explicit return types
11
+
12
+ ## Deep review standards
13
+
14
+ Apply these during implementation (not just review):
15
+
16
+ 1. **Cohesion**: Related functionality lives together; unrelated functionality is separated
17
+ 2. **Coupling**: Minimize dependencies between modules; prefer explicit injection over globals
18
+ 3. **Error handling**: All error paths are explicit and tested; no silent failures
19
+ 4. **Testability**: Every public function is independently testable; no hidden state
20
+ 5. **Naming**: Names describe what, not how; consistent vocabulary across codebase
21
+
22
+ ## Implementation self-check rules
23
+
24
+ Apply these during implementation (not just at review time):
25
+
26
+ - **Prefer deletion over addition**: Question every new file, export, layer, and moving part. If it does not earn its keep, remove it.
27
+ - **File size ceiling**: Files over ~1k lines need extraction or an explicit justification kept in a code comment or doc reference.
28
+ - **Logic placement**: Do not bolt conditionals onto unrelated paths; push logic into its own dedicated boundary.
29
+ - **Avoid thin abstractions**: No thin wrappers, re-export-only files, or identity abstractions that add indirection without clarity.
30
+ - **No leaky abstractions**: Do not leak feature-specific logic into shared or general-purpose modules.
31
+
32
+ ## Anti-patterns to avoid
33
+
34
+ - Over-engineering: adding abstraction layers "just in case"
35
+ - Copy-paste duplication: extracting shared logic too late
36
+ - Magic values: undocumented constants or configuration
37
+ - God modules: single file doing too many unrelated things
38
+
39
+ ## Cross-references
40
+
41
+ - [Anti-patterns](anti-patterns.md)
42
+ - [Validation policy](validation-policy.md)
@@ -0,0 +1,281 @@
1
+ # Tracker-First Story-to-PR Contract
2
+
3
+ This document defines the adapter-agnostic MVP contract for the tracker-first
4
+ story-to-PR workflow in `pi-dev-loops`.
5
+
6
+ **MVP invariant: one tracker work item → one GitHub PR.**
7
+
8
+ This document serves dual purpose:
9
+ 1. **Story-to-PR contract** — canonical for the PR-level tracker state machine
10
+ 2. **Loop state machine doc** — canonical for the tracker-first loop state machine (#449)
11
+
12
+ Implementation for each:
13
+
14
+ | Component | File |
15
+ |---|---|
16
+ | PR-level state machine (story-to-PR) | `packages/core/src/loop/tracker-pr-state.mjs` |
17
+ | PR-level CLI detector | `scripts/loop/detect-tracker-pr-state.mjs` |
18
+ | Loop state machine (tracker-first) | `packages/core/src/loop/tracker-first-loop-state.mjs` |
19
+ | Loop state CLI detector | `scripts/loop/detect-tracker-first-loop-state.mjs` |
20
+ | Loop state contract doc | this file |
21
+ | Entrypoint briefing | `skills/docs/entrypoint-strategies.md#tracker-first` |
22
+
23
+ ## 1. Artifact Subset and MVP Invariant
24
+
25
+ | Artifact | Role in this slice |
26
+ |---|---|
27
+ | One tracker work item (story, task, or bug) | Planning artifact; source of truth for work identity and planning state |
28
+ | One GitHub PR | Execution artifact; source of truth for lifecycle, review, CI, and merge facts |
29
+ | PR review / CI / merge facts | Observable from GitHub; feed reverse-sync decisions |
30
+ | Epic / PRD reference | Optional linked metadata only; no roll-up or automation in this slice |
31
+ | ADR / RFC reference | Optional linked metadata only; no decision-sync in this slice |
32
+
33
+ **Invariant:** For any single tracker work item in scope, there is at most
34
+ one active GitHub PR at a time. Multi-PR workflows and roll-up automation
35
+ are out of scope for this slice.
36
+
37
+ ## 2. Source-of-Truth Ownership
38
+
39
+ | Domain | Owner | Examples |
40
+ |---|---|---|
41
+ | Work-item identity and planning hierarchy | Tracker | Item ID, title, description, priority, assignee, epic link |
42
+ | Tracker-native workflow state | Tracker | In-progress, blocked, done/completed, and any tracker-specific sub-states |
43
+ | PR lifecycle facts | GitHub | Draft / ready-for-review, open / merged / closed, branch, head SHA |
44
+ | PR review and CI facts | GitHub | Reviewer assignments, review states, check-run results, merge status |
45
+ | Decision content | ADR / RFC artifacts (when linked) | Architecture decisions and RFCs referenced from PR body |
46
+ | PR projection and reverse-sync logic | `pi-dev-loops` | Title/body/label generation rules, state mapping, sync triggers |
47
+
48
+ `pi-dev-loops` **does not** become the canonical owner of any business fields.
49
+ It provides projection and sync logic only.
50
+
51
+ ## 3. PR Projection Contract
52
+
53
+ The following rules define how a tracker work item projects into GitHub PR
54
+ metadata. All rules are deterministic and idempotent: applying them to the
55
+ same inputs always produces the same output, and re-applying them to an
56
+ already-correct PR leaves it unchanged.
57
+
58
+ ### 3.1 Required PR Metadata
59
+
60
+ | Field | Rule |
61
+ |---|---|
62
+ | PR title | `[{TRACKER_ID}] {tracker item title}` — bracketed tracker identifier followed by the item title verbatim |
63
+ | PR body — Tracker section | Required. Must include the tracker item ID and a direct link to the item. |
64
+ | PR body — Summary section | Required. Brief description of the change implemented in this PR. |
65
+ | PR body — Acceptance Criteria section | Required. Copied or linked from the tracker item's acceptance criteria. |
66
+ | 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. |
68
+
69
+ ### 3.2 Optional Reference Metadata
70
+
71
+ Optional parent / decision references may appear in the PR body as links only.
72
+ They are read-only metadata in this slice and do not trigger any automation:
73
+
74
+ | Reference type | Placement | Effect |
75
+ |---|---|---|
76
+ | Epic | PR body — References section | Link only; no roll-up automation |
77
+ | PRD | PR body — References section | Link only; no roll-up automation |
78
+ | ADR | PR body — References section | Link only; no decision sync |
79
+ | RFC | PR body — References section | Link only; no decision sync |
80
+
81
+ ### 3.3 Deterministic Update Rules
82
+
83
+ 1. **On PR create**: Generate title, body, and labels from tracker item fields.
84
+ Apply draft state.
85
+ 2. **On tracker item field change**: Re-apply projection rules to PR title,
86
+ body, and labels. Leave any sections not covered by projection rules
87
+ (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.
91
+
92
+ ### 3.4 PR Body Template
93
+
94
+ ```
95
+ ## Summary
96
+ {brief description of the change}
97
+
98
+ ## Acceptance Criteria
99
+ {copied or linked from tracker item}
100
+
101
+ ## Tracker
102
+ - Item: [{TRACKER_ID}]({TRACKER_ITEM_URL})
103
+
104
+ ## References
105
+ <!-- optional; add epic/PRD/ADR/RFC links here as plain links only -->
106
+ ```
107
+
108
+ ## 4. Reverse-Sync Contract
109
+
110
+ The following table defines the canonical reverse-sync contract. Adapter
111
+ implementations map canonical action names to tracker-native field updates.
112
+
113
+ ### 4.1 Canonical State Table
114
+
115
+ | Lifecycle state | `reverseSyncAction` | Tracker effect |
116
+ |---|---|---|
117
+ | `ready_no_pr` — tracker item exists, no PR exists yet | `none` | No automatic mutation. Tracker-native readiness remains tracker-owned and is outside this snapshot-only helper. |
118
+ | `draft_pr_open` — draft PR created or open | `set_in_progress` | Tracker moves to in-progress (or nearest equivalent) |
119
+ | `pr_reviewable` — PR marked ready for review | `set_reviewable` | Tracker moves to reviewable / in-review (or nearest equivalent) |
120
+ | `pr_merged` — PR merged | `set_done` | Tracker moves to done/completed terminal state |
121
+ | `pr_closed_unmerged` — PR closed without merge | `none` | No automatic terminal transition; human decision required |
122
+ | `no_tracker_item` | `none` | No tracker item to update |
123
+ | `blocked_needs_user_decision` | `none` | Stop and report; no automatic tracker update |
124
+
125
+ ### 4.2 Event Triggers
126
+
127
+ | PR lifecycle event | Maps to canonical state | Sync trigger |
128
+ |---|---|---|
129
+ | No PR created yet | `ready_no_pr` | No trigger |
130
+ | Draft PR creation succeeds | `draft_pr_open` | Apply `set_in_progress` to tracker |
131
+ | PR converted from draft to ready-for-review | `pr_reviewable` | Apply `set_reviewable` to tracker |
132
+ | PR merged | `pr_merged` | Apply `set_done` to tracker |
133
+ | PR closed without merge | `pr_closed_unmerged` | No automatic sync; report to user |
134
+
135
+ ### 4.3 Adapter Mapping Guidance
136
+
137
+ Each tracker adapter maps the four canonical action names to its native API:
138
+
139
+ | Canonical action | Adapter responsibility |
140
+ |---|---|
141
+ | `set_in_progress` | Move item to the adapter's in-progress equivalent (e.g. "In Progress" column, status field, or state transition) |
142
+ | `set_reviewable` | Move item to the adapter's in-review / reviewable equivalent |
143
+ | `set_done` | Move item to the adapter's done/completed terminal state |
144
+ | `none` | No tracker mutation |
145
+
146
+ ## 5. State Machine
147
+
148
+ The full lifecycle path for this MVP slice is:
149
+
150
+ ```text
151
+ selected/ready (tracker-owned precondition; not encoded directly in this snapshot)
152
+ -> tracker item exists, no PR [ready_no_pr]
153
+ -> draft PR created [draft_pr_open] -> tracker: set_in_progress
154
+ -> PR marked ready for review [pr_reviewable] -> tracker: set_reviewable
155
+ -> PR merged [pr_merged] -> tracker: set_done
156
+ ```
157
+
158
+ Alternate paths:
159
+
160
+ ```text
161
+ PR closed without merge [pr_closed_unmerged] -> tracker: no automatic action
162
+ No tracker item, no PR [no_tracker_item] -> blocked; report and stop
163
+ Contradictory snapshot [blocked_needs_user_decision] -> stop and report
164
+ ```
165
+
166
+ ### 5.1 State Definitions
167
+
168
+ | State | Meaning |
169
+ |---|---|
170
+ | `no_tracker_item` | No tracker work item was found; lifecycle cannot proceed |
171
+ | `ready_no_pr` | Tracker item exists and no PR has been created yet. This helper does not infer tracker-native readiness beyond that no-PR fact. |
172
+ | `draft_pr_open` | Draft PR exists; tracker should reflect in-progress |
173
+ | `pr_reviewable` | PR is open and not draft; tracker should reflect reviewable / in-review |
174
+ | `pr_merged` | PR has been merged; tracker should be moved to done/completed |
175
+ | `pr_closed_unmerged` | PR was closed without merge; no automatic tracker transition |
176
+ | `blocked_needs_user_decision` | Unexpected or contradictory state; requires explicit user decision |
177
+
178
+ ### 5.2 Transition Graph
179
+
180
+ ```
181
+ no_tracker_item
182
+ (no transitions — obtain a valid tracker item first)
183
+
184
+ ready_no_pr
185
+ -> draft_pr_open (create a draft PR with required tracker metadata)
186
+
187
+ draft_pr_open
188
+ -> pr_reviewable (mark the PR as ready for review)
189
+
190
+ pr_reviewable
191
+ -> pr_merged (PR is merged)
192
+ -> pr_closed_unmerged (PR is closed without merge)
193
+ -> draft_pr_open (convert back to draft if rework is needed)
194
+
195
+ pr_merged
196
+ (no transitions — terminal success state)
197
+
198
+ pr_closed_unmerged
199
+ -> ready_no_pr (reopen work; create a new PR)
200
+ -> blocked_needs_user_decision (escalate if context is unclear)
201
+
202
+ blocked_needs_user_decision
203
+ (no transitions — stop and report; await explicit user authorization)
204
+ ```
205
+
206
+ ### 5.3 Snapshot Schema
207
+
208
+ | Field | Type | Description |
209
+ |---|---|---|
210
+ | `trackerItemExists` | `boolean` | Whether a tracker work item was found |
211
+ | `trackerItemId` | `string \| null` | Opaque tracker item identifier (e.g. `"PROJ-123"`) |
212
+ | `prExists` | `boolean` | Whether a GitHub PR exists for this tracker item |
213
+ | `prNumber` | `number \| null` | PR number if known. Canonical no-PR snapshots should set this to `null`; `prNumber` with `prExists=false` is treated as contradictory and blocked. |
214
+ | `prDraft` | `boolean` | Whether the PR is in draft state |
215
+ | `prMerged` | `boolean` | Whether the PR has been merged |
216
+ | `prClosed` | `boolean` | Whether the PR is closed on GitHub. Merged PRs are also closed, so `pr_closed_unmerged` is derived from `prClosed && !prMerged`. |
217
+
218
+ Unlike the Copilot/reviewer loop snapshots, this tracker contract uses `prClosed` for the raw GitHub closed state. Merged PRs therefore set both `prMerged=true` and `prClosed=true`, while `pr_closed_unmerged` remains the derived terminal state for `prClosed && !prMerged`.
219
+
220
+ This snapshot intentionally omits tracker-native workflow fields such as selected/ready, blocked, or done. Higher-level callers must combine tracker-owned readiness/state separately when deciding whether opening a PR is appropriate.
221
+
222
+ ## 6. Scope Boundaries
223
+
224
+ This contract is intentionally narrower than the parent epics:
225
+
226
+ | Boundary | In scope (this slice) | Out of scope (deferred) |
227
+ |---|---|---|
228
+ | Tracker adapters | Adapter-agnostic contract only | Jira adapter, Shortcut adapter, any specific adapter |
229
+ | Artifact model | One tracker item + one PR | Multi-PR, roll-up, cross-artifact sync |
230
+ | Epic / PRD | Metadata reference links only | Automated roll-up or field sync |
231
+ | ADR / RFC | Metadata reference links only | Decision synchronization |
232
+ | Reverse sync | Four canonical states above | Tracker comment mirroring, full bidirectional field sync |
233
+ | Parent epics | Consistent with #17 and #19 | Does not re-own the umbrella artifact model |
234
+
235
+ ## 7. Related
236
+
237
+ - Parent workflow-family epic: [mfittko/pi-dev-loops#17](https://github.com/mfittko/pi-dev-loops/issues/17)
238
+ - Umbrella artifact model epic: [mfittko/pi-dev-loops#19](https://github.com/mfittko/pi-dev-loops/issues/19)
239
+ - This contract (first implementable slice): [mfittko/pi-dev-loops#21](https://github.com/mfittko/pi-dev-loops/issues/21)
240
+ - Copilot loop state graph: [Copilot Loop State Graph](../../docs/copilot-loop-state-graph.md)
241
+ - Reviewer loop state graph: [Reviewer Loop State Graph](../../docs/reviewer-loop-state-graph.md)
242
+
243
+ ---
244
+
245
+ ## State machine integration
246
+
247
+ This document is also the canonical state-graph doc for the tracker-first loop state machine.
248
+
249
+ ### Loop state machine (distinct from PR-level machine above)
250
+
251
+ Implementation:
252
+ - `packages/core/src/loop/tracker-first-loop-state.mjs` — pure logic layer (`interpretTrackerLoopState`)
253
+ - `scripts/loop/detect-tracker-first-loop-state.mjs` — CLI wrapper (same interface as `detect-copilot-loop-state.mjs`)
254
+
255
+ **Loop state vocabulary:** `drafting`, `needs_triage`, `in_progress`, `in_review`, `merge_ready`, `blocked`, `completed`, `unknown`
256
+
257
+ **Loop interface contract:** `{ ok, state, snapshot, allowedTransitions, nextAction }`
258
+
259
+ **Loop snapshot fields:**
260
+ | Field | Type | Description |
261
+ |---|---|---|
262
+ | `trackerState` | `string` | Canonical loop state |
263
+ | `rawTrackerState` | `string` | Raw input state string |
264
+ | `prLinked` | `boolean` | Whether a linked PR was found |
265
+ | `prContext` | `object \| null` | Linked PR context (number, state, headRefName) |
266
+
267
+ **Loop nextAction mapping:**
268
+ | State | nextAction |
269
+ |---|---|
270
+ | `drafting` | `triage_or_block` |
271
+ | `needs_triage` | `start_work` |
272
+ | `in_progress` | `review` |
273
+ | `in_review` | `merge_or_fix` |
274
+ | `merge_ready` | `merge` |
275
+ | `blocked` | `resolve_blocker` |
276
+ | `completed` | `done` |
277
+ | `unknown` | `reconcile` |
278
+
279
+ **Fail-closed contract:** Unrecognized/empty/garbage tracker state → `needs_triage`. Only explicit `"unknown"` → canonical `unknown` state.
280
+
281
+ **PR-level state machine** (sections 1-5 above) uses a separate vocabulary (`ready_no_pr`, `draft_pr_open`, `pr_reviewable`, `pr_merged`, `pr_closed_unmerged`, `no_tracker_item`, `blocked_needs_user_decision`) with its own snapshot schema and reverse-sync actions.
@@ -0,0 +1,27 @@
1
+ # Validation policy
2
+
3
+ Canonical owner for validation requirements across all workflow families.
4
+
5
+ ## Default validation
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`
10
+
11
+ ## Gate-specific requirements
12
+
13
+ | Gate | Validation required |
14
+ |---|---|
15
+ | `draft_gate` | CI green on current head (or `--local-validation-head-sha` if CI absent) |
16
+ | `pre_approval_gate` | CI green on current head + resolved review threads + clean re-review |
17
+
18
+ ## Coverage requirements
19
+
20
+ - ≥90% coverage for lines, statements, functions, and branches on changed files
21
+ - Test-first for all non-trivial logic
22
+
23
+ ## Cross-references
24
+
25
+ - [Merge preconditions](merge-preconditions.md)
26
+ - [Stop conditions](stop-conditions.md)
27
+ - [Public Dev Loop Contract](public-dev-loop-contract.md)
@@ -0,0 +1,135 @@
1
+ # Workflow Handoff — Derivation Contract
2
+
3
+ > **Status:** This document defines the **contract** for the
4
+ > `buildDevLoopHandoffEnvelope()` function in `@dev-loops/core`.
5
+ > Agents should read the envelope as their first artifact and then load
6
+ > only the listed `requiredReads` before executing `nextAction`.
7
+
8
+ ## Authoritative sources
9
+
10
+ Every field in the handoff envelope is derived from authoritative sources as shown below. No field uses a hard-coded magic string or prose
11
+ template.
12
+
13
+ | Source | Fields derived |
14
+ |---|---|
15
+ | Resolver output (`resolve-dev-loop-startup.mjs` bundle) | `target`, `nextAction`, `requiredReads`, `executionMode` |
16
+ | Caller options (`repoRoot`, `worktreeCwd`) | `cwd` |
17
+ | Gate state (detectors) + strategy defaults | `currentGate`, `worktreeRequired` |
18
+ | Settings (`.devloops` at repo root + `defaults.yaml`) | `gateConfig`, `stopRules`, `asyncStartMode`, `requireDraftFirst`, `maxCopilotRounds` |
19
+ | Gate state (detectors) | `currentHeadSha`, `ciStatus`, `unresolvedThreadCount`, `copilotRoundCount` |
20
+
21
+ ## Acceptance templates
22
+
23
+ `acceptance.criteria`, `acceptance.evidence`, `acceptance.maxFinalizationTurns`,
24
+ and `control.*` are derived from a static strategy+gate mapping table:
25
+
26
+ | Strategy | Gate | criteria | evidence | maxFinalizationTurns | needsAttentionAfterMs |
27
+ |---|---|---|---|---|---|
28
+ | `copilot_pr_followup` | `draft` | AC check, scope, coverage, DoD alignment | commands-run, validation-output, review-findings | 4 | 300000 |
29
+ | `copilot_pr_followup` | `watch` | Copilot activity detection, no stuck watch | commands-run | 2 | 1800000 |
30
+ | `copilot_pr_followup` | `pre-approval` | Full pre-approval gate chain, clean verdict, unresolved threads, CI green | commands-run, validation-output, review-findings, residual-risks | 6 | 300000 |
31
+ | `final_approval` | `default` | Gate evidence, human confirmation, CI green | validation-output, manual-notes | 2 | 300000 |
32
+ | `local_implementation` | `default` | Phase-acceptance criteria, verify green | commands-run, validation-output, changed-files | 6 | 300000 |
33
+ | `issue_intake` | `default` | Contract compliance | commands-run, validation-output | 4 | 300000 |
34
+ | `external_pr_followup` | `default` | Contract compliance | commands-run, validation-output | 4 | 300000 |
35
+ | `reviewer_fixer` | `default` | Contract compliance | commands-run, validation-output | 4 | 300000 |
36
+ | `wait_watch` | `default` | Contract compliance | commands-run, validation-output | 4 | 1800000 |
37
+
38
+ Unknown strategy+gate combinations throw an explicit error listing known combos.
39
+
40
+ ## Stop rules
41
+
42
+ Stop rules are derived from `settings.autonomy.stopAt` when present.
43
+ When absent, strategy defaults apply:
44
+
45
+ | Strategy | Default stop rules |
46
+ |---|---|
47
+ | `copilot_pr_followup` | `["draft-pr", "merge"]` |
48
+ | `issue_intake` | `["merge"]` |
49
+ | `external_pr_followup` | `["merge"]` |
50
+ | `reviewer_fixer` | `["merge"]` |
51
+ | `wait_watch` | `["merge"]` |
52
+ | `final_approval` | `["merge"]` |
53
+ | `local_implementation` | `[]` (auto-continue) |
54
+
55
+ ## Envelope schema
56
+
57
+ ```typescript
58
+ interface HandoffEnvelope {
59
+ handoffVersion: 1;
60
+ derivedAt: string; // ISO timestamp
61
+
62
+ target: {
63
+ kind: "issue" | "pr" | "local_branch" | "local_phase";
64
+ repo: string;
65
+ issue?: number;
66
+ pr?: number;
67
+ linkedPr?: number;
68
+ branch?: string;
69
+ phase?: string;
70
+ };
71
+
72
+ currentGate: string;
73
+ currentHeadSha: string | null;
74
+ ciStatus: string | null;
75
+ unresolvedThreadCount: number;
76
+ copilotRoundCount: number;
77
+ maxCopilotRounds: number;
78
+ executionMode: "bounded_handoff" | "durable_auto";
79
+
80
+ nextAction: string;
81
+ requiredReads: string[];
82
+
83
+ gateConfig?: {
84
+ angles: string[];
85
+ excludeAngles?: string[];
86
+ blockCleanOnFindingSeverities: string[];
87
+ requireCi: boolean;
88
+ };
89
+
90
+ stopRules: string[];
91
+ asyncStartMode: "required" | "allowed";
92
+ requireDraftFirst: boolean;
93
+
94
+ cwd: string | null;
95
+ worktreeRequired: boolean;
96
+
97
+ acceptance: {
98
+ criteria: Array<{ id: string; must: string; severity: "required" | "recommended" }>;
99
+ evidence: string[];
100
+ maxFinalizationTurns: number;
101
+ };
102
+
103
+ control: {
104
+ needsAttentionAfterMs: number;
105
+ activeNoticeAfterMs: number;
106
+ };
107
+
108
+ overrides?: {
109
+ mergeAuthorized?: boolean;
110
+ preferLocal?: boolean;
111
+ scopeConstraint?: string;
112
+ customStopAt?: string;
113
+ };
114
+ }
115
+ ```
116
+
117
+ ## Agent consumption pattern
118
+
119
+ 1. Read the handoff envelope as the first artifact.
120
+ 2. Read every path listed in `requiredReads` (in order).
121
+ 3. Execute `nextAction`.
122
+ 4. Respect `stopRules` — do not proceed past a gated stop point without authorization.
123
+ 5. Use `acceptance` to self-validate before declaring completion.
124
+
125
+ ## Backward compatibility
126
+
127
+ The `acceptance` block maps 1:1 into the existing `subagent()` acceptance
128
+ contract shape. When the envelope is present, no separate prose task
129
+ parameter is required.
130
+
131
+ ## Non-goals
132
+
133
+ - This contract does not define dispatch mechanics.
134
+ - This contract does not define UI/UX for envelope display.
135
+ - This contract does not modify the `subagent()` API itself.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: final-approval
3
+ description: >-
4
+ Internal routed strategy behind `dev-loop` for the final human approval and
5
+ merge gate. The canonical procedure now lives in copilot-pr-followup.
6
+ compatibility: Pi skill for git+GitHub repositories. Requires gh auth.
7
+ allowed-tools: read bash edit write subagent review_loop
8
+ user-invocable: false
9
+ ---
10
+
11
+ # Final Approval (redirect)
12
+
13
+ This route now uses [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md) as the canonical procedure owner.
14
+
15
+ When the public router selects `final_approval`, load [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md)
16
+ and follow its **Human approval checkpoint** section inside Step 7.
17
+
18
+ Use this redirect only as a narrowed read-set pointer for the routed `final_approval` strategy.
19
+ Do not restate merge-ready preconditions, gate evidence rules, or merge authorization policy here.