Skip to content

Commit cf6b4d7

Browse files
committed
docs: plan release summary coverage enforcement
1 parent f3b6a9d commit cf6b4d7

1 file changed

Lines changed: 242 additions & 0 deletions

File tree

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# Complete Release Summary Coverage Implementation Plan
2+
3+
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
4+
5+
**Goal:** Prevent SDL-MCP releases from publishing concise grouped summaries that fail to account for work in the complete previous-tag-to-release-tag commit range.
6+
7+
**Architecture:** A dependency-free release-notes module owns the canonical Git inventory, strict hidden coverage-marker parser, validation, and final body rendering. `prepare-release` invokes validation before expensive gates; the repository release skills require final-tag rendering before `gh release create --notes-file`.
8+
9+
**Tech Stack:** Node.js 24 ESM, Node standard library, Git CLI, `node:test`, existing SDL-MCP release scripts.
10+
11+
---
12+
13+
## Chunk 1: Validator and renderer
14+
15+
### Task 1: Establish assertion-level RED
16+
17+
**Files:**
18+
- Create: `tests/unit/build-release-notes.test.ts`
19+
- Create bootstrap only: `scripts/build-release-notes.mjs`
20+
- Reference: `devdocs/superpowers/specs/2026-08-25-release-summary-coverage-design.md`
21+
22+
- [ ] **Step 1: Write the initial import test**
23+
24+
Import the wished-for exports:
25+
26+
```typescript
27+
import {
28+
buildReleaseNotes,
29+
extractVersionSection,
30+
readReleaseInventory,
31+
validateReleaseNoteCoverage,
32+
} from "../../scripts/build-release-notes.mjs";
33+
```
34+
35+
- [ ] **Step 2: Run once and capture bootstrap RED**
36+
37+
```bash
38+
node --experimental-strip-types --test-concurrency=1 --test tests/unit/build-release-notes.test.ts
39+
```
40+
41+
Expected: FAIL because `scripts/build-release-notes.mjs` does not exist.
42+
43+
- [ ] **Step 3: Add skeletal exports only**
44+
45+
Create exports that throw `new Error("not implemented")`. Do not add parsing, Git, validation, or rendering behavior.
46+
47+
- [ ] **Step 4: Write behavior tests**
48+
49+
Use full synthetic 40-hex object IDs. Assert:
50+
51+
- exact version-section extraction;
52+
- one visible summary bullet may cover multiple commits;
53+
- the renderer emits one `## Commits since <baseTag>` appendix;
54+
- missing, duplicate, unknown, abbreviated, include-plus-omit, malformed, orphaned, and out-of-section markers fail;
55+
- only `merge-only` is accepted, and only for a commit with multiple parents;
56+
- the final tagged release commit is implicitly allowed only when its sole parent equals `preReleaseTargetOid`, its subject is `chore: release v<version>`, and all changed paths are in the exact allowlist.
57+
58+
Create disposable repositories with `mkdtempSync`, `git init`, commits, and an annotated tag. Assert:
59+
60+
- refs resolve through `^{commit}`;
61+
- a missing ref or non-ancestor base tag fails;
62+
- shallow repositories fail;
63+
- inventory includes merge commits;
64+
- bounded buffer overflow fails rather than returning partial data.
65+
66+
- [ ] **Step 5: Run and capture assertion-level RED**
67+
68+
Run the focused test.
69+
70+
Expected: parser, inventory, renderer, and final-release-commit assertions fail with `not implemented`, proving each behavior is exercised.
71+
72+
### Task 2: Implement the minimum dependency-free module
73+
74+
**Files:**
75+
- Modify: `scripts/build-release-notes.mjs`
76+
- Test: `tests/unit/build-release-notes.test.ts`
77+
78+
- [ ] **Step 1: Implement strict section and marker parsing**
79+
80+
Export:
81+
82+
```javascript
83+
export function extractVersionSection(markdown, version) {}
84+
export function validateReleaseNoteCoverage(options) {}
85+
```
86+
87+
Use line-based parsing only. Accept exactly:
88+
89+
```text
90+
<!-- release-note-commits: <40hex>[ <40hex>...] -->
91+
<!-- release-note-omit: <40hex> merge-only -->
92+
```
93+
94+
Bind include markers only to the immediately preceding summary bullet inside the exact version section. Reject every malformed or duplicate assignment.
95+
96+
- [ ] **Step 2: Implement canonical Git inventory**
97+
98+
Export:
99+
100+
```javascript
101+
export function readReleaseInventory({ cwd, baseTag, target }) {}
102+
```
103+
104+
Use `execFileSync("git", args, { maxBuffer: 16 * 1024 * 1024 })`; never invoke a shell. Resolve `baseTag^{commit}` and `target^{commit}`, reject shallow repositories and non-ancestor ranges, and collect full object ID, parents, subject, and changed paths for every commit in `baseTag..target`.
105+
106+
- [ ] **Step 3: Implement final body rendering**
107+
108+
Export:
109+
110+
```javascript
111+
export function buildReleaseNotes(options) {}
112+
```
113+
114+
In build mode, read the changelog from the immutable target commit with `git show <target>^{commit}:CHANGELOG.md`, not from the working tree. Validate the grouped section, allow only the mechanically valid final release commit exception, then append exactly one deterministic commit appendix from the same inventory.
115+
116+
- [ ] **Step 4: Add the CLI**
117+
118+
Support:
119+
120+
```text
121+
node scripts/build-release-notes.mjs validate --version <version> --base-tag <tag> --target <ref>
122+
node scripts/build-release-notes.mjs build --version <version> --base-tag <tag> --target <tag> --pre-release-target <oid> --output <path>
123+
```
124+
125+
Validation prints machine-readable JSON containing `preReleaseTargetOid`. Build mode writes the validated body only after every check passes.
126+
127+
- [ ] **Step 5: Run focused tests and capture GREEN**
128+
129+
Run the focused test command.
130+
131+
Expected: all tests pass with zero failures.
132+
133+
- [ ] **Step 6: Run syntax and diff checks**
134+
135+
```bash
136+
git diff --check
137+
node --check scripts/build-release-notes.mjs
138+
```
139+
140+
Expected: both exit 0.
141+
142+
## Chunk 2: Release workflow integration and documentation
143+
144+
### Task 3: Gate release preparation
145+
146+
**Files:**
147+
- Modify: `scripts/prepare-release.mjs`
148+
- Test: `tests/unit/build-release-notes.test.ts`
149+
150+
- [ ] **Step 1: Add a failing prepare-release integration assertion**
151+
152+
Assert the release script imports or invokes the canonical validator before its existing expensive build/test commands and requires an explicit `--base-tag`.
153+
154+
- [ ] **Step 2: Run focused test and capture RED**
155+
156+
Expected: FAIL because `prepare-release` does not invoke coverage validation.
157+
158+
- [ ] **Step 3: Add the minimal integration**
159+
160+
Parse `--base-tag <tag>`, invoke validation with package version and `HEAD` before expensive gates, and print the returned full `preReleaseTargetOid`. Preserve every existing release check and exit-code contract.
161+
162+
- [ ] **Step 4: Run focused tests and capture GREEN**
163+
164+
Expected: all focused tests pass.
165+
166+
### Task 4: Synchronize required release instructions
167+
168+
**Files:**
169+
- Modify: `.codex/skills/release-notes/SKILL.md`
170+
- Modify: `.agents/skills/release-notes/SKILL.md`
171+
- Test: `tests/unit/build-release-notes.test.ts`
172+
173+
- [ ] **Step 1: Add failing guidance assertions**
174+
175+
For each skill file independently, assert it requires:
176+
177+
- the complete unbounded range;
178+
- concise grouped summaries;
179+
- hidden coverage markers;
180+
- no second visible commit list;
181+
- preservation of `preReleaseTargetOid`;
182+
- final body generation from the annotated tag;
183+
- `gh release create --notes-file`.
184+
185+
Do not require unrelated bytes in the two skill files to remain identical.
186+
187+
- [ ] **Step 2: Run focused test and capture RED**
188+
189+
Expected: FAIL because current skills lack the coverage-ledger and final-tag steps.
190+
191+
- [ ] **Step 3: Update both skill copies**
192+
193+
Keep the existing categories and concise grouped-summary rules. Add the explicit ledger/build workflow. Do not expose hashes twice in rendered output.
194+
195+
- [ ] **Step 4: Run focused test and capture GREEN**
196+
197+
Expected: all focused tests pass.
198+
199+
## Chunk 3: Verification and handoff
200+
201+
### Task 5: Verify the implementation
202+
203+
**Files:**
204+
- Verify all files above.
205+
206+
- [ ] **Step 1: Run the focused regression**
207+
208+
```bash
209+
node --experimental-strip-types --test-concurrency=1 --test tests/unit/build-release-notes.test.ts
210+
```
211+
212+
Expected: zero failures.
213+
214+
- [ ] **Step 2: Run affected release/tooling checks**
215+
216+
```bash
217+
npm run typecheck
218+
npm run lint
219+
git diff --check
220+
```
221+
222+
Expected: typecheck and diff check exit 0; lint has zero errors, with only known warnings if present.
223+
224+
- [ ] **Step 3: Exercise final-tag rendering in a synthetic repository**
225+
226+
Create a disposable tagged fixture containing grouped summaries and hidden coverage markers, then run build mode against its immutable annotated tag.
227+
228+
Expected: validation succeeds, grouped prose is concise, and one appendix contains every fixture commit exactly once.
229+
230+
- [ ] **Step 4: Verify historical inventory separately**
231+
232+
Call `readReleaseInventory({ cwd: process.cwd(), baseTag: "v0.13.4", target: "v0.13.5" })` from a one-line Node ESM command.
233+
234+
Expected: 35 commits, with no attempt to validate pre-marker historical changelog content and no external mutation.
235+
236+
- [ ] **Step 5: Review the final diff**
237+
238+
Confirm every changed line traces to full-range grouped-summary enforcement. Confirm no package, lockfile, workflow, tag, or release mutation.
239+
240+
- [ ] **Step 6: Commit only if explicitly authorized**
241+
242+
If commit authorization is confirmed, create one implementation commit separate from the approved design and plan commits. Otherwise leave the verified implementation uncommitted and report that state.

0 commit comments

Comments
 (0)