Skip to content

[finding] An unreadable .changeset/ makes check-changeset-no-major.mjs print a tick and exit 0 (#4690 shape) #7006

Description

@os-project-manager

Measured while adding --self-test fixtures to that script (#6923). Not fixed there — flipping an exit code in the enforcing half on the eve of the window where that half re-arms is a behaviour change, not a fixtures change. Filed per Prime Directive #10. Unclaimed. Observation class.

The observation

try {
  entries = readdirSync(changesetDir);
} catch {
  console.log('No .changeset directory found — nothing to check.');
  process.exit(0);
}

A gate that could not read its input reports as "no violations" in every checks list. That is the #4690 anti-pattern by its own definition, and the family's other members go the other way: check-empty-changeset.mjs exits 1 on an unresolvable base ref and writes several paragraphs about why.

The distinction #4690 turns on is between zero input and could not read the input. This script handles the first correctly — an existing but empty .changeset yields no offenders and a legitimate tick. It is only the second, the catch, that is the shape.

What #6923's PR did and did not do

It did not change the behaviour. It did make the two facts distinguishable, which is the prerequisite for changing it: readChangesets() now returns null for "could not read" and an empty Map for "read it, found nothing", and judge() turns those into two separate verdicts (no-changeset-dir vs clean). Both are pinned by fixtures, and the pin on the exit code is written to be flipped together with the behaviour rather than deleted:

'RESIDUAL pinned, not endorsed: an unreadable .changeset still exits 0 (#4690 shape).
 Filed for triage — flip this assertion together with the behaviour, never alone'

Impact, stated honestly

  • No known instance and no plausible one. The directory is resolved from the script's own location (resolve(__dirname, '..')), not from the cwd, so "missing" means .changeset/ was deleted from a full checkout — which would also break check-changeset-fixed.mjs, pre.json, config.json and the release pipeline, all more loudly than this.
  • Graded observation class for exactly that reason: it is a correctness inconsistency with the family's stated rule, not something a user hits.
  • Recorded so the inconsistency is a decision rather than an oversight, since the file now says in its own header that it is one.

Candidate dispositions (no conclusion presumed)

  1. Exit 1 with a message naming the unreadable path, matching check-empty-changeset.mjs. One line plus the fixture flip.
  2. Leave it and keep the header's RESIDUAL note as the record — a documented, argued exception rather than an accident.
  3. Fold it into whatever PR settles check-changeset-no-major.mjs judges the whole .changeset stock, not the PR's diff — at changeset pre exit it reds every unrelated PR (171 files measured) #7005, since both are about what this guard does when its input is not what it expects.

Leaning 1 for consistency with the family, but it is genuinely small and disposition 2 is defensible. Left to triage.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions