Skip to content

feat(scripts): read one changeset against itself, and date the contradiction - #9850

Merged
os-try-charles merged 3 commits into
mainfrom
claude/issue-9841-changeset-self-contradiction
Sep 18, 2026
Merged

os-try-charles merged 3 commits into
mainfrom
claude/issue-9841-changeset-self-contradiction

Conversation

@os-try-charles

@os-try-charles os-try-charles commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #9841

A .changeset/*.md that declares a package in its front matter while its own body says that package did not move contradicts itself, and both halves publish verbatim into that package's CHANGELOG. The four structural changeset gates all judge the front matter; the two readings already in check-changeset-claims.mjs judge a body against a diff. Nobody read the two halves of one file against each other.

The falsification pass ran FIRST, and it corrects the card

The card made this the first deliverable and said outright that its filer had not run it. It is run here, both halves, each with a positive control taken before its zeros were believed.

Pending corpus — every .changeset/*.md in this tree: 1,686 files read, 1,153 of them publishing a body (the rest declare no bump and reach no CHANGELOG).

Released corpus — every CHANGELOG.md with a package.json beside it: 42 files, 3,279 released version sections, 24,557 entries, versions 0.0.1 through 17.6.0. 883 of those entries carry a negation-of-change word.

coordinate pending released
the card's, as written — a sentence naming a declared package and carrying a negation of change 11 pairings / 10 bodies 8
the same, also accepting the bare short name (types, layout) 23 pairings / 21 bodies —
attached — the negation directly on a bare package spelling, nothing between but a copula 0 0

⭐ Every one of those 19 pairings at the card's coordinate is LEGITIMATE. Read one at a time, they all negate an ASPECT of the package and never the package: «@object-ui/fields re-exports all four names unchanged», «Both are internal to @object-ui/app-shell; no package export moves», «packages/app-shell type-checks unchanged», «@object-ui/console's / resolver is unchanged». ⇒ the card's step 2 as written is the noise its own falsification test was looking for, and it is not built here. Requiring the negation to ATTACH to the package drops all 19 and keeps the attested instance.

Positive controls, both corpora. Planting the carded instance verbatim moves every counter: pending 1,686 → 1,687, publishing 1,153 → 1,154, card's coordinate 11 → 12, attached 0 → 1 and it names the clause. Injecting the same clause as one released entry moves the released side 24,557 → 24,558 and attached 0 → 1. Both plants were removed and git diff HEAD verified empty.

⚠️ The measurement caveat, stated because it cuts against the artifact. Both corpora are the SURVIVING state. A contradiction repaired on a branch before merge is in neither — which is exactly what happened to the one attested instance. So these zeros are strong evidence about the FALSE-POSITIVE rate (the thing that decides the check's cost) and weak evidence about the incidence rate. The case for closing this card instead is real and the numbers above are what it rests on: the shape has landed zero times in 26,243 bodies. That call is the PM's, not this seat's.

What landed

A third reading in the same script, in the same report-only, exit-0 posture. ⛔ No new gate, ⛔ no new npm script, ⛔ no new CI context, so no command-parity doc edit.

  • Corpus — the .changeset/*.md this change adds or modifies, read out of this tree.
  • Coordinate — a package the file's own front matter declares, negated in its own body with nothing between the spelling and the negation but a copula. Either spelling: the npm name, or the workspace directory. The carded instance wrote packages/types, never @object-ui/types, so a reading that knew only npm names would have been silent on the one artefact it exists for.
  • --census re-derives both coordinates over whatever corpus the tree carries, so no number above is written down anywhere (AGENTS.md 完善设计器的每一个细节 #9).

BORN FALSE vs WENT FALSE, dated from the branch's own revisions

⛔ It cannot be read off the head, and this is measured on the artefact rather than assumed:

  • at 2bc8fd12e2, the commit that ADDED the carded changeset, the body already said «packages/types is untouched» and the front matter declared @object-ui/components and nothing else — the sentence was true;
  • at 21896172a9, the same commit that moved 62 lines in packages/types/src/overlay.ts (+35 −27, verified on that commit) added '@object-ui/types': patch to that same front matter.

Against the merge base the net diff shows the file simply ADDED with both halves present, which reads as born false. It was not. So the verdict is dated from the revisions of that file, and a range carrying none of them is reported UNDATED, ⛔ never defaulted to either verdict.

⚠️ The BASE revision is read first, and a branch walk alone is not enough — found and fixed in this PR's second commit, after the first had shipped the defect. A changeset can sit pending for weeks carrying a true sentence and be falsified by a later change adding ONE line to its front matter; that is the shape check-changeset-overwrite.mjs calls its case 2, so this repository already expects to see it. Walking only base..head finds the single commit that added that line, sees the declaration in it, and reports BORN FALSE — accusing an author of writing something untrue weeks before the branch making it existed, which is the exact misreading this card forbids, on the case where the sentence most obviously stood on its own. If the claim is already standing at the base, the base's own front matter decides; the branch walk is reached only for a claim this change itself brought.

The two mandated defences

Two distinct floors (objectui#9744). A 0 from a reader that read nothing and a 0 from a clean corpus are the same character. Floor one: this change adds and modifies no changeset. Floor two, one level in: a corpus was read but every body declares no bump, so there is no front matter for a body to contradict. Neither prints what the other prints, neither prints the clean tick, and each is pinned with not.toContain against the other two.

Hermeticity is structural, not conventional. This reading takes NO ambient input: no GITHUB_EVENT_PATH, no pull request body, no environment. Its corpus is git diff --name-only under the root it was given. The pin runs the same fixture twice — once clean, once with GITHUB_EVENT_PATH pointing at a payload whose body is the carded contradiction verbatim — and asserts the section is byte-identical, plus asserts the variable WAS live for that run (the born-false reading above it does read it and its report moves), so the comparison cannot be vacuous.

Acceptance notes

  • The delivery seam is CLOSED, under an explicit surface extension. The first round stopped at the file-surface boundary and reported that changeset-presence.yml decided comment creation from findings.length + bornFalse.length, so a self-contradiction-only finding rendered its comment and was never posted. The seat extended the declared surface to that one file and the third commit adds one term to that one sum — ⛔ no new step, job, permission or context, and the command-parity doc pin is untouched because nothing here adds a command. The declared surface for this work is now scripts/, scripts/__tests__/, .changeset/ and .github/workflows/changeset-presence.yml, and nothing outside it was touched.

    The new expression, verbatim:

    findings =
      measured.findings.length +
      (measured.bornFalse ?? []).length +
      (measured.selfContradiction ?? []).length;

    ⭐ The pin asserts the new term on its own, ⛔ never the sum verbatim. A pin matching the whole sum reddens on a harmless rewording, and it gets repaired by pasting whatever the file now says — which is how a dropped term gets blessed. A third case walks all three terms, so the next reading's author is told, where they will be standing, that a reading without a term here reaches nobody.

  • The known false positive, documented instead of carved out: a body that declares a package to carry a DEPENDENT bump and says so — «@object-ui/react is unchanged; it bumps because core's types moved». That sentence pairs, and it is honest. It occurs zero times in the corpus --census reads, and a carve-out for a shape nobody writes is a hole nobody is watching.

  • The first commit shipped a dating defect and the second fixes it, kept as two commits rather than squashed in place. The gap is worth the record: the branch-walk-only reading passed all seven controls and every pin in the suite, and was wrong on the case the card cares most about. Leg C of the ablation below is the pin that now holds it.

  • One pre-existing assertion moved rather than widened: the born-false hermeticity case counted PASS lines across the WHOLE output. A third control suite prints its own, so that count is now scoped to the born-false section.

  • noted, not filed: --audit and --census are two population readings in one script with no shared frame; a future round that adds a fourth reading will want one.

Verification

gate exit
pnpm exec vitest run over all ten suites that read changeset-presence.yml 0 — 455 passed (10 files), including ci-cd-pipeline-doc.test.ts, which is what says no command-parity doc edit is owed
pnpm type-check:scripts 0
pnpm lint:root (the wired task that reaches scripts/; turbo run lint depends on it) 0 — 32 warnings, 0 errors, none in the three changed files
pnpm check:changeset-claims 0
pnpm check:control-bytes 0 — 7,913 tracked text files scanned
pnpm check:pending-changeset-literals 0
pnpm check:new-line-citations 0
pnpm check:unreferenced-sources 0
pnpm check:test-path-roots 0
pnpm check:shell-escape-residue 0
pnpm check:comment-mask-corpus 0
node scripts/check-changeset-presence.mjs 0 — no changeset owed; no published source or contract moved
pnpm check:required-check-set · pnpm check:merge-queue-head · pnpm check:governed-queue-guard 0 · 0 · 0 — the three self-tests that read workflow wiring; the last reports 185 cases pass
node scripts/check-governed-queue-guard.mjs --test (both rounds' paths) 0 — NOT GOVERNED; .github/workflows/** is not one of the five ruled surfaces

Ablation — the split's pins actually redden, three legs. Each leg runs from a committed tree, mutates one deciding line, proves the mutation reached disk by blob hash AND by occurrence counts (deciding line 1 → 0, injected marker 0 → 1), re-runs the suite, then restores with git checkout HEAD -- and re-verifies by blob hash and an empty git diff HEAD.

leg from mutation result
A 2dd38efbb dateClaim collapsed to 'born' (blob 5b05a3c6 → deaf6adb) WENT FALSE pin fails — 1 failed / 106 passed
B 2dd38efbb dateClaim collapsed to 'went' (blob 5b05a3c6 → 9b1ad4d2) BORN FALSE pin fails — 1 failed / 106 passed
C 74d723cb3 base-first dating removed (blob 29919d26 → 2ac4bd83) the pre-existing-claim pin fails — 1 failed / 108 passed
D 6ca8d4259 the selfContradiction term dropped from the workflow's comment gate (blob cb79f55d → 6d2f14ae) both new delivery pins fail — 2 failed / 109 passed, while the born-false term's own pin stays GREEN

⭐ Leg D is the one worth reading: the ablated sum is still valid JavaScript and still counts two of the three readings, so it looks healthy — and that is exactly the shape a whole-sum pin blesses. The born-false pin staying green through it is what shows the new pin is specific rather than a blanket string match. Legs A–C redden exactly one assertion each and move nothing else, so the distinction is pinned by the verdict itself and ⛔ not by a count that happens to move. The verdicts are read off the finding lines rather than searched for in the section, because the section's closing prose explains what a WENT FALSE verdict is and is not — a substring search there would have reported a verdict never reached, and it did on the first run.

Session reference, as prose because a footer does not survive a body edit here: session_015h79niBMyoB1xcaQje3uiz.


Generated by Claude Code

…diction (objectui#9841)

A `.changeset/*.md` that declares a package in its front matter while its body
says that package did not move contradicts itself, and both halves publish
verbatim into that package's CHANGELOG. Four structural gates read `.changeset/`
and all four judge the front matter; the two readings already in
`check-changeset-claims.mjs` judge a body against a diff. Nobody read the two
halves of one file against each other.

The falsification pass the card demanded ran first, and it CORRECTS the card.
Measured over every pending changeset in this tree plus every released CHANGELOG
entry beside a `package.json`, the card's own proposed coordinate — "a sentence
that names a declared package adjacent to a negation of change" — pairs
repeatedly and every single pairing is legitimate: it negates an ASPECT of the
package (a face, a surface, an export, one symbol, one file), never the package.
Building that coordinate would have shipped a reader that is wrong every time it
speaks. Requiring the negation to attach DIRECTLY to a bare package spelling,
with nothing between them but a copula, drops all of them and keeps the attested
instance. `--census` re-derives both numbers rather than recording either.

The BORN FALSE / WENT FALSE split is dated from the branch's own revisions,
because it cannot be read off the head. At `2bc8fd12e2` the carded changeset
already said its sentence and declared one package; at `21896172a9` the same
commit that moved 62 lines in `packages/types/src/overlay.ts` added the second
declaration. Against the merge base the net diff shows the file simply added,
with both halves present, which reads as born false. It was not. A range that
carries no revision of the claim is reported UNDATED rather than guessed at.

Two distinct floors, one level apart, neither printing what the other prints and
neither printing the clean tick: an empty corpus, and a corpus read whose bodies
declare no bump. The reading is hermetic by construction — it takes no
environment, no event payload and no pull request body — and the pin asserts its
section is byte-identical with `GITHUB_EVENT_PATH` poisoned.

Report-only and exit 0 throughout, inside the posture this gate already has. No
new gate, no new npm script, no new CI context.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
…ainst (objectui#9841)

The branch walk alone gets the oldest WENT FALSE there is exactly backwards. A
changeset can sit pending for weeks carrying a true sentence, and a later change
falsifies it by adding one line to its front matter — the shape
`check-changeset-overwrite.mjs` calls its case 2, so it is a case this repository
already expects to see. Walking only `base..head` finds the single commit that
added that line, sees the declaration in it, and reports BORN FALSE: an
accusation that the author wrote something untrue, weeks before the branch making
it existed. That is precisely the misreading this card forbids.

The base revision is read first. If the claim is already standing there, the
base's own front matter decides the verdict, and the branch walk is reached only
for a claim this change itself brought.

Pinned by a fixture whose changeset exists at the base declaring another package
and is falsified by one front-matter line at the head. The pin reads the verdict
off the finding line, so a reader that answered `born` here fails it rather than
satisfying a substring search.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
…cided (objectui#9841)

The comment step decides whether to CREATE the claim comment from a sum of
finding counts, and that sum carried two of the gate's three readings. A run
whose only finding was a self-contradiction rendered its comment and then never
posted it: measured, rendered, delivered nowhere. objectui#9140 put that channel
at zero answers out of four when findings went to the job log, which is why the
delivery exists at all, so shipping a third reading into a sum that drops it
would have been that ruling undone on purpose.

One more term, same expression, same step. No new step, job, permission or
context, and the doc-parity pin is untouched because nothing here adds a command.

The pin is asserted AS ITS OWN TERM rather than as the whole sum. A pin matching
the sum verbatim reddens on a harmless rewording, and it gets repaired by pasting
whatever the file now says — which is how a dropped term gets blessed. The new
one goes red for exactly one reason: this reading stopped being counted. A third
case walks all three terms, so the next reading's author is told, where they will
be standing, that a reading without a term here reaches nobody.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
@os-try-charles
os-try-charles marked this pull request as ready for review September 18, 2026 10:43
@os-try-charles
os-try-charles added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit 5365b4c Sep 18, 2026
35 checks passed
@os-try-charles
os-try-charles deleted the claude/issue-9841-changeset-self-contradiction branch September 18, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants