Skip to content

feat(gate): read BORN-FALSE claims — an address this change's own diff moves - #9744

Merged
os-try-charles merged 4 commits into
mainfrom
claude/issue-9509-born-false-claims
Sep 18, 2026
Merged

os-try-charles merged 4 commits into
mainfrom
claude/issue-9509-born-false-claims

Conversation

@os-try-charles

Copy link
Copy Markdown
Collaborator

Fixes #9509

Clause-②: no — this change makes a gate see MORE. Nothing is relaxed: the went-false half keeps its exclusion of a change's own changesets verbatim, exit stays 0 on findings, and no context becomes required.

What was actually wrong, and where

scripts/check-changeset-claims.mjs printed, in its own output, that a born-false claim was outside it entirely. objectui#9509 carded three same-day instances across two pull requests. I resolved all three against the merged trees before designing anything, and the card names the symptom's location, not the defect's:

# carried in why the existing instruments cannot see it
1 objectui#9496's pull request body not a file — no tree-scanning gate has it in its population
2 objectui#9496's pull request body same
3 objectui#9495's app.ts docblock carries no line address at all — it is an ordinal claim ("a grep finds that member first")

scripts/check-new-cross-file-line-citations.mjs would have caught 0 of 3, measured rather than assumed: its population is changedPaths(), i.e. tracked files, and its five syntaxes all require a line address. Measured directly: the five files objectui#9496 changed carry the repaired, sha-bound spellings and not the born-false ones — the false prose was never in the tree. ⇒ the citation gate is the wrong home, which is the seat's own least-certain claim, falsified.

check-changeset-claims.mjs is the right one for a reason that is mechanical rather than thematic: it is the only gate here that already runs on pull_request and already delivers its finding onto the pull request, so the corpus where two of three instances live is reachable from it with ⛔ no new workflow, ⛔ no new required context, ⛔ no new permission and ⛔ no API call — GITHUB_EVENT_PATH is a file on the runner, read the way check-governed-queue-guard.mjs already reads it.

The mechanism

  • Corpus — the prose this change publishes about itself: the pull request body, plus the .changeset/*.md bodies this change adds. ⛔ Not the tree at large; that population has a reader already.
  • Coordinate — a backticked line address resolving unambiguously to one tracked file this change touches. Same resolution rule as the existing half.
  • Question — arithmetic, never semantic. MOVED: this diff's own hunks map the cited base line elsewhere, or delete it. UNANCHORED: the file is one this change creates, so the number can only have come from a tree that exists nowhere outside this pull request — instance 1's shape, where the frame moved twice between revisions of one branch.
  • Carve-out — an address whose own sentence names the tree it was read from is never reported. ⚠️ Per sentence, ⛔ not per paragraph: objectui#9496's section 2 paragraph does name a sha, so a paragraph-wide window would have exempted the exact claim the card is about.
  • The ask is never "correct the number." Correcting one produces a claim true today and born false on the next insertion — the card states that before anything else. The gate asks for the durable form both pull requests converged on independently.

Firing control, taken from the probed artefact

The controls run objectui#9496's real geometry — a 28-line insertion after old line 220 and a rewrite of old line 223, as git diff reports it on the merged commit. That geometry is attested outside this branch: objectui#9496's own body and the docblock it landed both publish the figure :246@b8a006883d = :274@Head. A mapper that drifts by one fails the control rather than reporting a clean branch, and the number cannot be re-derived to match a wrong implementation.

Five controls, both directions — ⛔ a suite that only ever fires proves nothing:

PASS  unbound-address-into-a-line-this-diff-moves: :223 -> moved, :246 -> moved (:274)
PASS  the-same-address-bound-to-a-sha-is-silent: (silent)
PASS  an-address-this-diff-does-not-move-is-silent: (silent)
PASS  the-insertion-point-itself-does-not-move: (silent)
PASS  an-address-into-a-file-this-change-adds-is-unanchored: ...:281 -> unanchored

A control failure exits 1 in a gate that is otherwise report-only, and says why: a differential reader that reports zero because its differ broke is indistinguishable from prose with nothing wrong in it. Pinned as failable — a judge that lies fails the suite instead of passing it.

Run against the real artefact

The reader, pointed at objectui#9496's merged body and its own diff (stable across both the pull request's merge base and the squash parent):

10 addresses read · 7 reported · 3 silent. The 3 silent are exactly the section-2 and pins-list repairs three review rounds produced — the discrimination.

⭐ Of the 7, six are confirmed born false by byte-level content comparison in both trees, i.e. they are live instances that survived three review rounds including a by-hand audit of all 16 citations in that body:

  • the section-1 evidence table says imported-defaults.ts:221-228 is the tuple arm and that :223 is byte-for-byte const rest = def.rest ? walk(def.rest) : undefined;. At the merged head those lines are the 28-line comment block the same diff inserted; the arm is at :249 and :223 was rewritten. This is instance 2's exact shape, in the one section the rounds never bound.
  • registry-meta-carry-9102.test.ts:833:7 — base :833 is the assertion frame; that same line is :885 at the head.
  • likewise :163, :295, :857:7.

⚠️ Those six confirmations are mine, by hand. The gate asserts none of them: it reports that a number was read from a tree this change replaced and asks for it to be bound. The seventh (:966) I could not confirm either way and do not claim.

Ablation — proven on disk, ⛔ never by an exit code

Mutating the insertion-point boundary (line <= hunk.oldStart to line <):

leg blob result
at HEAD c4915154fdf3cb9455ad5c4f2f2763948d8cbc90 gate exit 0, 5/5 controls, 73/73 pins green
mutated dccab53d35a1db4dcd45593913be81f558006937 gate exit 1, control the-insertion-point-itself-does-not-move FAILS reporting :220 -> moved (:248), 13 pins red
restored c4915154fdf3cb9455ad5c4f2f2763948d8cbc90 byte-identical to the HEAD blob, and git diff HEAD empty

The first ablation of this change changed the change. Before the fifth control existed, that same mutation turned a unit pin red while all four of the gate's own controls stayed green — the gate would have printed "instrument fine" while silently reporting every stable citation at an insertion point as moved. The boundary control exists because the ablation found that, ⛔ not because it was anticipated.

⚠️ And a count lesson, paid in this branch: grep -c 'line <= hunk.oldStart' reads 1 then 2 across the landing, and neither number is about the code — the second carrier is the comment explaining the ablation. The restore is proven by the blob hash and the empty diff, ⛔ not by that count.

Verification

  • scripts/__tests__/check-changeset-claims.test.ts + scripts/__tests__/render-changeset-claims-comment.test.ts101 passed.
  • every other test naming a file this branch touches (check-changeset-presence, check-pre-install-import-graph, ci-cd-pipeline-doc, merge-queue-reporting) — 192 passed.
  • check-control-bytes exit 0 over 7828 tracked text files; an independent control-byte scan of the five changed files finds none.
  • check-action-ref-convention, check-lint-coverage, check-node-esm-load, check-pre-install-import-graph — exit 0.
  • eslint . --no-inline-config over its own full population, 5050 files, at this head: 95 errors / 13207 warnings, 0 of them in the files this branch touches (targeted run over those four files: 0/0). The tree-wide totals are the pre-existing state, ⛔ not a reading about this change.
  • ⚠️ check-required-check-set exit 2 (HTTP 401 for the rulesets API) and check-governed-queue-guard exit 1 (no event payload locally) are PREREQUISITE NOT MET, ⛔ not findings — recorded as NOT MEASURED.

check-changeset-presence.mjs verdict on this diff, verbatim: "No source or published contract of a released package changed in this range, so no changeset is owed." ⇒ no changeset.

Surface — declared wider than dispatched, and measured

The dispatch named two files. A gate's implementation and the tests asserting on its output are one surface, and delivery is part of that output: a born-false-only run must create the comment, or the one half nothing later will ever turn red lands in the job log objectui#9140 measured at zero answers out of four. So five files:

file held by
scripts/check-changeset-claims.mjs 0 of 12 open PRs
scripts/__tests__/check-changeset-claims.test.ts 0 of 12
scripts/render-changeset-claims-comment.mjs 0 of 12
scripts/__tests__/render-changeset-claims-comment.test.ts 0 of 12
.github/workflows/changeset-presence.yml (one expression in an existing job) 0 of 12

Census: all 12 open pull requests, GET /pulls/{n}/files fully paginated, 1807 filenames (floor asserted — a zero-length census aborts). Positive control: .github/workflows/ci.yml, lint.yml and scripts/dependabot-merge-gate.mjs all resolve to #9584, so the membership test discriminates. ⛔ No governed surface is touched: none of the five paths matches GOVERNED_SURFACES.

⛔ What this does not do

  • ⛔ It does not read instance 3's shape. An ordinal claim with no coordinate needs the gate to decide what a sentence means, which is the one question triage fenced off when the gate was built. Stated as a limit in the gate's own output, where the old limit used to be.
  • ⛔ It does not judge truth. Every finding is a request to bind a number, on a channel that still exits 0.
  • ⛔ It does not see a body edited without a push; pull_request does not fire on that.

🤖 Generated with Claude Code

https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz


Generated by Claude Code

…f moves

`check-changeset-claims.mjs` covered WENT FALSE only and said so in its own
output. This adds the second reading, over a corpus of its own: the prose this
change publishes about ITSELF — the pull request body (from the event payload
the job already receives) plus the changeset bodies this change adds.

The coordinate is an unbound backticked LINE ADDRESS resolving to one tracked
file this change touches, and the question asked of it is arithmetic rather than
semantic: does this diff's own hunk map the cited base line somewhere else, or
is the file one this change creates, so the number can only have been read from
a tree that exists nowhere outside this pull request?

An address whose own SENTENCE names the tree it was read from is never reported
— that is the durable form both carded pull requests converged on, and a gate
that reported it would teach authors to unbind.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
…trols

The arithmetic cases use objectui#9496's own geometry, because that pull
request published the figure independently of this tree: its body and the
docblock it landed both state `:246`@`b8a006883d` = `:274`@head. A mapper that
drifts by one fails there rather than reporting a clean branch.

Two controls guard the instrument rather than the prose: a judge that lies
fails the control suite instead of passing it, and a corpus that was read but
spells no address is reported as neither clean nor a finding — one tick for
"nothing to judge" and "everything checked out" teaches the reader to skim it.

The gate's own footer inverted rather than moved: it used to name born-false as
a class it cannot see, and now names the one shape still out of reach. ⇒ a
count of the phrase across this landing reads 1 -> 1 and proves nothing, so the
pin asserts the sentence, on a run where that footer actually prints.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
…posed

Measured, ⛔ not anticipated. Mutating `line <= hunk.oldStart` to `line <`
turned the section-6 pin red while ALL FOUR of the gate's own controls stayed
green — so the gate would have printed "instrument fine" while reporting every
stable citation at an insertion point as moved. A gate that manufactures
findings on a report-only channel is how a channel gets muted.

A fifth control reads the anchor line itself. git spells a pure insertion as
"after old line N", so N is the last line it does not move.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
… live declaration

Two root causes, four red assertions, three gates.

1. A renderer fixture spelled `.changeset/9088-rest-less-tuple-identity.md`, a
   declaration the tree actually carries. `scripts/markdown-test-inputs.mjs`
   offers only documents that EXIST, so the literal became a ledger candidate on
   the pre-version tree and would have had nothing to resolve to after
   `changeset:version` deleted it — reddening the release lane days later, in a
   job no reader of a pull request sees. Renamed to a `fixture-` spelling, which
   belongs to neither the generated `adjective-animal-verb` namespace nor this
   repository's issue-number-and-slug one.

   ⛔ No ledger entry is added, deliberately: with no resolvable document left
   the scanner stops seeing that file entirely, and an entry for a file the
   scanner no longer sees is itself a `stale-test` finding.

2. ⭐ `GITHUB_EVENT_PATH` is exported to every process on a runner, and the gate
   read it whenever `--pr-body` was absent. So in CI the gate under test picked
   up the real pull request body of the build that happened to be running and
   counted it as "the prose this change publishes about itself", in a temp
   repository that has nothing to do with it — off by exactly one body. The
   empty-corpus floor, whose whole job is to report that a run measured NOTHING,
   did not hold in the one environment that actually runs it.

   The env read stays — it is the whole reason this needs no new workflow — but
   it is now gated on a predicate about the TREE rather than about how the
   process was launched: the payload names `pull_request.head.sha`, and a tree
   that cannot resolve that commit is not the tree the event is about. Fails
   closed and prints why, ⛔ never silently. Pinned in both directions, and the
   test harness strips the variable for every case rather than for the two that
   reddened — the others survived it by luck, not by hermeticity.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants