dev-loops 0.4.0 → 0.5.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.
- package/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/agents/dev-loop.md +1 -1
- package/.claude/agents/refiner.md +1 -0
- package/.claude/skills/dev-loop/SKILL.md +16 -6
- package/.claude/skills/dev-loop/templates/slides-story-review.md +54 -0
- package/.claude/skills/docs/artifact-authority-contract.md +86 -31
- package/.claude/skills/docs/local-planning-flow.md +63 -0
- package/.claude/skills/docs/local-planning-worked-example.md +139 -0
- package/.claude/skills/docs/merge-preconditions.md +35 -0
- package/.claude/skills/docs/plan-file-contract.md +37 -0
- package/.claude/skills/docs/retrospective-checkpoint-contract.md +55 -7
- package/.claude/skills/docs/spike-mode-contract.md +237 -0
- package/CHANGELOG.md +43 -0
- package/README.md +1 -0
- package/agents/refiner.agent.md +1 -0
- package/package.json +5 -3
- package/scripts/github/capture-review-threads.mjs +20 -2
- package/scripts/github/probe-copilot-review.mjs +69 -3
- package/scripts/github/upsert-checkpoint-verdict.mjs +18 -3
- package/scripts/lib/jq-output.mjs +297 -0
- package/scripts/loop/check-retro-tooling.mjs +246 -0
- package/scripts/loop/copilot-pr-handoff.mjs +21 -3
- package/scripts/loop/detect-pr-gate-coordination-state.mjs +35 -2
- package/scripts/loop/docs-grill-contract.mjs +70 -0
- package/scripts/loop/info.mjs +21 -2
- package/scripts/loop/pr-runner-coordination.mjs +20 -4
- package/scripts/loop/resolve-dev-loop-startup.mjs +176 -5
- package/scripts/loop/resolve-pr-conflicts.mjs +357 -0
- package/scripts/loop/run-watch-cycle.mjs +77 -3
- package/scripts/loop/slides-story-review-contract.mjs +123 -0
- package/scripts/pages/build-site.mjs +136 -0
- package/scripts/projects/add-queue-item.mjs +12 -2
- package/scripts/projects/list-queue-items.mjs +12 -2
- package/scripts/projects/move-queue-item.mjs +12 -2
- package/scripts/refine/_refine-helpers.mjs +20 -0
- package/scripts/refine/exit-spike.mjs +186 -0
- package/scripts/refine/promote-plan.mjs +387 -0
- package/scripts/refine/refine-plan-file.mjs +165 -0
- package/scripts/refine/validate-plan-file.mjs +64 -0
- package/scripts/refine/validate-spike-file.mjs +87 -0
- package/skills/dev-loop/SKILL.md +11 -1
- package/skills/dev-loop/templates/slides-story-review.md +54 -0
- package/skills/docs/artifact-authority-contract.md +86 -31
- package/skills/docs/local-planning-flow.md +63 -0
- package/skills/docs/local-planning-worked-example.md +139 -0
- package/skills/docs/merge-preconditions.md +35 -0
- package/skills/docs/plan-file-contract.md +37 -0
- package/skills/docs/retrospective-checkpoint-contract.md +55 -7
- package/skills/docs/spike-mode-contract.md +237 -0
|
@@ -22,7 +22,7 @@ The envelope is the primary handoff artifact — it is derived from resolver out
|
|
|
22
22
|
|
|
23
23
|
**Construction sequence:**
|
|
24
24
|
|
|
25
|
-
1. Run the deterministic startup resolver to produce the authoritative state bundle: `npx dev-loops@0.
|
|
25
|
+
1. Run the deterministic startup resolver to produce the authoritative state bundle: `npx dev-loops@0.5.0 loop startup --issue <n>` for issues, or `npx dev-loops@0.5.0 loop startup --pr <n>` for PRs.
|
|
26
26
|
2. Pass the resolver output, resolved settings (merged from `.devloops` and `.pi/dev-loop/defaults.yaml`), and current gate state to `buildDevLoopHandoffEnvelope()`.
|
|
27
27
|
3. **Validate the envelope** with `validateHandoffEnvelope()` before consuming any field. If validation returns `ok: false`, reject the handoff with the structured error — do not load requiredReads, do not execute nextAction, do not delegate.
|
|
28
28
|
4. Read the envelope as the first artifact.
|
|
@@ -44,6 +44,7 @@ For the active phase, require and produce:
|
|
|
44
44
|
- Do not invent audit findings when no audit artifact was provided
|
|
45
45
|
- when the phase includes watcher or predicate-driven behavior: explicit timeout semantics and negative-case expectations for non-target identities/events
|
|
46
46
|
- when the phase relies on package-first shared helpers inside a source-loaded workspace: explicit integration expectations about whether local callers use published package imports or a thin source/workspace adapter during development
|
|
47
|
+
- cross-check the phase's claims against the contracts and docs they reference (the autonomous docs-grill step, see ../docs/docs-grill-step.md): surface code-vs-doc drift, stale references, and contract-surface inaccuracies as refinement findings while the claims are still being verified
|
|
47
48
|
|
|
48
49
|
## Working style
|
|
49
50
|
- Prefer parallel fresh-context fan-out/fan-in when it improves refinement quality or surfaces materially different variants.
|
|
@@ -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.
|
|
28
|
+
Resolve authoritative state via the startup resolver (`npx dev-loops@0.5.0 loop startup --issue <n>` for issues, `npx dev-loops@0.5.0 loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops@0.5.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,10 +99,20 @@ 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.
|
|
103
|
-
- `npx dev-loops@0.
|
|
104
|
-
- `npx dev-loops@0.
|
|
105
|
-
- `npx dev-loops@0.
|
|
102
|
+
Info/handoff requests can be served directly via `npx dev-loops@0.5.0 loop info` (read-only; no full dev-loop run required):
|
|
103
|
+
- `npx dev-loops@0.5.0 loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
|
|
104
|
+
- `npx dev-loops@0.5.0 loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
|
|
105
|
+
- `npx dev-loops@0.5.0 loop info --issue <n> --json` — machine-readable JSON output
|
|
106
|
+
|
|
107
|
+
## Reading tool output (token-economical convention)
|
|
108
|
+
|
|
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
|
+
|
|
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`.
|
|
113
|
+
3. **`--jq <filter>` to extract a single field.** The `--jq`-wired scripts (`probe-copilot-review.mjs`, `capture-review-threads.mjs`, `upsert-checkpoint-verdict.mjs`, the `scripts/projects/*` queue scripts) accept a gh-style `--jq` filter (jq subset: field access, `.[]`/`.[N]`, `|`, `select(...)`, `==`/`!=`/`<`/`<=`/`>`/`>=`, `length`, `keys`). It prints only the filtered value. An invalid filter fails closed (stderr + exit `2`), distinct from a clean predicate-false (silent exit `1`).
|
|
114
|
+
4. **`gh … --jq` on a raw `gh` call** when no dev-loops script covers it.
|
|
115
|
+
5. **NEVER `| python3` or `node -e`** to parse tool JSON. If a field you need is missing from a script's output, add it to the script (or its concise mode), not an inline parser.
|
|
106
116
|
|
|
107
117
|
## Guard rules
|
|
108
118
|
|
|
@@ -114,7 +124,7 @@ Info/handoff requests can be served directly via `npx dev-loops@0.4.0 loop info`
|
|
|
114
124
|
|
|
115
125
|
**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`.
|
|
116
126
|
|
|
117
|
-
**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 — never re-request after the cap.
|
|
127
|
+
**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 — never re-request after 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`.
|
|
118
128
|
|
|
119
129
|
## Shorthand issue-based auto trigger contract
|
|
120
130
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Slides content & storytelling review prompt template
|
|
2
|
+
|
|
3
|
+
Use this template when running the bounded slides-story reviewer mode behind `dev-loop`.
|
|
4
|
+
|
|
5
|
+
You are a public-audience storytelling reviewer judging a deck's **narrative**, not its pixels. The visual designer/vision loop owns layout, spacing, and contrast; you own arc, message, sequencing, and audience fit.
|
|
6
|
+
|
|
7
|
+
## Inputs
|
|
8
|
+
|
|
9
|
+
- `acceptanceCriteria`: required list of slice-level acceptance criteria the review is judging
|
|
10
|
+
- `storytellingBrief`: required short focus brief (audience, intended takeaway, what to watch for)
|
|
11
|
+
- `deckBundle.deckSourcePath`: required path to the deck source (e.g. the Slidev `.md`)
|
|
12
|
+
- `deckBundle.slideScreenshots[]`: optional captured slides from the UI smoke harness
|
|
13
|
+
- `slideId`
|
|
14
|
+
- `screenshotPath`
|
|
15
|
+
|
|
16
|
+
## Review lens (public audience)
|
|
17
|
+
|
|
18
|
+
1. **Arc**: hook → tension → resolution. Does slide 1 make a stranger care?
|
|
19
|
+
2. **One message per slide**: each slide has a single takeaway; titles state the claim, not the topic.
|
|
20
|
+
3. **Sequencing**: order builds understanding; no forward references; jargon introduced before it is used.
|
|
21
|
+
4. **Audience calibration**: a public/non-insider reader can follow it — internal enum names, state-machine identifiers, and pills are translated or earn their keep, not dumped raw.
|
|
22
|
+
5. **Cut / merge / reorder**: explicit recommendations — which slides to drop, combine, or move.
|
|
23
|
+
6. **Close**: a memorable takeaway, not a feature list.
|
|
24
|
+
|
|
25
|
+
## Review policy
|
|
26
|
+
|
|
27
|
+
1. Fail closed on any missing, ambiguous, or unreadable required input — do not guess the narrative.
|
|
28
|
+
2. Ground every finding in a specific `slideId` (and a `screenshotPath` when available).
|
|
29
|
+
3. Return only deterministic findings tied to the brief and acceptance criteria; do not invent content.
|
|
30
|
+
|
|
31
|
+
## Required output format
|
|
32
|
+
|
|
33
|
+
Allowed enum values:
|
|
34
|
+
- `outcome`: `"story_review_satisfied"` | `"needs_iteration"`
|
|
35
|
+
- `severity`: `"high"` | `"medium"` | `"low"`
|
|
36
|
+
|
|
37
|
+
Return strict JSON with this shape (example uses concrete values):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"outcome": "needs_iteration",
|
|
42
|
+
"summary": "short overall verdict on whether the deck lands",
|
|
43
|
+
"findings": [
|
|
44
|
+
{
|
|
45
|
+
"severity": "high",
|
|
46
|
+
"slideId": "hero",
|
|
47
|
+
"problem": "what is wrong with the narrative on this slide and why it fails the public audience",
|
|
48
|
+
"correctiveAction": "what to change next (cut/merge/reorder/reword)"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
When `outcome` is `"needs_iteration"`, `findings` must be non-empty.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Artifact authority contract
|
|
2
2
|
|
|
3
|
-
This document is the canonical authority for the artifact-selection model:
|
|
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).
|
|
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
|
|
|
@@ -8,9 +8,11 @@ Other repo docs may summarize or link this contract, but they should not redefin
|
|
|
8
8
|
|
|
9
9
|
## Two-tier model
|
|
10
10
|
|
|
11
|
-
dev-loops supports two mutually exclusive artifact authority modes. Every work item
|
|
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.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The shipped extension default selects local-planning; see [Shipped default posture](#shipped-default-posture) below. The mode names that follow describe the two tiers; "default" in their headings refers to the github-first code-level fallback in `BUILT_IN_DEFAULTS`, which the shipped extension layer overrides to local-first.
|
|
14
|
+
|
|
15
|
+
### Tracker-first
|
|
14
16
|
|
|
15
17
|
**GitHub issues are the authoritative artifact store.** Work originates from a GitHub issue. A linked PR is the execution artifact. GitHub is the canonical source of truth for issue identity, acceptance criteria, scope, and lifecycle state.
|
|
16
18
|
|
|
@@ -25,12 +27,12 @@ Key contract:
|
|
|
25
27
|
- When an open linked PR exists, reuse it rather than opening another
|
|
26
28
|
- Implementation may proceed through either the GitHub-first routed path or the local implementation strategy (see [Public Dev Loop Contract](public-dev-loop-contract.md) `targetPreference`)
|
|
27
29
|
|
|
28
|
-
### Local-planning
|
|
30
|
+
### Local-planning
|
|
29
31
|
|
|
30
|
-
**Persisted markdown plan files are the authoritative artifact store.** Work originates from a markdown plan file
|
|
32
|
+
**Persisted markdown plan files are the authoritative artifact store.** Work originates from a local markdown plan file in the repo working tree, and no GitHub issue is required. The plan file stays uncommitted through authoring, refinement, and local review; promotion is the step that commits it (the helper sequence commits it as part of opening the PR). GitHub PRs carry review and merge while the plan file stays the canonical spec.
|
|
31
33
|
|
|
32
34
|
Artifacts:
|
|
33
|
-
- **Planning artifact:** Persisted markdown plan file (e.g., `docs/phases/phase-<n>.md`)
|
|
35
|
+
- **Planning artifact:** Persisted markdown plan file (e.g., `docs/phases/phase-<n>.md`); its format and required base sections are defined in the [Plan-file Contract](plan-file-contract.md)
|
|
34
36
|
- **Execution artifact:** Local branch and associated GitHub PR (created during implementation)
|
|
35
37
|
- **No GitHub issue:** The plan file replaces the issue as the canonical spec
|
|
36
38
|
|
|
@@ -41,10 +43,10 @@ Key contract:
|
|
|
41
43
|
|
|
42
44
|
### Mode selection table
|
|
43
45
|
|
|
44
|
-
| Mode | Canonical artifact | GitHub issue required | Settings
|
|
46
|
+
| Mode | Canonical artifact | GitHub issue required | Settings value |
|
|
45
47
|
|---|---|---|---|
|
|
46
|
-
| Tracker-first
|
|
47
|
-
| Local-planning (
|
|
48
|
+
| Tracker-first | GitHub issue | Yes | `strategy.default: github-first` |
|
|
49
|
+
| Local-planning (shipped default) | Markdown plan file | No | `strategy.default: local-first` |
|
|
48
50
|
|
|
49
51
|
`inputSource.default` further disambiguates local-first startup:
|
|
50
52
|
| inputSource | Meaning |
|
|
@@ -54,62 +56,115 @@ Key contract:
|
|
|
54
56
|
|
|
55
57
|
## Settings mechanism
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
The repo's default artifact-authority posture is declared by `strategy.default`, set in `.devloops` at repo root and resolved against the layered config defaults:
|
|
58
60
|
|
|
59
61
|
```yaml
|
|
60
62
|
# .devloops
|
|
61
63
|
strategy:
|
|
62
|
-
default:
|
|
63
|
-
# default:
|
|
64
|
+
default: local-first # local-planning (markdown plan file)
|
|
65
|
+
# default: github-first # tracker-first (GitHub issue required)
|
|
64
66
|
inputSource:
|
|
65
67
|
default: tracker # spec source for local-first: tracker (issue body) or phase-docs
|
|
66
68
|
```
|
|
67
69
|
|
|
68
|
-
The `strategy.default` key
|
|
69
|
-
1. It
|
|
70
|
-
2. It sets the
|
|
70
|
+
The `strategy.default` key carries two jobs:
|
|
71
|
+
1. It declares the repo's default artifact-authority posture (local-planning under `local-first`, tracker-first under `github-first`).
|
|
72
|
+
2. It sets the routing preference (`targetPreference`) in dev-loop startup — `prefer_local` under `local-first`, `prefer_github_first` under `github-first`.
|
|
73
|
+
|
|
74
|
+
The authoritative artifact for a given run is selected by the explicit startup input. `scripts/loop/resolve-dev-loop-startup.mjs` takes `--issue` / `--pr` / `--input` / `--plan-file` (mutually exclusive), and `strategy.default` supplies the default routing preference; it does not force the artifact per invocation.
|
|
71
75
|
|
|
72
76
|
The `inputSource.default` key disambiguates local-first startup:
|
|
73
|
-
- `tracker` (default): local agent implements from the GitHub issue body; the issue is canonical spec
|
|
74
|
-
- `phase-docs`: local agent implements from persisted phase docs; no tracker issue required
|
|
77
|
+
- `tracker` (default): the local agent implements from the GitHub issue body; the issue is the canonical spec
|
|
78
|
+
- `phase-docs`: the local agent implements from persisted phase docs; no tracker issue required
|
|
79
|
+
|
|
80
|
+
### Shipped default posture
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
The effective default for a consumer comes from the config-merge layering in `packages/core/src/config/config.mjs`. Precedence, low to high:
|
|
77
83
|
|
|
78
|
-
|
|
84
|
+
1. `BUILT_IN_DEFAULTS` (frozen in `config.mjs`) — `strategy.default: github-first`. This is the code-level fallback when no other layer sets the key.
|
|
85
|
+
2. Extension-packaged defaults (`packages/core/src/config/extension-defaults.yaml`, loaded as the `extensionDefaults` layer) — `strategy.default: local-first`. This is the opinion the package ships and the layer that wins over the built-in fallback.
|
|
86
|
+
3. Repo-local `.pi/dev-loop/defaults.*` (legacy) — applied when present.
|
|
87
|
+
4. Repo `.devloops` at repo root — the per-repo override, highest precedence. When `.devloops` is absent, the legacy `.pi/dev-loop/settings.*` / `overrides.*` apply at this position instead.
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
With nothing but the shipped package in place, the extension layer resolves `strategy.default` to `local-first`, so the shipped default posture is local-planning (epic #947, decision #7). A repo opts back into tracker-first by setting `strategy.default: github-first` in its own `.devloops`.
|
|
90
|
+
|
|
91
|
+
Two legacy repo-local layers also exist under `.pi/dev-loop/` (the package no longer ships a `.pi/dev-loop/defaults.yaml`). They differ in how they load: `.pi/dev-loop/defaults.*` is always applied when present, between the extension defaults and `.devloops`; `.pi/dev-loop/settings.*` (and the older `overrides.*`) load only when no `.devloops` is present — when `.devloops` exists it is authoritative and those files are ignored (with a deprecation warning). The full precedence, low to high, is: `BUILT_IN_DEFAULTS` < extension defaults < `.pi/dev-loop/defaults.*` < repo `.devloops` (or, when `.devloops` is absent, `.pi/dev-loop/settings.*` / `overrides.*`).
|
|
82
92
|
|
|
83
93
|
### Explicit non-knobs
|
|
84
94
|
|
|
85
95
|
These are not valid artifact authority mode selectors:
|
|
86
|
-
- `strategy.default: copilot` — not a valid mode;
|
|
96
|
+
- `strategy.default: copilot` — not a valid mode; the enum accepts only `github-first` or `local-first` (`packages/core/src/config/config.mjs`)
|
|
87
97
|
- Free-form string values — fail closed
|
|
88
|
-
- Omitting `strategy.default`
|
|
98
|
+
- Omitting `strategy.default` from every layer — resolves to `github-first` from `BUILT_IN_DEFAULTS`; with the shipped extension layer present it resolves to `local-first`
|
|
99
|
+
|
|
100
|
+
## Local-first plan-file flow end to end
|
|
101
|
+
|
|
102
|
+
Under local-planning, one plan file moves through four stages. Each stage has a shipped helper script; the start, refine, and promote stages also expose their pure logic as an `@dev-loops/core` contract, while the validate stage's `validatePlanFile` lives in its helper script (`scripts/refine/validate-plan-file.mjs`). The [Local-Planning Flow](local-planning-flow.md) skill doc walks the same sequence as operator steps, and the [Local-Planning Worked Example](local-planning-worked-example.md) shows one plan file evolving through every stage.
|
|
103
|
+
|
|
104
|
+
### P1 — Plan-file artifact + config (#949)
|
|
105
|
+
|
|
106
|
+
The plan file is a phase-doc-format markdown document. Its directory is `localPlanning.plansDir`, which defaults to `docs/phases/` (built-in default in `config.mjs`, mirrored in `extension-defaults.yaml`; `resolvePlansDir(config)` resolves it). Its required base authoring sections — `## Status`, `## Objective`, `## In scope`, `## Explicit non-goals` — and the validator `scripts/refine/validate-plan-file.mjs` (`validatePlanFile`, distinct `missing_*` codes per absent or empty section) are defined in the [Plan-file Contract](plan-file-contract.md).
|
|
107
|
+
|
|
108
|
+
### P2 — Intake (#950)
|
|
109
|
+
|
|
110
|
+
`scripts/loop/resolve-dev-loop-startup.mjs` accepts `--plan-file <path>` (mutually exclusive with `--issue`, `--pr`, `--input`). It validates the plan and threads an intake state onto its output. The pure contract `@dev-loops/core/loop/plan-file-intake-contract` (`packages/core/src/loop/plan-file-intake-contract.mjs`) defines `evaluatePlanFileIntakeState` and the three `PLAN_FILE_INTAKE_STATE` values:
|
|
111
|
+
|
|
112
|
+
| State | Meaning |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `new_plan_needs_refinement` | Base sections valid; the refinement sections are not yet present |
|
|
115
|
+
| `plan_refined_ready_for_promotion` | Base sections valid and both `PLAN_FILE_REFINEMENT_SECTIONS` (`Acceptance criteria`, `Definition of done`) present |
|
|
116
|
+
| `ambiguous_fail_closed` | Base sections invalid, or only one refinement section present — the resolver does not route the plan forward |
|
|
117
|
+
|
|
118
|
+
In the CLI, a base-valid plan carrying only one refinement section is reported as `ambiguous_fail_closed` with exit 0 (the operator completes the missing section before refine/promote); a missing/unreadable plan, or one that fails the base-section validator, makes startup exit 1 with no readiness bundle.
|
|
119
|
+
|
|
120
|
+
### P3 — Local refine + review checkpoint (#951)
|
|
121
|
+
|
|
122
|
+
`scripts/refine/refine-plan-file.mjs` drives the refine step; the pure contract `@dev-loops/core/loop/plan-file-refine-contract` (`packages/core/src/loop/plan-file-refine-contract.mjs`) exports `refinePlanFileInPlace`, which writes the refiner payload back into the single canonical plan file in place — the `Acceptance criteria` and `Definition of done` sections, a `Coverage matrix` section (`COVERAGE_MATRIX_HEADING`), and a `Docs-grill findings` section (`DOCS_GRILL_FINDINGS_HEADING`) — then stops at the `local_human_review` checkpoint (`PLAN_FILE_REFINE_STOP.LOCAL_HUMAN_REVIEW`) with the intake state advanced to `plan_refined_ready_for_promotion`. The module performs no GitHub mutation, no network calls, and no filesystem I/O; the caller reads and writes the plan file. The docs-grill runs as a step within refinement: the CLI classifies each finding with `classifyDocsGrillFinding` (`scripts/loop/docs-grill-contract.mjs`) and the contract records the dispositions. See the [Docs-Grill Step](../../docs/docs-grill-step.md).
|
|
123
|
+
|
|
124
|
+
### P4 — Promotion + authority transfer (#952)
|
|
125
|
+
|
|
126
|
+
`scripts/refine/promote-plan.mjs` promotes a refined plan; the pure contract `@dev-loops/core/loop/plan-file-promote-contract` (`packages/core/src/loop/plan-file-promote-contract.mjs`) exports `evaluatePromoteEligibility` and `buildPromotionPrBody`. Promotion is PR-first: it commits the plan doc and opens exactly one draft PR via the canonical PR wrapper, and mints no GitHub issue. The plan↔PR link is bidirectional — the PR body carries the committed plan-doc path (the spec-of-record) and the plan front-matter carries `prNumber:` (`PLAN_FILE_PR_FRONT_MATTER_KEY`). Promotion is idempotent: a plan that already carries `prNumber` resolves to `already_promoted` (`PLAN_FILE_PROMOTE_ACTION.ALREADY_PROMOTED`) and opens nothing. The optional `prNumber` front-matter and its parser/serializer are described in the [Plan-file Contract](plan-file-contract.md).
|
|
127
|
+
|
|
128
|
+
### P5 — Local-first noise profile (#953)
|
|
129
|
+
|
|
130
|
+
The shipped extension layer pairs local-first with a low-noise posture, in `packages/core/src/config/extension-defaults.yaml`:
|
|
131
|
+
|
|
132
|
+
| Key | Shipped value | Why |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `strategy.default` | `local-first` | The shipped default posture (decision #7) |
|
|
135
|
+
| `autonomy.humanMergeOnly` | `true` | Local-first never auto-merges; a human always merges |
|
|
136
|
+
| `queue.maxAutoFiledIssues` | `1` | Local-first is PR-first, so auto-filing issues is near-zero; a low cap keeps tracker noise minimal |
|
|
137
|
+
| `gates.postFindingsComments` | `true` | Gate findings live on the PR (the spec-of-record and human-review surface) as evidence |
|
|
138
|
+
|
|
139
|
+
These values come from the existing config-merge layering, so no new resolver is involved: `BUILT_IN_DEFAULTS` keeps the github-first posture (`humanMergeOnly: false`, `maxAutoFiledIssues: 10`), and the extension layer sets the local-first values above. A repo `.devloops` can override any of them.
|
|
89
140
|
|
|
90
141
|
## dev-loops own mode
|
|
91
142
|
|
|
92
|
-
dev-loops
|
|
143
|
+
dev-loops runs **local-planning**, set in its repo-root `.devloops`.
|
|
93
144
|
|
|
94
|
-
- **Mode:**
|
|
95
|
-
- **Settings:** `.
|
|
96
|
-
- **Artifact authority:**
|
|
97
|
-
- **
|
|
98
|
-
- **Why
|
|
145
|
+
- **Mode:** Local-planning
|
|
146
|
+
- **Settings:** repo-root `.devloops` sets `strategy.default: local-first` and `inputSource.default: tracker`
|
|
147
|
+
- **Artifact authority:** the canonical spec for a work item is its plan artifact; the repo dogfoods the same local-first posture the package ships as default
|
|
148
|
+
- **Per-run input:** with `inputSource.default: tracker`, a local-first session can still implement from a GitHub issue body when one is supplied (the issue is the spec source for that run); `phase-docs` switches the source to a committed plan file
|
|
149
|
+
- **Why local-planning:** the repo runs the local-first plan-file flow (plan-file → refine → review → promote) on its own work so the shipped default posture is exercised end to end.
|
|
99
150
|
|
|
100
151
|
## Relationship to other docs
|
|
101
152
|
|
|
102
153
|
| Doc | Relationship |
|
|
103
154
|
|---|---|
|
|
104
155
|
| [Public Dev Loop Contract](public-dev-loop-contract.md) | This contract is the canonical entrypoint; artifact authority contract defines the artifact model it assumes |
|
|
105
|
-
| [
|
|
156
|
+
| [Plan-file Contract](plan-file-contract.md) | Defines the plan-file format (phase-doc format) and its required base sections for local-planning mode |
|
|
157
|
+
| [Local-Planning Flow](local-planning-flow.md) | Operator sequence for the local-first flow: validate → start → refine → promote, naming the shipped helper scripts |
|
|
158
|
+
| [Local-Planning Worked Example](local-planning-worked-example.md) | One plan file shown through every stage, with the file content evolving |
|
|
159
|
+
| [Spike-mode Contract](spike-mode-contract.md) | Time-boxed exploratory runs; a graduated spike emits a plan file that enters this local-planning tier |
|
|
160
|
+
| [Tracker-First Loop State](tracker-first-loop-state.md) | Defines the PR-level state machine for tracker-first PR workflows; that is execution state, separate from artifact authority |
|
|
106
161
|
| [Main Agent Contract](main-agent-contract.md) | Defines the delegation boundary; artifact authority defines which artifacts govern work |
|
|
107
162
|
| AGENTS.md | Repo constitution; cites the work-origin rule and points to this contract |
|
|
108
163
|
| [Dev Loop Skill](../dev-loop/SKILL.md) | Public entrypoint skill; cites the work-origin rule and points to this contract |
|
|
109
164
|
|
|
110
165
|
### Distinction: artifact authority vs tracker-first PR workflow
|
|
111
166
|
|
|
112
|
-
`tracker-first-loop-state.md` defines a state machine for PR lifecycle management when a tracker item (e.g., Shortcut story) drives a GitHub PR.
|
|
167
|
+
`tracker-first-loop-state.md` defines a state machine for PR lifecycle management when a tracker item (e.g., Shortcut story) drives a GitHub PR. It is a **PR-level workflow contract**. The term "tracker-first" there refers to tracker-driven PR state transitions, a separate concern from the artifact authority model this doc defines.
|
|
113
168
|
|
|
114
169
|
## Non-goals
|
|
115
170
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Local-planning flow
|
|
2
|
+
|
|
3
|
+
The operator sequence for the local-first plan-file flow. The [Artifact Authority Contract](artifact-authority-contract.md) owns the model and the per-phase contract details; this doc names the shipped helper scripts in the order they run for one plan file. The [Local-Planning Worked Example](local-planning-worked-example.md) shows a single plan file evolving through these steps.
|
|
4
|
+
|
|
5
|
+
The flow applies under local-planning mode (`strategy.default: local-first`, the shipped default — see the contract's [Shipped default posture](artifact-authority-contract.md#shipped-default-posture)). The plan file lives under `localPlanning.plansDir`, which defaults to `docs/phases/`.
|
|
6
|
+
|
|
7
|
+
## Stages
|
|
8
|
+
|
|
9
|
+
| Stage | Helper script | Pure logic |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Validate | `scripts/refine/validate-plan-file.mjs` | `validatePlanFile` (in the helper script; not a `@dev-loops/core` export) |
|
|
12
|
+
| Start | `scripts/loop/resolve-dev-loop-startup.mjs --plan-file <path>` | `evaluatePlanFileIntakeState` (`@dev-loops/core/loop/plan-file-intake-contract`) |
|
|
13
|
+
| Refine | `scripts/refine/refine-plan-file.mjs` | `refinePlanFileInPlace` (`@dev-loops/core/loop/plan-file-refine-contract`) |
|
|
14
|
+
| Promote | `scripts/refine/promote-plan.mjs` | `evaluatePromoteEligibility` / `buildPromotionPrBody` (`@dev-loops/core/loop/plan-file-promote-contract`) |
|
|
15
|
+
|
|
16
|
+
## 1. Author and validate the plan
|
|
17
|
+
|
|
18
|
+
Author a phase-doc-format plan under `docs/phases/` with the four base sections `## Status`, `## Objective`, `## In scope`, `## Explicit non-goals`. Check it against the base-section contract:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
node scripts/refine/validate-plan-file.mjs --input docs/phases/phase-<n>.md --json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The JSON payload reports `{ checker: "validate-plan-file", ok, errors }`. Each absent or empty-body base section contributes one entry with a distinct `missing_*` code; `ok` is `true` when every base section is present with a non-empty body.
|
|
25
|
+
|
|
26
|
+
## 2. Start the local-planning session
|
|
27
|
+
|
|
28
|
+
Hand the plan to startup with `--plan-file` (mutually exclusive with `--issue`, `--pr`, and `--input`):
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
node scripts/loop/resolve-dev-loop-startup.mjs --plan-file docs/phases/phase-<n>.md
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Startup validates the plan and threads an intake state onto its output. A plan with valid base sections and no refinement sections is `new_plan_needs_refinement`; the same plan once it also carries `Acceptance criteria` and `Definition of done` is `plan_refined_ready_for_promotion`. A base-valid plan that carries only one of the two refinement sections is reported as `ambiguous_fail_closed` with exit 0 and is not routed forward — the operator completes the missing section first. A plan that is missing/unreadable or fails the base validator makes startup fail closed (exit 1, no readiness bundle).
|
|
35
|
+
|
|
36
|
+
## 3. Refine in place and stop at the local human-review checkpoint
|
|
37
|
+
|
|
38
|
+
The refine step writes the refiner output back into the same plan file:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
node scripts/refine/refine-plan-file.mjs --plan-file docs/phases/phase-<n>.md --payload <payload.json>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`refinePlanFileInPlace` appends the `Acceptance criteria` and `Definition of done` sections, a `Coverage matrix` section, and a `Docs-grill findings` section, then advances the intake state to `plan_refined_ready_for_promotion` and stops at the `local_human_review` checkpoint. The docs-grill runs as a step within refinement: each finding is classified with `classifyDocsGrillFinding` (see the [Docs-Grill Step](../../docs/docs-grill-step.md)) and the dispositions are recorded into the plan. The step makes no GitHub or network call; the human reviews the refined plan before anything is promoted.
|
|
45
|
+
|
|
46
|
+
## 4. Promote to a single draft PR
|
|
47
|
+
|
|
48
|
+
Once the human approves the refined plan, promote it:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
node scripts/refine/promote-plan.mjs --plan-file docs/phases/phase-<n>.md
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Promotion is PR-first: it commits the plan doc and opens exactly one draft PR via the canonical PR wrapper, and mints no GitHub issue. It records the plan↔PR link bidirectionally — the PR body references the committed plan-doc path, and the plan's front-matter gains a `prNumber:` entry. The committed plan doc is the spec-of-record; the draft PR enters the standard draft → pre-approval → human-merge flow. Promotion is idempotent: re-running on a plan that already carries `prNumber` resolves to `already_promoted` and opens nothing.
|
|
55
|
+
|
|
56
|
+
## Relationship to other docs
|
|
57
|
+
|
|
58
|
+
| Doc | Relationship |
|
|
59
|
+
|---|---|
|
|
60
|
+
| [Artifact Authority Contract](artifact-authority-contract.md) | Canonical model and per-phase contract details for the local-first flow |
|
|
61
|
+
| [Plan-file Contract](plan-file-contract.md) | Plan-file format, base sections, validator, and the optional `prNumber` front-matter |
|
|
62
|
+
| [Local-Planning Worked Example](local-planning-worked-example.md) | One plan file shown through every stage of this sequence |
|
|
63
|
+
| [Docs-Grill Step](../../docs/docs-grill-step.md) | The in-loop grill that runs as a step within refinement |
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Local-planning worked example
|
|
2
|
+
|
|
3
|
+
One plan file, `docs/phases/phase-42.md`, carried through every stage of the [Local-Planning Flow](local-planning-flow.md): authored, validated, refined in place, held at the local human-review checkpoint, then promoted to a draft PR. Each stage below shows the same file's content as it evolves. The contract details live in the [Artifact Authority Contract](artifact-authority-contract.md); this is a concrete trace of one file.
|
|
4
|
+
|
|
5
|
+
The repo runs local-planning by default (`strategy.default: local-first`), and the plan lives under `localPlanning.plansDir` (`docs/phases/`).
|
|
6
|
+
|
|
7
|
+
## Stage 1 — Authored
|
|
8
|
+
|
|
9
|
+
The operator writes the four base sections. The file at this point:
|
|
10
|
+
|
|
11
|
+
```markdown
|
|
12
|
+
# phase-42 plan
|
|
13
|
+
|
|
14
|
+
## Status
|
|
15
|
+
|
|
16
|
+
draft
|
|
17
|
+
|
|
18
|
+
## Objective
|
|
19
|
+
|
|
20
|
+
Add a `--dry-run` flag to the queue dispatcher so an operator can preview the
|
|
21
|
+
dispatch order without mutating the board.
|
|
22
|
+
|
|
23
|
+
## In scope
|
|
24
|
+
|
|
25
|
+
- Parse `--dry-run` in the queue dispatcher CLI.
|
|
26
|
+
- Print the resolved dispatch order and exit 0 without any board mutation.
|
|
27
|
+
|
|
28
|
+
## Explicit non-goals
|
|
29
|
+
|
|
30
|
+
- Changing the dispatch ordering algorithm.
|
|
31
|
+
- Any change to non-dry-run dispatch behavior.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Validation passes:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
$ node scripts/refine/validate-plan-file.mjs --input docs/phases/phase-42.md --json
|
|
38
|
+
{ "checker": "validate-plan-file", "ok": true, "errors": [] }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Stage 2 — Started (intake state)
|
|
42
|
+
|
|
43
|
+
Startup reads the plan and reports the intake state. With the four base sections present and no refinement sections yet, the state is `new_plan_needs_refinement`:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
$ node scripts/loop/resolve-dev-loop-startup.mjs --plan-file docs/phases/phase-42.md
|
|
47
|
+
# ... startup output carries planFileIntakeState: "new_plan_needs_refinement"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Stage 3 — Refined in place
|
|
51
|
+
|
|
52
|
+
`refine-plan-file.mjs` appends the refinement sections, the coverage matrix, and the recorded docs-grill findings to the same file, then stops at the `local_human_review` checkpoint. The file now reads (added sections shown):
|
|
53
|
+
|
|
54
|
+
```markdown
|
|
55
|
+
# phase-42 plan
|
|
56
|
+
|
|
57
|
+
## Status
|
|
58
|
+
|
|
59
|
+
draft
|
|
60
|
+
|
|
61
|
+
## Objective
|
|
62
|
+
|
|
63
|
+
Add a `--dry-run` flag to the queue dispatcher so an operator can preview the
|
|
64
|
+
dispatch order without mutating the board.
|
|
65
|
+
|
|
66
|
+
## In scope
|
|
67
|
+
|
|
68
|
+
- Parse `--dry-run` in the queue dispatcher CLI.
|
|
69
|
+
- Print the resolved dispatch order and exit 0 without any board mutation.
|
|
70
|
+
|
|
71
|
+
## Explicit non-goals
|
|
72
|
+
|
|
73
|
+
- Changing the dispatch ordering algorithm.
|
|
74
|
+
- Any change to non-dry-run dispatch behavior.
|
|
75
|
+
|
|
76
|
+
## Acceptance criteria
|
|
77
|
+
|
|
78
|
+
- `--dry-run` prints the resolved dispatch order to stdout.
|
|
79
|
+
- `--dry-run` performs zero board mutation (no Projects V2 writes).
|
|
80
|
+
- Exit code is 0 on a successful dry-run preview.
|
|
81
|
+
|
|
82
|
+
## Definition of done
|
|
83
|
+
|
|
84
|
+
- CLI parses `--dry-run`; unit test covers the parse path.
|
|
85
|
+
- A test asserts no board-mutation call fires under `--dry-run`.
|
|
86
|
+
- The queue docs note the flag.
|
|
87
|
+
|
|
88
|
+
## Coverage matrix
|
|
89
|
+
|
|
90
|
+
| Item | Type | Status | Evidence | Notes |
|
|
91
|
+
|---|---|---|---|---|
|
|
92
|
+
| `--dry-run` prints dispatch order | AC | Unverified | pending implementation | |
|
|
93
|
+
| `--dry-run` performs zero mutation | AC | Unverified | pending implementation | |
|
|
94
|
+
| Exit code 0 on preview | AC | Unverified | pending implementation | |
|
|
95
|
+
| No-mutation test | DoD | Unverified | pending test | |
|
|
96
|
+
| Dispatch algorithm unchanged | Non-goal | Unverified | scope boundary | |
|
|
97
|
+
|
|
98
|
+
## Docs-grill findings
|
|
99
|
+
|
|
100
|
+
- [fix_in_place] (stale_reference) Queue docs example omits the new flag; add it on this branch.
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The intake state for this file is now `plan_refined_ready_for_promotion`. The loop stops here for the operator to review the refined plan before promotion.
|
|
104
|
+
|
|
105
|
+
## Stage 4 — Local human-review checkpoint
|
|
106
|
+
|
|
107
|
+
The operator reads the refined plan, confirms the acceptance criteria and coverage matrix, and dispositions the docs-grill findings. No GitHub mutation has happened so far; the plan file is the only artifact. When the operator approves, the flow moves to promotion.
|
|
108
|
+
|
|
109
|
+
## Stage 5 — Promoted to a draft PR
|
|
110
|
+
|
|
111
|
+
`promote-plan.mjs` commits the plan doc, opens one draft PR, and writes the PR number back into the plan's front-matter. The file gains a leading front-matter block (the body is unchanged):
|
|
112
|
+
|
|
113
|
+
```markdown
|
|
114
|
+
---
|
|
115
|
+
prNumber: 1234
|
|
116
|
+
---
|
|
117
|
+
# phase-42 plan
|
|
118
|
+
|
|
119
|
+
## Status
|
|
120
|
+
|
|
121
|
+
draft
|
|
122
|
+
|
|
123
|
+
## Objective
|
|
124
|
+
|
|
125
|
+
Add a `--dry-run` flag to the queue dispatcher so an operator can preview the
|
|
126
|
+
dispatch order without mutating the board.
|
|
127
|
+
|
|
128
|
+
# ... base, refinement, coverage-matrix, and docs-grill sections unchanged ...
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The PR body references `docs/phases/phase-42.md` as the spec-of-record, completing the bidirectional plan↔PR link. No GitHub issue was minted. Re-running `promote-plan.mjs` on this file now resolves to `already_promoted` and opens nothing, because the front-matter already carries `prNumber`.
|
|
132
|
+
|
|
133
|
+
## Relationship to other docs
|
|
134
|
+
|
|
135
|
+
| Doc | Relationship |
|
|
136
|
+
|---|---|
|
|
137
|
+
| [Local-Planning Flow](local-planning-flow.md) | The operator sequence this example traces |
|
|
138
|
+
| [Artifact Authority Contract](artifact-authority-contract.md) | Canonical model and per-phase contract details |
|
|
139
|
+
| [Plan-file Contract](plan-file-contract.md) | Plan-file format, base sections, and the `prNumber` front-matter |
|
|
@@ -2,8 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
Canonical owner for merge preconditions across all workflow families.
|
|
4
4
|
|
|
5
|
+
## Conflict-free (mergeable) is a precondition at every gate
|
|
6
|
+
|
|
7
|
+
A PR that conflicts with its base gets **no `pull_request` CI run** — GitHub can't
|
|
8
|
+
compute the merge ref — so the gate silently stalls (green-less with no obvious
|
|
9
|
+
cause). Conflict-free is therefore a required gate precondition, checked at **two
|
|
10
|
+
seams**: before requesting CI/Copilot, and again before merge.
|
|
11
|
+
|
|
12
|
+
- `gh pr view --json mergeable,mergeStateStatus` drives it. A `CONFLICTING` /
|
|
13
|
+
`DIRTY` / `BEHIND` PR does **not** pass any gate (`gateBoundary:
|
|
14
|
+
conflict_resolution`, `nextAction: resolve_merge_conflicts`).
|
|
15
|
+
- `mergeable` is computed asynchronously, so a freshly-pushed head briefly reads
|
|
16
|
+
`UNKNOWN`. The detect layer **re-polls a bounded number of times**; if it never
|
|
17
|
+
settles, the gate **fails closed to a recheck** (`nextAction: wait_for_ci`) —
|
|
18
|
+
an unsettled merge state is never treated as clean.
|
|
19
|
+
- `loop info` surfaces a **Mergeable:** line (mergeStateStatus included) so a
|
|
20
|
+
conflict is diagnosed immediately, never mistaken for missing CI.
|
|
21
|
+
|
|
22
|
+
### Deterministic auto-resolve (additive CHANGELOG only)
|
|
23
|
+
|
|
24
|
+
When a PR is behind/`CONFLICTING`, run the conservative resolver before resuming
|
|
25
|
+
the gate path:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
node scripts/loop/resolve-pr-conflicts.mjs [--base <branch>] [--push]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
It merges `origin/<base>` into the PR branch and resolves **only the safe additive
|
|
32
|
+
case** — a `CHANGELOG.md` conflict where both sides only ADD list/section entries
|
|
33
|
+
(keep BOTH sides, in order) — then runs `npm run test:docs` and (with `--push`)
|
|
34
|
+
pushes. **Any other conflicted path, or a non-additive CHANGELOG edit, FAILS
|
|
35
|
+
CLOSED** naming the conflicted paths (no silent stall, no guessing — resolve those
|
|
36
|
+
by hand). This encodes the exact additive-CHANGELOG resolution the loop has been
|
|
37
|
+
doing manually; it is not a general conflict-resolution engine.
|
|
38
|
+
|
|
5
39
|
## Required before merge
|
|
6
40
|
|
|
41
|
+
1. ✅ Conflict-free with base (`mergeable: MERGEABLE`; not `CONFLICTING`/`DIRTY`/`BEHIND`/`UNKNOWN`)
|
|
7
42
|
1. ✅ CI green on current head (or crediblyGreen via `--local-validation-head-sha`)
|
|
8
43
|
2. ✅ Draft gate satisfied (clean verdict)
|
|
9
44
|
3. ✅ Pre-approval gate satisfied (clean verdict, current head)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Plan-file contract
|
|
2
|
+
|
|
3
|
+
A plan file is the persisted markdown artifact that drives work in local-planning mode. It reuses the existing phase-doc format under `docs/phases/` (for example, `docs/phases/phase-<n>.md`). The artifact authority model that selects local-planning mode lives in the [Artifact Authority Contract](artifact-authority-contract.md); this document defines the file format and its required base sections.
|
|
4
|
+
|
|
5
|
+
## Format
|
|
6
|
+
|
|
7
|
+
A plan file is a markdown document whose sections are level-2 (`##`) headings. It follows the same shape as the phase docs already committed under `docs/phases/`. The directory that holds plan files is `localPlanning.plansDir`, which defaults to `docs/phases/`.
|
|
8
|
+
|
|
9
|
+
## Base authoring sections
|
|
10
|
+
|
|
11
|
+
A plan file carries these base sections when it is authored, before refinement adds acceptance criteria and a definition of done:
|
|
12
|
+
|
|
13
|
+
| Heading | Purpose |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `## Status` | The current lifecycle state of the plan (for example, in progress, deferred). |
|
|
16
|
+
| `## Objective` | What the plan sets out to achieve. |
|
|
17
|
+
| `## In scope` | The bounded work this plan covers. |
|
|
18
|
+
| `## Explicit non-goals` | What this plan leaves out. |
|
|
19
|
+
|
|
20
|
+
Each base section has a non-empty body. Refinement is a later phase that adds acceptance criteria and a definition of done; those are outside the base authoring contract this document defines.
|
|
21
|
+
|
|
22
|
+
## Validator
|
|
23
|
+
|
|
24
|
+
`scripts/refine/validate-plan-file.mjs` checks a plan file against the base sections above. It exports a pure function `validatePlanFile(markdownText)` that returns `{ checker: "validate-plan-file", ok, errors }`, where each absent or empty-body base section contributes one entry with a distinct `missing_*` code. `ok` is `true` when every base section is present with a non-empty body.
|
|
25
|
+
|
|
26
|
+
The thin CLI accepts `--input <path>`, `--json`, and `--help`. The validation verdict is reported in the JSON payload; the process exits non-zero for argument or path errors (house style — see the [Validation Policy](validation-policy.md)).
|
|
27
|
+
|
|
28
|
+
## Optional front-matter (plan↔PR link)
|
|
29
|
+
|
|
30
|
+
A plan file may carry an optional leading YAML front-matter block — a `---` line, `key: value` lines, a closing `---` line — before its first heading. It is additive: a plan without front-matter is unchanged and fully valid. The only key the tooling reads or writes today is `prNumber:`, the number of the draft PR a plan was promoted into (PR-FIRST promotion never mints an issue; the committed plan doc is the spec-of-record and the PR body links it). The link is recorded bidirectionally — the PR body references the committed plan-doc path; the plan's `prNumber` front-matter references the PR. Promotion (`scripts/refine/promote-plan.mjs`) writes `prNumber` and is idempotent on a plan that already carries one. The parser/serializer lives in the pure `@dev-loops/core/loop/plan-file-promote-contract` module.
|
|
31
|
+
|
|
32
|
+
## Relationship to other docs
|
|
33
|
+
|
|
34
|
+
| Doc | Relationship |
|
|
35
|
+
|---|---|
|
|
36
|
+
| [Artifact Authority Contract](artifact-authority-contract.md) | Owns the artifact-selection model; selects local-planning mode and points here for the plan-file format. |
|
|
37
|
+
| [Validation Policy](validation-policy.md) | Owns the shared CLI verdict-versus-exit-code convention this validator follows. |
|