You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(tools): harden the release-notes drafter (PR #38 review)
All five points were real. Two of them made the tool quietly report LESS than
the truth, which is the failure mode that matters most for something whose whole
job is producing accurate facts.
1. prevTag was interpolated into a shell string. A tag name is repo-controlled
but still runtime-discovered input, so this is now defended twice: all git
calls go through spawnSync with an argv array (a shell cannot be quoted out
of one), and the base tag must match ^vX.Y.Z$ before use. Shape matters
independently of injection — a stray v0.9.2-rc1 sorts into the 'v*' glob and
would silently produce the wrong range, wrong commit list and wrong compare
link. Verified: v0.9.2-rc1, v-wip and "v0.9.2; rm -rf /" are all rejected.
2. PRs were only detected from merge commits, so on a squash-merging repo the
list would read "(none detected)" while every subject carried "(#123)". Both
shapes are now collected.
3. Suites were keyed by basename, so two extensions with the same test filename
would produce an ambiguous "biggest suites" list. Keyed by relative path now.
4. The coverage line printed "N cases in total" while suites whose summary could
not be parsed silently contributed 0 — authoritative-sounding and UNDER-
reporting. This one bit immediately: 5 of 24 suites do not print a count, so
"273 cases in total" was wrong. It now says how many suites were counted and
names the ones that were not.
5. The fix/perf heading also carried reverts; renamed to say so.
Also fixes a bug I introduced while making #2: prNumbers became a Set, but the
renderer still read .length, which is undefined on a Set — so the PR list emptied
itself silently. Caught by re-running against the real range (expected #33-36,
got "(none detected)"). Materialised to a sorted array.
Verified end to end against v0.9.1..HEAD: PRs #33-36 detected, coverage line now
honest about the 5 unparsed suites, and all guardrails still fire.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
not the biggest diff. Two features is a fine release; say so plainly. -->
132
165
133
166
## Highlights
134
-
${section('Candidates — feat (write these up, or move them down / delete)',features)}${section('Candidates — fix/perf (usually "Under the hood", unless a user hit the bug)',fixes)}
167
+
${section('Candidates — feat (write these up, or move them down / delete)',features)}${section('Candidates — fix/perf/revert (usually "Under the hood", unless a user hit the bug)',fixes)}
135
168
<!-- TODO For each thing you keep: say what it does, then the ONE non-obvious property a user should
136
169
know (a bound, a tradeoff, a thing it deliberately will not do). That sentence is the whole
137
170
value of hand-writing these. -->
@@ -142,7 +175,8 @@ ${section('Candidates — feat (write these up, or move them down / delete)', fe
142
175
143
176
## Test coverage
144
177
145
-
- **${suites.length} suites** across the bundled extensions, ${totalCases} cases in total — all green.
178
+
- **${suites.length} suites** across the bundled extensions — all green.
179
+
- ${totalCases} cases counted${uncounted.length ? ` across ${suites.length-uncounted.length} of them; ${uncounted.length} suite(s) did not report a count (${uncounted.map((s)=>s.file).join(', ')}), so the real total is higher` : ' in total'}.
146
180
${biggest.map((s)=>`- \`${s.file}\`${s.cases!=null ? ` (${s.cases} cases)` : ''} — <!-- TODO what does it guard? -->`).join('\n')}
0 commit comments