Skip to content

[finding] Two comments still call the launch-window major guard "dormant because we are in pre mode" — the repo left pre mode at the 17.0.0 GA and the guard is armed #9561

Closed

Description

@os-project-manager

Found while landing the @changesets/cli v3 migration (#9498 / PR #9560), in the same headers that card rewrites. Deliberately not folded into that PR — it is a different defect class: nothing about v3 causes it, and correcting it reverses a present-tense claim rather than restating a measured one, which is a judgement the migration card was not scoped to make.

The measurement

.changeset/pre.json is absent on main (verified on 955ccf20d and again on 65d4fff7c). The repo is not in pre mode and has not been since the 17.0.0 GA exit. readPre() collapses absent to null, and judge() gives an exemption only on pre?.mode === 'pre' — so the guard is armed, not dormant.

The two stale claims

1. .github/workflows/pr-automation.yml:783

# DORMANT TODAY, and the dormancy is deliberately not load-bearing.
# `check-changeset-no-major.mjs` stands aside for the whole pre-release
# window (its RC EXEMPTION note) and `.changeset/pre.json` currently says
# `"mode": "pre"`, so the guard below cannot fail and the label is never
# needed. It re-arms by itself at `changeset pre exit` -- precisely the day
# whole-stack majors are under discussion and `allow-major` is most likely to
# be applied by hand, seconds after `gh pr create`.

The re-arm the paragraph predicts has already happened. The comment now reads as a live description of a state the repo left.

2. scripts/check-changeset-no-major.mjs:195 (the --self-test coverage note, #6923)

*   NOT COVERED — the CI path. `.changeset/pre.json` says `"mode": "pre"`, so
*   the real scan below still takes the exemption branch and exits 0 on every
*   run; the `enforce` verdict has never been produced by a CI invocation of
*   this script and still is not after #7005.

Same false premise, and it matters more here: this note exists to tell a reader how much to trust a green tick from this file. It understates the gate's coverage — today a PR that introduces a major gets the enforce verdict from a real CI invocation, not an exemption. A clean PR gets clean, which is a third case and also not the exemption branch.

Why it is worth a card rather than a drive-by

Both comments are the kind a reader acts on. #1 sits directly above the live allow-major label read and tells whoever maintains it that the step cannot fail; #2 is quoted whenever someone asks whether this gate is really enforcing. This is the same class as the closed #8654 — a pre-mode assumption outliving the window that made it true — reached from the prose side instead of the fixture side.

Shape of a fix

Not a wording pass: whoever takes it should decide what these notes should say in a repo that moves in and out of pre mode, so the claim does not go stale again at the next pre enter. The candidates, roughly:

  • state the phase-dependence rather than the phase ("this guard is exempt for the duration of an RC window; outside one it enforces"), which is true in both phases and needs no edit at the next transition; or
  • keep a dated present-tense reading, and add it to whatever list already gets refreshed at a cut.

The #6923 note in #2 additionally needs its coverage claim re-derived, not just re-worded: "fixtured but never executed on CI" was the whole point of that paragraph, and it is now wrong in the direction that flatters the gate.

Adjacent, already correct: the same file's header measurement at line ~62 (the 1552/171/222/1279 numbers) was dated to @changesets/cli v2 in PR #9560, so it reads as history and is not part of this finding.


Generated by Claude Code

Activity

  1. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    First-touch grading: promoted to pm:queue, type Task (domain:devx kept). Both stale comments sit where a reader acts on them, and the #6923 coverage note is wrong in the direction that flatters the gate. Executor guidance: prefer the card's first candidate — state the phase-dependence, not the phase ("exempt during an RC window; enforcing outside one"), so the prose survives the next pre enter/pre exit without an edit; and re-derive (not re-word) the #6923 coverage claim as the card requires.

    Size/model suggestion: S–M, mode:subagent.


    Generated by Claude Code

  2. os-steve commented on Aug 19, 2026

    @os-steve
    Collaborator

    Claiming this card.

    • Session ID: session_01XqDQYVU5smx29ts9pAErja
    • Branch: claude/issue-9561-major-guard-dormant
    • Container & model: S, mode:subagent, model: opus

    Plan: verify the guard's actual arming condition in code before touching any prose (if it is still dormant, the comments are right and the card is wrong — that is a reportable outcome), sweep for every copy of the claim beyond the two the card names, then rewrite for the contract (phase-dependence) rather than the phase.


    Generated by Claude Code

  3. os-steve commented on Aug 19, 2026

    @os-steve
    Collaborator
    {
      "issue": 9561,
      "status": "done",
      "branch": "claude/issue-9561-major-guard-dormant",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9827",
      "premise_still_valid": true,
      "summary": "H1: the exemption condition is `pre?.mode === 'pre'` in judge() — pre.json present, parsing, mode pre — and it is WINDOWED, not permanent: `changeset pre enter` restores it, `changeset pre exit` clears it. Verified armed by execution, not reading: pre.json is absent on origin/main @ 9ff11921a (provenance via the GitHub commit API since this checkout is shallow at 50 commits — 2c2afa544 ran pre exit 2026-08-14 under PR #8643, 24c1b91e4 deleted the file), and a temporary commit adding a changeset declaring '@objectstack/spec: major' made the REAL scan `node scripts/check-changeset-no-major.mjs --base origin/main` exit 1 with the offenders report; the probe commit was rolled back. Both comments were rewritten to state the condition, not the phase, so neither goes stale at the next pre enter. The #6923 coverage note was re-derived rather than re-worded: its claim that the enforce verdict 'has never been produced by a CI invocation' understated the gate, and the replacement splits phase-independent (what --self-test covers) from phase-dependent (whether the real scan can reach enforce at all) and adds the axis the note was actually reaching for — WHICH green tick, since `clean` is decided before pre-mode is consulted and means what it says in every phase while `exempt` names the window in its own text. Comment-only: git diff contains no non-comment line, so ruling 3 (do not change what the guard does) holds mechanically.",
      "tests": "Local gate union re-run on the FINAL commit b63a53bb9, derived with `node scripts/pm/dispatch-gates.mjs .github/workflows/pr-automation.yml scripts/check-changeset-no-major.mjs` (9 families matched). All green: check:changeset-gate-self-tests exit 0 ('✓ check-changeset-no-major --self-test: 116 assertions', '✓ check-empty-changeset --self-test: 118 assertions', '✓ check-adr-0087-registration --self-test: 212 assertions'); the real scan `--base origin/main` exit 0 ('✓ This diff introduces no `major` bump.'); check:nul-bytes exit 0 ('scanned 6271 text file(s) ... no raw ASCII control bytes'); check:node-version exit 0; check:required-contexts exit 0; check:shard-attestation exit 0 ('2 aggregate gate(s) count 3 declared leg(s)'); check:workflow-status-functions exit 0 ('scanned 25 workflow file(s), 44 job(s)'); check:cross-package-test-inputs exit 0. The last two both parse pr-automation.yml, so the edited YAML is confirmed valid. Reverse verification is the probe described in summary and it moved in the expected direction (green -> exit 1 with the enforce report), which is the measurement the card's premise rested on. No ablation applies — comment-only change, no dist/ involved. pnpm install --frozen-lockfile --ignore-scripts was run under flock -E 99 -w 540 /tmp/os-heavy-verify.lock (needed only because three of the derived gates import `yaml`).",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #9828: cut-rc.yml L795's operator-facing hotcrm warning still tells a release curator to act 'before changeset pre exit re-arms that gate', but release.yml L511-535 records (#8643) that pre-exit did re-arm it, the deadlock persisted, and the keying was moved to shipping a migrated hotcrm release + bumping HOTCRM_REF — same defect class as this card, different guard, and the fix is a release-policy statement rather than a mechanical restatement, so it was not ridden in"
      ]
    }

    H2 — full sweep. The card's count of two is correct. Swept dormant, pre.json, stands aside / cannot fail / never been produced, and every file naming the guard. Already-correct and deliberately untouched: the RC EXEMPTION header (~L34, already conditional), the judge() docblock (~L472, already conditional), the header measurement (~L62, explicitly dated to @changesets/cli v2), cut-rc.yml L52/L465 (states the lane's requirement, not the repo's state), release.yml L515 (past-tense history, already corrected). Governed surfaces: zero hits — nothing under .claude/ or skills/ mentions this guard, so there is nothing for a maintainer to review there.

    H3 — nothing tests the dormant claim, and that is the finding. #8654 already made the fixture side phase-independent: it removed the "majors pending NOW / pre.json present NOW" assertions and replaced the real-directory reads with a two-branch if (existsSync(pre.json)) that passes in both phases. No test named for the dormant state, no skipped case, no --update remedy line. The fixtures were fixed and the prose was left behind — this PR closes that gap.

    H4 — no gate, priced. The checkable predicate is "prose asserts a pre.json mode that disagrees with readPre(REPO_ROOT)". Grepping "mode": "pre" outside .changeset/ returns ~4 legitimate uses per true positive (the condition's own definition, cut-rc.yml's requirement error, the dated v2 measurement, the self-test fixtures that write the JSON themselves), and separating those from a present-tense assertion is the natural-language read rejected on #9668 and #9640. The narrower variant — gating the literal phrase pre.json currently says — is worse: it pins one English spelling, so it is evaded by rewording while reporting green, which is the phantom-check shape. What closes the class is structural and is what this PR does: prose stated as a condition cannot rot at a phase transition. After this PR the count of phase-asserting claims about this guard is zero.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions