dev-loops 0.6.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/agents/dev-loop.md +8 -7
- package/.claude/agents/fixer.md +1 -0
- package/.claude/agents/refiner.md +2 -2
- package/.claude/agents/review.md +4 -3
- package/.claude/commands/{auto.md → loop-auto.md} +1 -1
- package/.claude/commands/loop-continue.md +15 -0
- package/.claude/commands/loop-enqueue.md +22 -0
- package/.claude/commands/loop-grill.md +17 -0
- package/.claude/commands/{info.md → loop-info.md} +2 -2
- package/.claude/commands/loop-queue-status.md +24 -0
- package/.claude/commands/{start-spike.md → loop-start-spike.md} +2 -2
- package/.claude/commands/{start.md → loop-start.md} +1 -1
- package/.claude/commands/{status.md → loop-status.md} +1 -1
- package/.claude/hooks/_bash-command-classify.mjs +333 -29
- package/.claude/hooks/_hook-decisions.mjs +138 -15
- package/.claude/hooks/pre-tool-use-bash-gate.mjs +36 -15
- package/.claude/skills/copilot-pr-followup/SKILL.md +60 -34
- package/.claude/skills/dev-loop/SKILL.md +16 -12
- package/.claude/skills/docs/acceptance-criteria-verification.md +8 -3
- package/.claude/skills/docs/anti-patterns.md +5 -3
- package/.claude/skills/docs/artifact-authority-contract.md +30 -11
- package/.claude/skills/docs/confirmation-rules.md +1 -0
- package/.claude/skills/docs/contract-style-guide.md +37 -0
- package/.claude/skills/docs/copilot-loop-operations.md +19 -15
- package/.claude/skills/docs/cross-harness-regression-contract.md +60 -0
- package/.claude/skills/docs/debt-remediation-contract.md +1 -1
- package/.claude/skills/docs/epic-tree-refinement-procedure.md +25 -22
- package/.claude/skills/docs/issue-intake-procedure.md +26 -19
- package/.claude/skills/docs/local-planning-flow.md +1 -1
- package/.claude/skills/docs/local-planning-worked-example.md +1 -1
- package/.claude/skills/docs/merge-preconditions.md +30 -12
- package/.claude/skills/docs/plan-file-contract.md +1 -1
- package/.claude/skills/docs/pr-lifecycle-contract.md +44 -35
- package/.claude/skills/docs/public-dev-loop-contract.md +60 -44
- package/.claude/skills/docs/retrospective-checkpoint-contract.md +106 -79
- package/.claude/skills/docs/spike-mode-contract.md +14 -7
- package/.claude/skills/docs/stop-conditions.md +30 -15
- package/.claude/skills/docs/tracker-first-loop-state.md +13 -8
- package/.claude/skills/docs/ui-e2e-scoping-step.md +32 -0
- package/.claude/skills/docs/validation-policy.md +4 -5
- package/.claude/skills/docs/workflow-handoff-contract.md +39 -0
- package/.claude/skills/local-implementation/SKILL.md +62 -145
- package/.claude/skills/loop-grill/SKILL.md +163 -0
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +101 -0
- package/README.md +9 -9
- package/agents/dev-loop.agent.md +11 -10
- package/agents/developer.agent.md +1 -1
- package/agents/docs.agent.md +1 -1
- package/agents/fixer.agent.md +2 -1
- package/agents/quality.agent.md +1 -1
- package/agents/refiner.agent.md +3 -3
- package/agents/review.agent.md +5 -4
- package/cli/index.mjs +35 -42
- package/extension/README.md +4 -4
- package/extension/checks.ts +1 -5
- package/extension/harness-types.ts +1 -0
- package/extension/index.ts +6 -0
- package/extension/pi-extension-adapter.ts +2 -0
- package/extension/presentation.ts +7 -19
- package/package.json +12 -12
- package/scripts/_core-helpers.mjs +2 -0
- package/scripts/claude/headless-dev-loop.mjs +1 -1
- package/scripts/docs/validate-rule-ownership.mjs +442 -0
- package/scripts/docs/validate-state-machine-conformance.mjs +1092 -0
- package/scripts/github/_review-thread-mutations.mjs +5 -2
- package/scripts/github/capture-review-threads.mjs +2 -9
- package/scripts/github/comment-issue.mjs +2 -9
- package/scripts/github/create-label.mjs +133 -0
- package/scripts/github/create-pr.mjs +119 -10
- package/scripts/github/detect-checkpoint-evidence.mjs +259 -18
- package/scripts/github/detect-linked-issue-pr.mjs +22 -6
- package/scripts/github/edit-pr.mjs +259 -0
- package/scripts/github/fetch-ci-logs.mjs +2 -9
- package/scripts/github/list-issues.mjs +2 -9
- package/scripts/github/list-review-threads.mjs +277 -0
- package/scripts/github/manage-sub-issues.mjs +46 -10
- package/scripts/github/offer-human-handoff.mjs +8 -5
- package/scripts/github/post-gate-findings.mjs +20 -5
- package/scripts/github/probe-ci-status.mjs +8 -2
- package/scripts/github/probe-copilot-review.mjs +21 -14
- package/scripts/github/ready-for-review.mjs +26 -8
- package/scripts/github/reconcile-draft-gate.mjs +7 -3
- package/scripts/github/reply-resolve-review-thread.mjs +16 -5
- package/scripts/github/reply-resolve-review-threads.mjs +69 -7
- package/scripts/github/request-copilot-review.mjs +98 -26
- package/scripts/github/resolve-handoff-candidates.mjs +7 -3
- package/scripts/github/resolve-tracker-local-spec.mjs +9 -3
- package/scripts/github/stage-reviewer-draft.mjs +10 -2
- package/scripts/github/tick-verified-checkboxes.mjs +202 -0
- package/scripts/github/upsert-checkpoint-verdict.mjs +105 -15
- package/scripts/github/verify-briefing-prefixes.mjs +191 -0
- package/scripts/github/verify-fresh-review-context.mjs +226 -32
- package/scripts/github/view-pr.mjs +150 -0
- package/scripts/github/wait-pr-checks.mjs +171 -0
- package/scripts/github/write-gate-context.mjs +458 -65
- package/scripts/github/write-gate-findings-log.mjs +128 -2
- package/scripts/lib/jq-output.mjs +18 -0
- package/scripts/loop/_post-convergence-change.mjs +211 -0
- package/scripts/loop/_pr-runner-coordination.mjs +70 -0
- package/scripts/loop/_repo-root-resolver.mjs +47 -0
- package/scripts/loop/build-handoff-envelope.mjs +14 -4
- package/scripts/loop/check-retro-tooling.mjs +14 -3
- package/scripts/loop/checkpoint-contract.mjs +7 -4
- package/scripts/loop/cleanup-worktree.mjs +12 -3
- package/scripts/loop/conductor-monitor.mjs +12 -18
- package/scripts/loop/copilot-pr-handoff.mjs +162 -14
- package/scripts/loop/debt-remediate.mjs +24 -12
- package/scripts/loop/detect-change-scope.mjs +38 -9
- package/scripts/loop/detect-copilot-loop-state.mjs +34 -8
- package/scripts/loop/detect-copilot-session-activity.mjs +7 -3
- package/scripts/loop/detect-initial-copilot-pr-state.mjs +7 -3
- package/scripts/loop/detect-internal-only-pr.mjs +8 -2
- package/scripts/loop/detect-issue-refinement-artifact.mjs +6 -3
- package/scripts/loop/detect-pr-gate-coordination-state.mjs +184 -69
- package/scripts/loop/detect-reviewer-loop-state.mjs +12 -2
- package/scripts/loop/detect-tracker-first-loop-state.mjs +9 -1
- package/scripts/loop/detect-tracker-pr-state.mjs +10 -3
- package/scripts/loop/ensure-worktree.mjs +8 -3
- package/scripts/loop/info.mjs +12 -4
- package/scripts/loop/inspect-run-viewer/constants.mjs +4 -18
- package/scripts/loop/inspect-run-viewer/vendor/mermaid.min.js +3405 -0
- package/scripts/loop/inspect-run-viewer-ci-changes.mjs +18 -6
- package/scripts/loop/inspect-run-viewer.mjs +67 -4
- package/scripts/loop/inspect-run.mjs +8 -2
- package/scripts/loop/outer-loop.mjs +8 -4
- package/scripts/loop/pr-runner-coordination.mjs +2 -9
- package/scripts/loop/pre-commit-branch-guard.mjs +16 -10
- package/scripts/loop/pre-flight-gate.mjs +9 -9
- package/scripts/loop/pre-pr-ready-gate.mjs +8 -4
- package/scripts/loop/pre-write-remote-freshness-guard.mjs +13 -4
- package/scripts/loop/provision-worktree.mjs +74 -3
- package/scripts/loop/resolve-dev-loop-startup.mjs +216 -10
- package/scripts/loop/resolve-gate-dispatch.mjs +134 -0
- package/scripts/loop/resolve-pr-conflicts.mjs +14 -7
- package/scripts/loop/run-conductor-cycle.mjs +8 -2
- package/scripts/loop/run-queue.mjs +18 -9
- package/scripts/loop/run-refinement-audit.mjs +8 -9
- package/scripts/loop/run-watch-cycle.mjs +2 -9
- package/scripts/loop/sanctioned-commands.mjs +106 -0
- package/scripts/loop/steer-loop.mjs +29 -16
- package/scripts/loop/validate-pr-body-spec.mjs +223 -0
- package/scripts/loop/watch-initial-copilot-pr.mjs +8 -3
- package/scripts/pages/build-site.mjs +59 -8
- package/scripts/pages/build-state-atlas.mjs +443 -0
- package/scripts/projects/_resolve-project.mjs +1 -0
- package/scripts/projects/add-queue-item.mjs +60 -54
- package/scripts/projects/archive-done-items.mjs +49 -84
- package/scripts/projects/ensure-queue-board.mjs +10 -36
- package/scripts/projects/list-queue-items.mjs +59 -382
- package/scripts/projects/move-queue-item.mjs +21 -449
- package/scripts/projects/reconcile-queue.mjs +253 -0
- package/scripts/projects/reorder-queue-item.mjs +43 -68
- package/scripts/projects/resolve-active-board-item.mjs +108 -46
- package/scripts/projects/sync-item-status.mjs +15 -10
- package/scripts/refine/_refine-helpers.mjs +21 -5
- package/scripts/refine/exit-spike.mjs +18 -8
- package/scripts/refine/promote-plan.mjs +22 -16
- package/scripts/refine/prose-linkage-detector.mjs +1 -1
- package/scripts/refine/refine-plan-file.mjs +13 -4
- package/scripts/refine/refinement-completeness-checker.mjs +1 -1
- package/scripts/refine/scaffold-spike-file.mjs +4 -8
- package/scripts/refine/scope-boundary-cross-checker.mjs +1 -1
- package/scripts/refine/tree-integrity-validator.mjs +1 -1
- package/scripts/refine/validate-plan-file.mjs +1 -1
- package/scripts/refine/validate-spike-file.mjs +1 -1
- package/scripts/refine/verify.mjs +11 -6
- package/scripts/release/assert-core-dependency-version.mjs +123 -0
- package/scripts/release/extract-changelog-section.mjs +16 -2
- package/skills/copilot-pr-followup/SKILL.md +60 -34
- package/skills/dev-loop/SKILL.md +11 -7
- package/skills/docs/acceptance-criteria-verification.md +8 -3
- package/skills/docs/anti-patterns.md +5 -3
- package/skills/docs/artifact-authority-contract.md +30 -11
- package/skills/docs/confirmation-rules.md +1 -0
- package/skills/docs/contract-style-guide.md +37 -0
- package/skills/docs/copilot-loop-operations.md +19 -15
- package/skills/docs/cross-harness-regression-contract.md +60 -0
- package/skills/docs/debt-remediation-contract.md +1 -1
- package/skills/docs/epic-tree-refinement-procedure.md +25 -22
- package/skills/docs/issue-intake-procedure.md +26 -19
- package/skills/docs/local-planning-flow.md +1 -1
- package/skills/docs/local-planning-worked-example.md +1 -1
- package/skills/docs/merge-preconditions.md +30 -12
- package/skills/docs/plan-file-contract.md +1 -1
- package/skills/docs/pr-lifecycle-contract.md +44 -35
- package/skills/docs/public-dev-loop-contract.md +60 -44
- package/skills/docs/required-rules.json +149 -0
- package/skills/docs/retrospective-checkpoint-contract.md +106 -79
- package/skills/docs/spike-mode-contract.md +14 -7
- package/skills/docs/stop-conditions.md +30 -15
- package/skills/docs/tracker-first-loop-state.md +13 -8
- package/skills/docs/ui-e2e-scoping-step.md +32 -0
- package/skills/docs/validation-policy.md +4 -5
- package/skills/docs/workflow-handoff-contract.md +39 -0
- package/skills/local-implementation/SKILL.md +62 -145
- package/skills/loop-grill/SKILL.md +165 -0
- package/.claude/commands/continue.md +0 -15
- package/scripts/docs/validate-no-duplicate-rules.mjs +0 -250
- package/scripts/loop/conductor.mjs +0 -233
- package/scripts/loop/detect-stale-runner.mjs +0 -265
- package/scripts/loop/pre-push-main-guard.mjs +0 -117
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Retrospective checkpoint contract
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Canonical owner for the enforcement seam of the post-run behavioral retrospective checkpoint after qualifying async `dev-loop` completions in this repository.
|
|
4
|
+
|
|
5
|
+
<!-- rule: RETRO-ENFORCEMENT-CONFIG-GATED -->
|
|
6
|
+
Whether a missing checkpoint blocks the next qualifying start/resume MUST be controlled by `.devloops` at repo root `workflow.requireRetrospective`; shipped defaults remain permissive and this repo opts in.
|
|
4
7
|
|
|
5
8
|
## Relationship to formal dev mode
|
|
6
9
|
|
|
@@ -85,67 +88,99 @@ Callers have two supported integration options:
|
|
|
85
88
|
4. Call `evaluateRetrospectiveGate({ checkpointState, proposedRouting })`.
|
|
86
89
|
5. Use the gate result (not the raw routing result) as the effective routing decision when enforcement is enabled; otherwise keep the raw routing result and treat the checkpoint artifact as advisory context only.
|
|
87
90
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
**
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
91
|
+
<!-- rule: RETRO-GATE-FAIL-CLOSED -->
|
|
92
|
+
If the gate result is `needs_reconcile`, the caller MUST NOT proceed with the proposed routing. The `nextAction` field instructs the operator to complete or explicitly skip the retrospective.
|
|
93
|
+
|
|
94
|
+
## Advisory findings — never a merge gate (issue #1077, Reading B)
|
|
95
|
+
|
|
96
|
+
<!-- rule: RETRO-ADVISORY-NEVER-GATE -->
|
|
97
|
+
The retrospective is **advisory**: it runs, records flagged raw-call / discipline
|
|
98
|
+
observations honestly, and passes them back to the conductor (main agent) to
|
|
99
|
+
**decide** what to do with them — but it MUST NOT block a merge or any lifecycle
|
|
100
|
+
transition. The pre-merge retrospective gate (`evaluateRetrospectiveMergeApproval`
|
|
101
|
+
and the `requireRetrospectiveGate` / `requireRetrospectiveInternalTooling` config
|
|
102
|
+
keys) has been **removed**. There is no `retrospective_gate_pending` / `blocked`
|
|
103
|
+
disposition on account of the internal-tooling raw-call record.
|
|
104
|
+
|
|
105
|
+
### How findings travel (Reading B)
|
|
106
|
+
|
|
107
|
+
<!-- rule: RETRO-FINDINGS-ENVELOPE-CARRY -->
|
|
108
|
+
1. **Deterministic return contract.** The loop subagent's handoff envelope MUST carry
|
|
109
|
+
the retrospective findings as a structured `retrospectiveFindings` field — the
|
|
110
|
+
`check-retro-tooling.mjs` JSON output (`{ internalToolingOnly, rawCallViolations,
|
|
111
|
+
allowedWriteOps }`), not prose. The conductor reads that field. This is a hard
|
|
112
|
+
contract; see [Workflow Handoff Contract](./workflow-handoff-contract.md).
|
|
113
|
+
2. **Durability — advisory PR comment.** The conductor posts a single advisory PR
|
|
114
|
+
comment carrying the findings (`rawCallViolations`, `internalToolingOnly`,
|
|
115
|
+
`allowedWriteOps`). Durable and on-GitHub, but **not a gate**. No disk artifact
|
|
116
|
+
is written for retrospective *findings*.
|
|
117
|
+
3. **No config.** There is nothing to configure: the retrospective always runs and
|
|
118
|
+
always returns findings. `requireRetrospectiveGate` and
|
|
119
|
+
`requireRetrospectiveInternalTooling` no longer exist.
|
|
120
|
+
|
|
121
|
+
A PR that is otherwise green becomes merge-ready with the violations **recorded**,
|
|
122
|
+
not blocked. The conductor may note them, open a follow-up, coach — or ignore.
|
|
123
|
+
|
|
124
|
+
### Internal-tooling-only rule (issue #982) — now advisory
|
|
125
|
+
|
|
126
|
+
This rule records the dev-loops maintainers' own dogfooding discipline: the loop's
|
|
127
|
+
own execution should use internal dev-loops tooling, not agent-level raw
|
|
128
|
+
`gh`/`python`/`node -e` escape hatches. **It no longer blocks.** The flagged calls
|
|
129
|
+
are reported as advisory findings via the envelope + PR comment.
|
|
130
|
+
|
|
131
|
+
**Flagged as raw-call violations:** `gh ...` (including `gh api`, `gh ... --jq`),
|
|
132
|
+
`python` / `python3`, `node -e` / `node --eval` (inline eval). **Allowed (NOT
|
|
133
|
+
violations):** dev-loops subcommands and `node scripts/*.mjs` invocations — those
|
|
134
|
+
scripts legitimately call `gh`/GraphQL internally; that is the tooling. The rule
|
|
135
|
+
targets the agent's own top-level shell calls, not a script's internals.
|
|
136
|
+
|
|
137
|
+
**Write-op allowlist (verifier only):** `gh pr merge`, `gh pr ready`,
|
|
138
|
+
`gh issue create`, `gh issue edit` have no internal wrapper today. The deterministic
|
|
139
|
+
verifier records these as `allowedWriteOps` rather than violations so they are
|
|
140
|
+
surfaced distinctly, not as breaches. Close the gap with a wrapper to remove an
|
|
141
|
+
entry. None of these block anything.
|
|
142
|
+
|
|
143
|
+
**Inline-interpreter check item:** the raw-call scan below mechanically catches
|
|
144
|
+
`node -e`/`python3 -c`/heredoc calls as a `rawCallViolations` entry — the same
|
|
145
|
+
class barred by `OPS-NO-INLINE-INTERPRETER` in
|
|
146
|
+
[Copilot loop operations](copilot-loop-operations.md). This is an addition to
|
|
147
|
+
what the retrospective records, not a new gate: `RETRO-ADVISORY-NEVER-GATE`
|
|
148
|
+
semantics are unchanged.
|
|
149
|
+
|
|
150
|
+
### Deterministic verifier (findings-producer)
|
|
151
|
+
|
|
152
|
+
`node scripts/loop/check-retro-tooling.mjs [--transcript <path>] [--json]` reads a
|
|
153
|
+
newline-delimited transcript of the shell commands the agent ran (one top-level
|
|
154
|
+
command per line, via `--transcript` or stdin) and reports agent-level raw
|
|
155
|
+
`gh`/`python`/`python3`/`node -e`/`node --eval` calls. It is a **findings-producer**:
|
|
156
|
+
its JSON output (`{ ok, internalToolingOnly, rawCallViolations, allowedWriteOps }`)
|
|
157
|
+
is returned to the conductor via the envelope's `retrospectiveFindings` field (the
|
|
158
|
+
envelope carries the normalized shape `{ internalToolingOnly, rawCallViolations,
|
|
159
|
+
allowedWriteOps }` — the redundant `ok` flag is dropped by normalization) — it
|
|
160
|
+
is **not** written to a checkpoint and **not** a gate. Exit code `1` when violations
|
|
161
|
+
are found, `0` when clean. The pure `analyzeTranscript(transcript)` export returns
|
|
162
|
+
`{ violations, allowedWriteOps, internalToolingOnly }`.
|
|
163
|
+
|
|
164
|
+
Matching rules: a tool name at the start of a command segment (start of line, or
|
|
165
|
+
after `&&`/`||`/`|`/`;`); `node` is a violation only with `-e`/`--eval`. Before
|
|
166
|
+
classifying, the verifier normalizes the segment head — it strips leading
|
|
167
|
+
`NAME=value` env-assignment prefixes (`GH_TOKEN=x gh api`), strips a leading
|
|
168
|
+
wrapper binary from `{sudo, env, xargs, time, nice, command}` (`sudo gh api`,
|
|
169
|
+
`xargs gh api`), and reduces a path-prefixed binary to its basename
|
|
170
|
+
(`./node_modules/.bin/gh`, `/usr/bin/python3`) — so the common prefixed/wrapped
|
|
171
|
+
raw-call forms are caught. Known limitation: it does NOT fully parse shell
|
|
172
|
+
quoting/substitution. A separator inside a quoted argument can over-report; deeply
|
|
173
|
+
obfuscated calls (command substitution `$(...)`, aliases, `eval`) may evade it —
|
|
174
|
+
prefer single-line, single-purpose commands in transcripts.
|
|
175
|
+
|
|
176
|
+
### Lifecycle reconciliation
|
|
177
|
+
|
|
178
|
+
The retrospective is described consistently as a **post-merge / advisory
|
|
179
|
+
reflection**, never a pre-merge blocker. The former contradiction —
|
|
180
|
+
`lifecycle-state.mjs` documenting the retro as a post-merge write while
|
|
181
|
+
`pr-gate-coordination.mjs` enforced it pre-merge — is resolved by removing the
|
|
182
|
+
pre-merge gate: the merge lifecycle step proceeds, and the retrospective is an
|
|
183
|
+
advisory reflection whose findings reach the conductor via the envelope.
|
|
149
184
|
|
|
150
185
|
## Durable artifact format
|
|
151
186
|
|
|
@@ -162,25 +197,17 @@ The checkpoint file is written by `.pi/extensions/dev-loop-behavioral-review.ts`
|
|
|
162
197
|
|
|
163
198
|
### After retrospective is done (written by operator or skill)
|
|
164
199
|
|
|
165
|
-
A minimal completion clears the startup gate.
|
|
166
|
-
|
|
167
|
-
`
|
|
200
|
+
A minimal completion clears the startup/resume completion gate. The checkpoint
|
|
201
|
+
file carries **only completion state** — retrospective *findings*
|
|
202
|
+
(`behavioralReview`, `rawCallViolations`, `internalToolingOnly`) no longer live on
|
|
203
|
+
disk; they travel in the handoff envelope's `retrospectiveFindings` field and an
|
|
204
|
+
advisory PR comment (issue #1077, Reading B):
|
|
168
205
|
|
|
169
206
|
```json
|
|
170
207
|
{
|
|
171
208
|
"state": "complete",
|
|
172
209
|
"completedAt": "2026-05-29T16:30:00.000Z",
|
|
173
|
-
"notes": "Loop followed working agreement; minor drift on thread resolution."
|
|
174
|
-
"gateQuality": "Real gates with concrete findings and follow-through.",
|
|
175
|
-
"mergeRecommendation": "Merge approved — all gates passed clean.",
|
|
176
|
-
"behavioralReview": {
|
|
177
|
-
"mergeApproved": true,
|
|
178
|
-
"followedWorkingAgreement": true,
|
|
179
|
-
"gateQualityAcceptable": true,
|
|
180
|
-
"drifts": [],
|
|
181
|
-
"internalToolingOnly": true,
|
|
182
|
-
"rawCallViolations": []
|
|
183
|
-
}
|
|
210
|
+
"notes": "Loop followed working agreement; minor drift on thread resolution."
|
|
184
211
|
}
|
|
185
212
|
```
|
|
186
213
|
|
|
@@ -199,9 +226,9 @@ A minimal completion clears the startup gate. To also clear the **merge gate**
|
|
|
199
226
|
| Artifact | Location |
|
|
200
227
|
|---|---|
|
|
201
228
|
| Checkpoint state machine | `packages/core/src/loop/retrospective-checkpoint.mjs` (internal core module; not part of the public package exports surface) |
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
| Tests | `packages/core/test/retrospective-checkpoint.test.mjs`, `packages/core/test/pr-gate-coordination.test.mjs` |
|
|
229
|
+
| Internal-tooling verifier (findings-producer) | `scripts/loop/check-retro-tooling.mjs` (+ `test/loop/check-retro-tooling.test.mjs`) |
|
|
230
|
+
| Advisory findings envelope field | `packages/core/src/loop/handoff-envelope.mjs` — `retrospectiveFindings` |
|
|
231
|
+
| Tests | `packages/core/test/retrospective-checkpoint.test.mjs`, `packages/core/test/pr-gate-coordination.test.mjs`, `packages/core/test/handoff-envelope.test.mjs` |
|
|
205
232
|
| Extension (writes required marker, fires review prompt) | `.pi/extensions/dev-loop-behavioral-review.ts` |
|
|
206
233
|
| Checkpoint file | `.pi/dev-loop-retrospective-checkpoint.json` |
|
|
207
234
|
| AGENTS.md repo contract | [Agent Instructions](../../AGENTS.md) — concise repo contract and working rules |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Spike-mode contract
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Canonical owner for spike mode: the time-boxed, exploratory dev-loop run started from a local question with no GitHub issue. Its deliverable is a findings document. When the exploration reaches a recommendation, the operator concludes it with one of two exits: **discard** (drop it with zero tracker artifacts) or **graduate** (emit a #947-style local-first plan file that enters the existing plan→PR promotion path).
|
|
4
4
|
|
|
5
5
|
This doc is the canonical operator sequence for spike mode and carries a worked example showing both exits. The spike artifact lives outside the tracker; it is exempt from the production-gate ceremony at entry and runs under a relaxed gate profile (`gates.spike`). The [Artifact Authority Contract](artifact-authority-contract.md) owns the local-planning model a graduated spike feeds into.
|
|
6
6
|
|
|
@@ -53,7 +53,10 @@ Hand the artifact to startup with `--spike` (mutually exclusive with `--issue`,
|
|
|
53
53
|
node scripts/loop/resolve-dev-loop-startup.mjs --spike <spike.md>
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
`buildSpikeInput` (`scripts/loop/resolve-dev-loop-startup.mjs`) reads the file read-only — no tracker mutation, no GitHub call, no issue or PR number.
|
|
56
|
+
`buildSpikeInput` (`scripts/loop/resolve-dev-loop-startup.mjs`) reads the file read-only — no tracker mutation, no GitHub call, no issue or PR number.
|
|
57
|
+
|
|
58
|
+
<!-- rule: SPIKE-STARTUP-SCAFFOLD-GATE -->
|
|
59
|
+
Startup MUST require the exploration scaffold: a missing/unreadable file, or one failing `validateSpikeExplorationSections`, MUST throw, and startup MUST fail closed (exit 1, no readiness bundle). On success it builds a `local_phase` startup input with the resolved spike path as the target `phase` and threads `spikeIntakeState` onto the output. A spike with no Recommendation reports `spike_in_progress`. The spike path is exempt from the worktree-isolation guard because there is no issue to key a worktree on (`planFileExempt: true`).
|
|
57
60
|
|
|
58
61
|
### 3. Run under the relaxed gate profile
|
|
59
62
|
|
|
@@ -67,7 +70,8 @@ When the exploration concludes, fill in `## Recommendation`. The intake state is
|
|
|
67
70
|
node scripts/refine/exit-spike.mjs --spike-file <spike.md> --disposition <discard|graduate> [--plan-file <path>] [--json]
|
|
68
71
|
```
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
<!-- rule: SPIKE-EXIT-ELIGIBILITY -->
|
|
74
|
+
`evaluateSpikeExit` (`packages/core/src/loop/spike-exit-contract.mjs`) MUST be eligible only from `spike_ready_for_exit`: from any other state it MUST fail closed with `not_ready_for_exit`, and an unrecognized disposition MUST fail closed with `unknown_disposition`. On a fail-closed path the CLI MUST make zero tracker mutation, write no plan file, and exit 1.
|
|
71
75
|
|
|
72
76
|
## Exit dispositions
|
|
73
77
|
|
|
@@ -75,22 +79,25 @@ The two dispositions are `SPIKE_EXIT_DISPOSITION.DISCARD` (`"discard"`) and `SPI
|
|
|
75
79
|
|
|
76
80
|
### discard
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
<!-- rule: SPIKE-DISCARD-ZERO-MUTATION -->
|
|
83
|
+
The recommendation is "don't pursue". Discard MUST drop the spike with zero tracker artifacts — the findings document on disk is the whole record. The CLI MUST write nothing and MUST create no GitHub artifact. `--plan-file` is irrelevant for a discard.
|
|
79
84
|
|
|
80
85
|
### graduate
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
<!-- rule: SPIKE-GRADUATE-PLAN-FILE-REQUIRED -->
|
|
88
|
+
The recommendation is "pursue this". `buildGraduatedPlanBody` builds a #947-consumable plan-file body from the spike's `Question`, `Approach`, `Findings`, and `Recommendation`, and `exit-spike.mjs` writes it to the required `--plan-file` path — `--plan-file` MUST be present for a graduate exit; the CLI MUST NOT guess an output path. The mapping:
|
|
83
89
|
|
|
84
90
|
- `Question` + `Approach` become the Objective's context.
|
|
85
91
|
- `Recommendation` becomes the In-scope work.
|
|
86
92
|
- `Findings` are recorded as supporting evidence.
|
|
87
93
|
- A fixed Explicit non-goals block keeps the plan from re-opening the concluded exploration.
|
|
88
94
|
|
|
89
|
-
The emitted body carries the four plan-file base sections (`## Status`, `## Objective`, `## In scope`, `## Explicit non-goals` — see [Plan-file Contract](plan-file-contract.md)), so it passes `validatePlanFile` and enters the local-first plan→PR promotion path (#952) unchanged: refine it, hold the local human-review checkpoint, then `promote-plan.mjs`. Graduation is idempotent — `buildGraduatedPlanBody` is pure, so re-running reproduces the same plan file
|
|
95
|
+
The emitted body carries the four plan-file base sections (`## Status`, `## Objective`, `## In scope`, `## Explicit non-goals` — see [Plan-file Contract](plan-file-contract.md)), so it passes `validatePlanFile` and enters the local-first plan→PR promotion path (#952) unchanged: refine it, hold the local human-review checkpoint, then `promote-plan.mjs`. Graduation is idempotent — `buildGraduatedPlanBody` is pure, so re-running reproduces the same plan file, and it fails closed (throws) on an empty required section, so a graduate exit cannot emit a plan the validator would reject.
|
|
90
96
|
|
|
91
97
|
## Relaxed gate profile
|
|
92
98
|
|
|
93
|
-
|
|
99
|
+
<!-- rule: SPIKE-RELAXED-GATE-PROFILE -->
|
|
100
|
+
A spike's deliverable is a findings doc, so it MUST NOT carry the full production draft → pre-approval → Copilot gate set; it runs under `gates.spike`, a relaxed profile shipped in `packages/core/src/config/extension-defaults.yaml`:
|
|
94
101
|
|
|
95
102
|
```yaml
|
|
96
103
|
gates:
|
|
@@ -2,29 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
Canonical owner for agent stop / wait / block conditions across all workflow families.
|
|
4
4
|
|
|
5
|
+
## Terms
|
|
6
|
+
|
|
7
|
+
| Term | Definition |
|
|
8
|
+
|---|---|
|
|
9
|
+
| <!-- term: state:blocked --> `blocked` | Lifecycle state requiring a human decision before the loop continues. |
|
|
10
|
+
| <!-- term: state:done --> `done` | Terminal lifecycle state. |
|
|
11
|
+
| <!-- term: state:approval_ready --> `approval_ready` | Lifecycle state where approval evidence is ready but merge authorization is not implied. |
|
|
12
|
+
| <!-- term: state:merge_ready --> `merge_ready` | Lifecycle state where merge preconditions are ready to evaluate. |
|
|
13
|
+
| <!-- term: state:waiting --> `waiting` | Lifecycle state for healthy external waits. |
|
|
14
|
+
| <!-- term: state:waiting_for_initial_copilot_implementation --> `waiting_for_initial_copilot_implementation` | Bootstrap wait state for the first Copilot PR. |
|
|
15
|
+
| <!-- term: state:waiting_for_copilot_review --> `waiting_for_copilot_review` | Copilot review-settle wait state for the current head. |
|
|
16
|
+
| <!-- term: state:waiting_for_merge_authorization --> `waiting_for_merge_authorization` | Stop state for merge-ready work without explicit merge authorization. |
|
|
17
|
+
| <!-- term: reason:needs_reconcile --> `needs_reconcile` | Reconcile reason for ambiguous, contradictory, or unsupported state. |
|
|
18
|
+
|
|
5
19
|
## Genuine stop conditions
|
|
6
20
|
|
|
7
|
-
| Condition | Strategy | Behavior |
|
|
8
|
-
|
|
9
|
-
| `blocked` lifecycle state | all |
|
|
10
|
-
| `done` lifecycle state | all |
|
|
11
|
-
| `approval_ready` without explicit merge
|
|
12
|
-
| `merge_ready` without explicit merge
|
|
13
|
-
| `autonomy.humanMergeOnly: true` | all |
|
|
14
|
-
| Ambiguous
|
|
15
|
-
| Missing authoritative startup inputs | `dev-loop` |
|
|
21
|
+
| Rule ID | Condition | Strategy | Behavior |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| <!-- rule: STOP-BLOCKED-001 --> `STOP-BLOCKED-001` | `blocked` lifecycle state | all | The loop MUST stop for a human decision. |
|
|
24
|
+
| <!-- rule: STOP-DONE-001 --> `STOP-DONE-001` | `done` lifecycle state | all | The loop MUST treat the state as terminal. |
|
|
25
|
+
| <!-- rule: STOP-APPROVAL-001 --> `STOP-APPROVAL-001` | `approval_ready` without explicit merge authorization | `final_approval` | The loop MUST stop at the approval gate. |
|
|
26
|
+
| <!-- rule: STOP-MERGE-AUTH-001 --> `STOP-MERGE-AUTH-001` | `merge_ready` without explicit merge authorization | all | The loop MUST stop at `waiting_for_merge_authorization`. |
|
|
27
|
+
| <!-- rule: STOP-HUMAN-MERGE-001 --> `STOP-HUMAN-MERGE-001` | `autonomy.humanMergeOnly: true` | all | The loop MUST stop at merge for human action even with explicit merge authorization; the agent MUST NOT run `gh pr merge` (see [Merge preconditions](merge-preconditions.md)). |
|
|
28
|
+
| <!-- rule: STOP-RECONCILE-001 --> `STOP-RECONCILE-001` | Ambiguous or contradictory state | all | The loop MUST fail closed to `needs_reconcile`. |
|
|
29
|
+
| <!-- rule: STOP-STARTUP-INPUTS-001 --> `STOP-STARTUP-INPUTS-001` | Missing authoritative startup inputs | `dev-loop` | The loop MUST fail closed. |
|
|
16
30
|
|
|
17
31
|
## Non-stop conditions
|
|
18
32
|
|
|
19
|
-
| Condition | Strategy | Behavior |
|
|
20
|
-
|
|
21
|
-
| `waiting` lifecycle state | `wait_watch` |
|
|
22
|
-
| `waiting_for_initial_copilot_implementation` | `issue_intake` |
|
|
23
|
-
| `waiting_for_copilot_review` | `copilot_pr_followup` |
|
|
24
|
-
| Quiet watcher observations | `wait_watch` |
|
|
33
|
+
| Rule ID | Condition | Strategy | Behavior |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| <!-- rule: STOP-WAIT-001 --> `STOP-WAIT-001` | `waiting` lifecycle state | `wait_watch` | The loop MUST treat this as a healthy wait and re-dispatch from the main session. |
|
|
36
|
+
| <!-- rule: STOP-INITIAL-COPILOT-001 --> `STOP-INITIAL-COPILOT-001` | `waiting_for_initial_copilot_implementation` | `issue_intake` | The loop MUST use the bootstrap wait with a one-hour watch budget. |
|
|
37
|
+
| <!-- rule: STOP-COPILOT-REVIEW-001 --> `STOP-COPILOT-REVIEW-001` | `waiting_for_copilot_review` | `copilot_pr_followup` | The loop MUST treat this as a continuation boundary, not completion. |
|
|
38
|
+
| <!-- rule: STOP-QUIET-WATCHER-001 --> `STOP-QUIET-WATCHER-001` | Quiet watcher observations | `wait_watch` | The loop MUST treat quiet observations as observational only and MUST NOT surface them as stops by themselves. |
|
|
25
39
|
|
|
26
40
|
## Cross-references
|
|
27
41
|
|
|
28
42
|
- [Confirmation rules](confirmation-rules.md)
|
|
29
43
|
- [Merge preconditions](merge-preconditions.md)
|
|
30
44
|
- [Public Dev Loop Contract](public-dev-loop-contract.md)
|
|
45
|
+
- [Contract style guide](contract-style-guide.md)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Tracker-First Story-to-PR Contract
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
story-to-PR workflow in `dev-loops
|
|
3
|
+
Canonical owner for the adapter-agnostic MVP contract for the tracker-first
|
|
4
|
+
story-to-PR workflow in `dev-loops`, and for the tracker-first loop state
|
|
5
|
+
machine (#449).
|
|
5
6
|
|
|
6
7
|
**MVP invariant: one tracker work item → one GitHub PR.**
|
|
7
8
|
|
|
@@ -30,7 +31,8 @@ Implementation for each:
|
|
|
30
31
|
| Epic / PRD reference | Optional linked metadata only; no roll-up or automation in this slice |
|
|
31
32
|
| ADR / RFC reference | Optional linked metadata only; no decision-sync in this slice |
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
<!-- rule: TRACKER-ONE-ACTIVE-PR -->
|
|
35
|
+
For any single tracker work item in scope, there MUST be at most
|
|
34
36
|
one active GitHub PR at a time. Multi-PR workflows and roll-up automation
|
|
35
37
|
are out of scope for this slice.
|
|
36
38
|
|
|
@@ -57,6 +59,9 @@ already-correct PR leaves it unchanged.
|
|
|
57
59
|
|
|
58
60
|
### 3.1 Required PR Metadata
|
|
59
61
|
|
|
62
|
+
<!-- rule: TRACKER-PROJECTION-REQUIRED-METADATA -->
|
|
63
|
+
Every projected PR MUST carry the following fields:
|
|
64
|
+
|
|
60
65
|
| Field | Rule |
|
|
61
66
|
|---|---|
|
|
62
67
|
| PR title | `[{TRACKER_ID}] {tracker item title}` — bracketed tracker identifier followed by the item title verbatim |
|
|
@@ -64,7 +69,7 @@ already-correct PR leaves it unchanged.
|
|
|
64
69
|
| PR body — Summary section | Required. Brief description of the change implemented in this PR. |
|
|
65
70
|
| PR body — Acceptance Criteria section | Required. Copied or linked from the tracker item's acceptance criteria. |
|
|
66
71
|
| Labels | Required: `tracker:{TRACKER_ID}`. Optional reference labels: `epic:{EPIC_ID}`, `prd:{PRD_ID}` when references are present. |
|
|
67
|
-
| Draft state | PR
|
|
72
|
+
| Draft state | PR MUST start as a draft. It MUST NOT be marked ready-for-review until development work is complete. |
|
|
68
73
|
|
|
69
74
|
### 3.2 Optional Reference Metadata
|
|
70
75
|
|
|
@@ -85,9 +90,9 @@ They are read-only metadata in this slice and do not trigger any automation:
|
|
|
85
90
|
2. **On tracker item field change**: Re-apply projection rules to PR title,
|
|
86
91
|
body, and labels. Leave any sections not covered by projection rules
|
|
87
92
|
(e.g. reviewer-added review comments) intact.
|
|
88
|
-
3. **Idempotent regeneration**:
|
|
89
|
-
sections, and labels
|
|
90
|
-
|
|
93
|
+
3. <!-- rule: TRACKER-PROJECTION-IDEMPOTENT --> **Idempotent regeneration**: Projection MUST be a no-op when the PR already has the correct
|
|
94
|
+
title, body sections, and labels — re-applying the rules to an already-correct PR MUST NOT
|
|
95
|
+
mutate it.
|
|
91
96
|
|
|
92
97
|
### 3.4 PR Body Template
|
|
93
98
|
|
|
@@ -120,7 +125,7 @@ implementations map canonical action names to tracker-native field updates.
|
|
|
120
125
|
| `pr_merged` — PR merged | `set_done` | Tracker moves to done/completed terminal state |
|
|
121
126
|
| `pr_closed_unmerged` — PR closed without merge | `none` | No automatic terminal transition; human decision required |
|
|
122
127
|
| `no_tracker_item` | `none` | No tracker item to update |
|
|
123
|
-
| `blocked_needs_user_decision` | `none` |
|
|
128
|
+
| <!-- rule: TRACKER-BLOCKED-FAIL-CLOSED --> `blocked_needs_user_decision` | `none` | An unexpected or contradictory snapshot MUST stop and report; the helper MUST NOT apply an automatic tracker update |
|
|
124
129
|
|
|
125
130
|
### 4.2 Event Triggers
|
|
126
131
|
|
|
@@ -96,6 +96,38 @@ to match `UI_E2E_CHECK_NAMES` so the gate can read a real signal:
|
|
|
96
96
|
So a deck- or article-only PR has a CI check that can actually satisfy the gate;
|
|
97
97
|
without these jobs such a PR would fail closed with no satisfiable signal.
|
|
98
98
|
|
|
99
|
+
## Verifying a UI change: render and look, but don't persist ad-hoc checks
|
|
100
|
+
|
|
101
|
+
When you change a rendered artifact's layout, **verify it visually before merging** —
|
|
102
|
+
render the built page and inspect real geometry (`getBoundingClientRect` for
|
|
103
|
+
edges/widths, a full-page screenshot to actually look at it) at desktop and mobile
|
|
104
|
+
widths. This intermediary e2e pass is a required *verification strategy*: a layout
|
|
105
|
+
claim ("the columns align now") is only credible once measured against the rendered
|
|
106
|
+
page, not the source CSS.
|
|
107
|
+
|
|
108
|
+
It is **not** something to persist into the codebase. The registered, always-on
|
|
109
|
+
coverage is the shared fit/CSP suite (`defineArticleSuite`/`defineDeckSuite`); do
|
|
110
|
+
not grow it with one-off pixel/alignment assertions written to confirm a single
|
|
111
|
+
fix. Use a throwaway script to render, measure, and screenshot while iterating,
|
|
112
|
+
then drop it. Adding a bespoke geometry assertion per fix bloats the suite and
|
|
113
|
+
couples it to incidental layout numbers.
|
|
114
|
+
|
|
115
|
+
## An assertion must be able to fail on the defect
|
|
116
|
+
|
|
117
|
+
A UI assertion that is **true for the broken state validates the bug**. Real
|
|
118
|
+
example (the layout regression this rule comes from): a check asserted
|
|
119
|
+
`margin-left > 1px` on the prose to prove it was "centered" — but the visible bug
|
|
120
|
+
*was* the prose floating in a narrow centered measure, misaligned from the
|
|
121
|
+
full-width boxes beside it. `margin-left` was large in exactly the broken state, so
|
|
122
|
+
the check passed green on every gate while the page rendered visibly broken.
|
|
123
|
+
|
|
124
|
+
Before trusting any UI assertion, confirm it **fails on the defect**: run it against
|
|
125
|
+
the broken layout, or inject the defect (e.g. shove one block's left edge) and check
|
|
126
|
+
that the assertion goes red. An assertion that cannot fail on the bug it names is
|
|
127
|
+
not coverage — it is false confidence. Prefer asserting the invariant that actually
|
|
128
|
+
defines "correct" (e.g. all content blocks share one left edge within tolerance)
|
|
129
|
+
over a proxy that happens to correlate with it.
|
|
130
|
+
|
|
99
131
|
## Non-goals
|
|
100
132
|
|
|
101
133
|
Not always-on screenshot testing; not mandatory multi-browser. The criterion is
|
|
@@ -4,9 +4,8 @@ Canonical owner for validation requirements across all workflow families.
|
|
|
4
4
|
|
|
5
5
|
## Default validation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
- At minimum: `npm test && npm run test:dev-loop`
|
|
7
|
+
<!-- rule: VALIDATE-VERIFY-BEFORE-GATE -->
|
|
8
|
+
`VALIDATE-VERIFY-BEFORE-GATE`: `npm run verify` is the default repo-level local validation path and MUST pass before PR creation, gate entry, and merge; at minimum this means `npm test && npm run test:dev-loop`.
|
|
10
9
|
|
|
11
10
|
## Gate-specific requirements
|
|
12
11
|
|
|
@@ -17,8 +16,8 @@ Canonical owner for validation requirements across all workflow families.
|
|
|
17
16
|
|
|
18
17
|
## Coverage requirements
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
-
|
|
19
|
+
<!-- rule: VALIDATE-COVERAGE-THRESHOLD -->
|
|
20
|
+
`VALIDATE-COVERAGE-THRESHOLD`: Changed files MUST have ≥90% coverage for lines, statements, functions, and branches, and non-trivial logic MUST be test-first.
|
|
22
21
|
|
|
23
22
|
## Cross-references
|
|
24
23
|
|
|
@@ -17,6 +17,35 @@ template.
|
|
|
17
17
|
| Gate state (detectors) + strategy defaults | `currentGate`, `worktreeRequired` |
|
|
18
18
|
| Settings (`.devloops` at repo root + `defaults.yaml`) | `gateConfig`, `stopRules`, `asyncStartMode`, `requireDraftFirst`, `maxCopilotRounds` |
|
|
19
19
|
| Gate state (detectors) | `currentHeadSha`, `ciStatus`, `unresolvedThreadCount`, `copilotRoundCount` |
|
|
20
|
+
| Canonical sanctioned-command map (`scripts/loop/sanctioned-commands.mjs`) | `sanctionedCommands` |
|
|
21
|
+
|
|
22
|
+
## Sanctioned commands (MANDATORY DEFAULT — issue #1081)
|
|
23
|
+
|
|
24
|
+
`sanctionedCommands` is a **mandatory default** element of every handoff
|
|
25
|
+
envelope. It is the operation → wrapper command map (which wrapper performs
|
|
26
|
+
which GitHub/loop operation), plus the forbidden and orchestrator-owned lists.
|
|
27
|
+
The `loop build-envelope` CLI injects it into every emitted envelope, so a
|
|
28
|
+
spawned dev-loop subagent receives it verbatim without the briefer adding it —
|
|
29
|
+
no re-deriving which wrapper does `gh pr ready` etc.
|
|
30
|
+
|
|
31
|
+
The **single source of truth** is `scripts/loop/sanctioned-commands.mjs` (a
|
|
32
|
+
frozen data module). `@dev-loops/core` stays consumer-agnostic: it defines the
|
|
33
|
+
envelope SHAPE and carries whatever `sanctionedCommands` object the consumer
|
|
34
|
+
supplies; the repo-specific `scripts/...` paths live only in the consumer
|
|
35
|
+
module. Do not duplicate the map into prose — reference the module.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
sanctionedCommands?: {
|
|
39
|
+
reads: Record<string, string>; // operation → wrapper path (some also accept `loop info`)
|
|
40
|
+
edits: Record<string, string>;
|
|
41
|
+
lifecycle: Record<string, string>;
|
|
42
|
+
forbidden: string[]; // raw `gh pr view/checks/edit`, `node -e`, `python -c`, transcript tailing, sleep-poll loops
|
|
43
|
+
orchestratorOwned: string[]; // `gh pr merge`, board status transitions — never done by a subagent
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
A contract test (`test/contracts/sanctioned-commands-exist.test.mjs`) asserts
|
|
48
|
+
every mapped wrapper exists on disk and fails closed if one is renamed/removed.
|
|
20
49
|
|
|
21
50
|
## Acceptance templates
|
|
22
51
|
|
|
@@ -111,6 +140,15 @@ interface HandoffEnvelope {
|
|
|
111
140
|
scopeConstraint?: string;
|
|
112
141
|
customStopAt?: string;
|
|
113
142
|
};
|
|
143
|
+
|
|
144
|
+
// Mandatory default (issue #1081). Source: scripts/loop/sanctioned-commands.mjs
|
|
145
|
+
sanctionedCommands?: {
|
|
146
|
+
reads: Record<string, string>;
|
|
147
|
+
edits: Record<string, string>;
|
|
148
|
+
lifecycle: Record<string, string>;
|
|
149
|
+
forbidden: string[];
|
|
150
|
+
orchestratorOwned: string[];
|
|
151
|
+
};
|
|
114
152
|
}
|
|
115
153
|
```
|
|
116
154
|
|
|
@@ -121,6 +159,7 @@ interface HandoffEnvelope {
|
|
|
121
159
|
3. Execute `nextAction`.
|
|
122
160
|
4. Respect `stopRules` — do not proceed past a gated stop point without authorization.
|
|
123
161
|
5. Use `acceptance` to self-validate before declaring completion.
|
|
162
|
+
6. Use `sanctionedCommands` as the authoritative operation → wrapper map: never call a raw `gh`/`node -e`/`python -c` for an operation the map covers, and never perform an `orchestratorOwned` action.
|
|
124
163
|
|
|
125
164
|
## Backward compatibility
|
|
126
165
|
|