Skip to content

fix(devx): a trailing YAML comment no longer hides a bump entry from the changeset frontmatter parsers (#7004) - #7053

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7004-changeset-trailing-comment
Aug 9, 2026
Merged

fix(devx): a trailing YAML comment no longer hides a bump entry from the changeset frontmatter parsers (#7004)#7053
os-project-manager merged 2 commits into
mainfrom
claude/issue-7004-changeset-trailing-comment

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #7004

One shared entry regex, four carriers, three different consequences. Fixed family-wide in one change, because fixing one alone would make the invariant all four state in their own comments false — that they read the same block the same way.

What was wrong

The entry regex was anchored ([A-Za-z]+)\s*$, which accepts nothing after the bump word:

/^\s*["']?([^"':]+)["']?\s*:\s*([A-Za-z]+)\s*$/

Re-measured the way #7004 measured it — extracting each shipped function's source and loading it standalone, then judging against the real @changesets/parse@0.4.3 this repo resolves. Not a re-implementation, and not a regex reading.

#7004's reported shape, confirmed:

■ '"@objectstack/spec": major # keep'
    @changesets/parse                        : [{"name":"@objectstack/spec","type":"major"}]
    check-changeset-no-major.majorPackagesIn : []
    check-empty-changeset.declaredBumpsIn    : {"fenced":true,"packages":[]}
    check-adr-0087.parseChangeset            : []

Two more the same anchoring hid, not in the report:

  1. A quoted bump value. "@objectstack/spec": "major" and : 'major' — changesets reads both as a real major; all four read nothing. Same anchor, same miss.
  2. The opposite direction — a declaration invented rather than hidden. A whole-line YAML comment containing a colon is entry-shaped, so # note: major parsed as a package literally named # note bumped major. changesets declares nothing for it.

And a fourth carrier #7004 did not name: scripts/objectui-changeset-digest.mjs carries the byte-identical regex. It is not a gate, so it neither reds nor greens — an entry it cannot see makes the changeset read release-nothing and the commit leaves the release digest entirely. That is #4731's harm reached through the parser instead of through a type filter, worst for breaking changes, the one class that must never vanish from a release record.

The fix

/^\s*["']?([^"':]+)["']?\s*:\s*["']?([A-Za-z]+)["']?(?:\s+#.*)?\s*$/

plus, before it, if (/^\s*#/.test(line)) continue; — a whole-line comment declares nothing.

Byte-identical in all four files. The comment arm is spelled (?:\s+#.*)? and not (?:#.*)? on purpose: YAML requires whitespace before an inline #, so major# note is the scalar major# note and changesets throws invalid version type. Matching YAML exactly means this family misses only what changesets refuses.

Measured, before and after

Against @changesets/parse@0.4.3 over 31 shapes:

OLD disagreed on 19 accepted-by-changesets rows; NEW disagrees on 0. Regressions: 0.

Every surviving difference is on a file changesets throws on — the documented harmless direction (this family being stricter about a changeset that could never version anything).

Against the real stock, both parsers run over all 1568 changesets: 0 verdicts changed. The fix is verdict-preserving on every file that exists.

Reverse verification — each carrier in its own predicted direction

Direction predicted before running, then the limb put back (old anchor restored, comment guard dropped, all four files). Running the real gate CLIs copied into throwaway repos:

gate predicted ablated result
check-changeset-no-major tick to RED ✓ No 'major' bumps in pending changesets.exit 0, the tick, on a whole-stack major
check-empty-changeset RED to accept This PR adds an empty-frontmatter changesetexit 1 on a perfectly valid changeset
check-adr-0087-registration missed to seen signals=[] breaking=false (with the fix: signals=["major"] breaking=true)
objectui-changeset-digest dropped to kept {} — the package vanishes; and C6 shows the phantom {"# note":"major", ...}

Self-tests under the same ablation: 12 / 15 / 5 / 6 failures respectively. Controls stayed green throughout (digest C7, the no-major major-in-the-slot control), so the reds are about the entry regex and not about a fixture that stopped parsing.

Anti-vacuous-green, per #6983 and #7008: every fixture is paired with a control that differs by exactly the shape under test, the ablation script refuses to run if its anchor no longer matches (ABLATION DID NOT APPLY), and the family assertion below asserts it extracted exactly one regex per file before comparing them.

#7008's pin: flipped, not deleted

check-changeset-no-major.mjs carried a pinned assertion with the instruction "flip this when the family-wide regex is fixed, never delete it". It is flipped — the same inputs, now asserted to be caught — and extended with the two extra shapes and the reverse direction.

The invariant is now mechanical, not prose

Until now "the family reads one block one way" was four comments asserting each other, which is exactly what let the gap reach all four at once and let a fourth carrier go unnoticed in the report. check-empty-changeset.mjs's self-test now extracts the entry regex literal from all four files and asserts they are byte-identical, plus that the shared spelling actually carries the comment arm (so four identically-stale copies cannot satisfy it).

Verified by drifting one file only:

family: all four changeset frontmatter parsers must use a byte-identical entry regex
        — found 2 distinct spellings: [... "scripts/objectui-changeset-digest.mjs", "OLD ANCHOR" ]
family: scripts/objectui-changeset-digest.mjs must skip whole-line YAML comments

Coordination with #7005 — verified, not assumed

#7005 is in flight on check-changeset-no-major.mjs (stock vs diff scoping). Its change and this one are orthogonal in intent, but I verified composition rather than assuming it: trial-merged both branches, then ran the merged tree's self-tests.

Result: clean merge, merged file 1319 lines (mine 705 + theirs 1257 from a 643-line base), my comment-aware regex present and the old anchor gone, their baseText diff-scoping present, my flipped pin present. The merged tree's own self-test reports 113 assertions naming both contributions, and all four self-tests pass on it.

⚠️ One honest note on method: my first trial merge measured the wrong ref — an intervening git fetch origin main had overwritten FETCH_HEAD, so I had merged my branch with main and read the clean result as "composes with #7005". Caught it because baseText came back 0. Redone against a pinned ref; the numbers above are from the real one.

Gates

pnpm lint clean; all 37 check: gates in lint.yml's ESLint job green, before and after merging current main.

No changeset

scripts/** is repo tooling in a private root package — this PR releases nothing, so it carries skip-changeset and no changeset file.

Filed, not fixed here


Generated by Claude Code

@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 9, 2026 11:54am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A trailing YAML comment hides a bump entry from all three changeset frontmatter parsers

2 participants