Six defects in the release/changelog seam #231

Closed
opened 2026-08-22 19:12:57 +00:00 by john · 0 comments
Owner

Six defects in the release/changelog seam, all reproduced by execution against
the current tree. They were filed between 2026-08-05 and 2026-08-08, archived
unread in .planning/CAPTURE.md, and re-verified live on 2026-08-22.

This is the seam v3.5.8 rewrote. Item 6 below and the open item phase 2 filed
against release-decision.mjs's JSDoc code set are the same defect class in the
same subsystem, filed two weeks apart, neither aware of the other.

1. skip/no-version-field is a silent no-op no halt covers

lib/release-decision.mjs:198-200 returns action:'skip', code:'no-version-field'
rather than a refusal. Running release-bump.mjs bump on a manifest with no
version field and a valid --version returns
{"ok":true,"action":"skip","reason":"no-version-field"}. workflows/milestone.md:65-85
enumerates exactly four halts and none fires on this, so a close continues over a
manifest that was never bumped.

2. promoteUnreleased section bounding is fence-blind

sectionEnd (lib/release-decision.mjs:383-389) tests /^## / with no fence
tracking, so a ## line inside a fenced code block reads as a section boundary.
Observed: the promotion truncates, stranding the fence-close and the trailing
bullet under the ## [Unreleased] stub.

3. Scaffold-only bodies defeat section_empty

releaseSectionEmpty (lib/release-decision.mjs:391-405) flags any non-blank
line, a bare ### Added included. A changelog carrying ### Added and ### Fixed
with no bullets returns sectionEmpty:false, so the close's fourth halt passes
over a heading with nothing under it.

4. An absent CHANGELOG.md is indistinguishable from a clean run

release-bump.mjs:335 keeps absence as changed:false, ok:true. A run against a
directory with no CHANGELOG.md returns "changelog":{"changed":false} with no
section_empty and no missing key, and workflows/milestone.md names no such
state. The caller cannot tell "nothing to do" from "the file is gone".

sectionEnd's backward-scan branch (lib/release-decision.mjs:385-388) strips a
trailing [#NN]: url line as the trailing ref block. With ## [Unreleased] as
the last section, the bullet promotes into the dated section while
[#42]: https://... stays behind under the stub.

6. The seam header under-documents its verdict codes

release-bump.mjs:101-103 names 4 of the 7 codes, missing unparseable-version,
downgrade and not-an-upgrade. Separately, --version v returns
{"ok":false,"reason":"no-target-version","target":""} because
normalizeTargetVersion (lib/release-decision.mjs:37-42) strips the v to an
empty string rather than returning null.

Six defects in the release/changelog seam, all reproduced by execution against the current tree. They were filed between 2026-08-05 and 2026-08-08, archived unread in `.planning/CAPTURE.md`, and re-verified live on 2026-08-22. This is the seam `v3.5.8` rewrote. Item 6 below and the open item phase 2 filed against `release-decision.mjs`'s JSDoc code set are the same defect class in the same subsystem, filed two weeks apart, neither aware of the other. ### 1. `skip/no-version-field` is a silent no-op no halt covers `lib/release-decision.mjs:198-200` returns `action:'skip', code:'no-version-field'` rather than a refusal. Running `release-bump.mjs bump` on a manifest with no `version` field and a valid `--version` returns `{"ok":true,"action":"skip","reason":"no-version-field"}`. `workflows/milestone.md:65-85` enumerates exactly four halts and none fires on this, so a close continues over a manifest that was never bumped. ### 2. `promoteUnreleased` section bounding is fence-blind `sectionEnd` (`lib/release-decision.mjs:383-389`) tests `/^## /` with no fence tracking, so a `## ` line inside a fenced code block reads as a section boundary. Observed: the promotion truncates, stranding the fence-close and the trailing bullet under the `## [Unreleased]` stub. ### 3. Scaffold-only bodies defeat `section_empty` `releaseSectionEmpty` (`lib/release-decision.mjs:391-405`) flags any non-blank line, a bare `### Added` included. A changelog carrying `### Added` and `### Fixed` with no bullets returns `sectionEmpty:false`, so the close's fourth halt passes over a heading with nothing under it. ### 4. An absent CHANGELOG.md is indistinguishable from a clean run `release-bump.mjs:335` keeps absence as `changed:false, ok:true`. A run against a directory with no `CHANGELOG.md` returns `"changelog":{"changed":false}` with no `section_empty` and no `missing` key, and `workflows/milestone.md` names no such state. The caller cannot tell "nothing to do" from "the file is gone". ### 5. A body-final link-ref definition strands under the Unreleased stub `sectionEnd`'s backward-scan branch (`lib/release-decision.mjs:385-388`) strips a trailing `[#NN]: url` line as the trailing ref block. With `## [Unreleased]` as the last section, the bullet promotes into the dated section while `[#42]: https://...` stays behind under the stub. ### 6. The seam header under-documents its verdict codes `release-bump.mjs:101-103` names 4 of the 7 codes, missing `unparseable-version`, `downgrade` and `not-an-upgrade`. Separately, `--version v` returns `{"ok":false,"reason":"no-target-version","target":""}` because `normalizeTargetVersion` (`lib/release-decision.mjs:37-42`) strips the `v` to an empty string rather than returning null.
john closed this issue 2026-08-23 02:38:29 +00:00
john added this to the v3.5.9 milestone 2026-08-23 20:51:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: crenshawdev/cadence-archived#231
No description provided.