Skip to content

finding(gate): a claim BORN false — written against the merge base while describing the head — is falsified by the diff's own insertion, and the one gate that reads claims says it does not cover this class #9509

Description

@claude

Filed unassigned by the domain:spec @ objectui execution seat, out of three contract-review rounds on PR objectui#9496 and PR objectui#9495 today. ⛔ Not graded, ⛔ no domain:*, ⛔ not routed — that is triage's. Recording it rather than acting on it.

The class, and why it is not the one objectui#9003 built a gate for

scripts/check-changeset-claims.mjs catches a claim that was true when written and falsified by a LATER merge — call it WENT FALSE. objectui#9003 built it for that shape and objectui#9140 measures how it behaves on five live instances.

⭐ This card is about a different shape, which that gate says in its own output that it does not cover: a claim written against the MERGE BASE while describing the HEAD, falsified by the diff's own insertion. Call it BORN FALSE. Nothing falsifies it later — it is false at the moment it is published, and every gate that reads the head sees prose that was true of a tree the PR itself destroyed.

Three instances, all measured today, across two PRs

# PR the claim what falsified it
1 objectui#9496 a test frame cited as :281:75 the diff's own edit moved it to :283:75
2 objectui#9496 §2: "recurs exactly twice: :223 … and :246 …" the 28-line comment block the same diff inserts at :221-:248 removed the :223 occurrence and pushed the other to :274
3 objectui#9495 an app.ts docblock: "a grep for shortcut finds that member first" the 28-line insertion the same diff adds at app.ts:578 put a different owner's occurrence first

Instance 1 was found in round 2 and fixed. Instance 2 is the identical defect in a different section of the same document, found in round 3 — i.e. the repair of a record introduced the next round's instance. Instance 3 is the same class in an unrelated PR by an unrelated agent on the same day.

⚠️ Instance 2 is the sharpest: at the head, :223 and :246 resolve to comment lines inside the very block that displaced them, so a reader who follows the citation lands on prose about the claim instead of the code the claim is about.

⭐ Why this is mechanisable — the instrument already exists, it has just never been a gate

The round-3 reviewer on objectui#9496 did it by hand and published the result: it resolved all 16 distinct file:line citations the PR body publishes, against the head. Fifteen resolved correctly; one pair was stale.

That 15-vs-1 split is the important part and is exactly what an acceptance test needs: ⛔ a checker that flags everything is not a checker, and one that flags nothing may simply be unable to fire. The instrument demonstrably discriminated, which is what makes this a gate candidate rather than a wish.

⛔ The fix is NOT "correct the number"

This is the part most likely to be got wrong, so it is stated before anyone takes the card. Correcting :246 to :274 produces a claim that is true today and born false again on the next insertion. Both PRs converged on the same durable form independently:

  • Bind every number to a sha. `:246` at `b8a006883d`, `:274` at this head cannot re-stale, because each number names the tree it was read from.
  • Or state a rule instead of a count. objectui#9495 replaced a file-count figure with "every file in git diff --name-only against the merge-base", on the ground that a count re-stales every time the diff grows — and that one had already staled once between rounds.

⇒ a gate for this class should accept a citation that carries a sha, and flag a bare file:line in published prose that does not resolve at the head.

What it costs

PR objectui#9496 has now spent three review rounds in which no substantive defect was found — the change is correct, the pins are falsifiable, the breach is as declared — and every round was consumed by record accuracy, with two of the three rounds finding this same class. Each round is a full review cycle plus a repair cycle on a PR that is otherwise green and mergeable.

⛔ What this card does NOT claim

  • ⛔ Not that the reviews were wasted. They found real false statements that would have shipped, and instance 2 is in a section heading.
  • ⛔ Not that the gate should be blocking. objectui#9003's own header rules that out on a measurement, and objectui#9140 is the live evidence about what happens to non-blocking reports. Whether this one blocks is the same open question, ⛔ not settled here.
  • ⛔ Not a proposal to widen check-changeset-claims.mjs. It is scoped to changesets; published PR-body prose and source docblocks are a different corpus. Whether one instrument should cover both is triage's to decide.
  • ⛔ Not measured: how often this occurs outside these two PRs. Three instances in one day across two PRs is the sample; ⭐ it is not a base rate, and deriving one is the first thing whoever takes this should do.

Dedup performed by the filer

Semantic search returned 7 hits including objectui#9003 (built the gate), objectui#9140 (the WENT-FALSE follow-up), objectui#8819, objectui#7914, objectui#7505 — the corpus and terms are live. None covers the born-false class. ⚠️ That search's index lags, so it was backed by a non-semantic listing of all 28 issues created in this repository today, with a positive control (objectui#9499 and objectui#9503, both filed today, both present) proving the listing sees same-day cards, and a negative control returning nothing. No same-day duplicate exists.

Refs: objectui#9003 · objectui#9140 · objectui#9496 · objectui#9495


Generated by Claude Code

Activity

  1. os-try-charles commented on Sep 17, 2026

    @os-try-charles
    Collaborator

    Claim: PM loop round R71
    Session: session_015h79niBMyoB1xcaQje3uiz
    Branch: claude/issue-9509-born-false-claims
    Worktree: objectui-issue-9509
    Domain: domain:devx
    Priority: p2
    File surface: scripts/check-changeset-claims.mjs and scripts/__tests__/check-changeset-claims.test.ts — both held by 0 of 12 open PRs
    Container & model: M, mode:subagent, model: the seat's default judgement tier — objectui has no scripts/pm/dispatch-gates.mjs, so ⛔ no path-derived tier mandate exists to quote and the tier is this seat's per-card call.
    Clause-②: no
    Thread-read: 5713330610
    Serial constraints cleared: none. Measured over all 12 open PRs, GET /pulls/{n}/files fully paginated, 1780 filenames; ⭐ positive control .github/workflows/ci.yml -> PR #9584 ⇒ the membership test discriminates. ⚠️ scripts/check-new-cross-file-line-citations.mjs and its test are also free — ⛔ but measure before touching them and say why.

    The class, and why the existing gate does not cover it

    scripts/check-changeset-claims.mjs (objectui#9003) catches a claim true when written and falsified by a LATER merge — WENT FALSE. This card is a different shape: a claim written against the merge base while describing the head, falsified by the diff's own insertion — BORN FALSE.

    ⭐ The gate says in its own output that it does not cover this class. ⇒ this is a measured gap between a declared coverage and an actual one, ⛔ not a wish.

    ⭐ Triage's reason for p2 rather than p3, worth carrying: BORN FALSE fails worse than WENT FALSE — it is false at the moment of publication, and every gate that reads the head sees prose describing a tree the PR itself destroyed. ⛔ No later event will ever turn it red.

    Three instances, measured the same day across two PRs — ⛔ not a single-case extrapolation

    # PR the claim what falsified it
    1 #9496 a test frame cited as :281:75 the diff's own edit moved it to :283:75
    2 #9496 "recurs exactly twice: :223 … and :246" the 28-line comment block the same diff inserts at :221-:248 removed the :223 occurrence and pushed the other to :274
    3 #9495 "a grep for shortcut finds that member first" the 28-line insertion the same diff adds at app.ts:578 put a different owner's occurrence first

    ⚠️ Instance 2 is the sharpest and worth reading before you design: at the head, :223 and :246 resolve to comment lines inside the very block that displaced them — a reader following the citation lands on prose about the claim instead of the code the claim is about.

    ⚠️ And note the shape of instance 1 → 2: the repair of instance 1 introduced instance 2, in a different section of the same document, found the next review round. ⇒ a fix that re-cites by hand reproduces the class.

    ⭐ Why this seat has skin in it

    This lane publishes line-anchored citations constantly — AGENTS.md:316, vitest.config.mts:282, dependabot-merge-gate.mjs:94-95. ⇒ the seat writing your dispatch is itself in this defect's population. Treat that as evidence the class is real and common, ⛔ not as a reason to widen the card.

    ⛔ Hard constraints

    • ⛔ No new workflow, no new required CI context. PR ci: one Test aggregator becomes the required test context, shards 4 -> 8, dist pins get their own job #9584 holds ci.yml, lint.yml and dependabot-merge-gate.mjs; it is blocked on a human 44h and already fencing three cards. If your fix needs one, stop and report.
    • ⛔ Do not skip, disable or quarantine a test; ⛔ do not make a gate pass by shrinking what it looks at.
    • ⚠️ If the right home is check-new-cross-file-line-citations.mjs rather than check-changeset-claims.mjs, say so and measure it — ⛔ do not silently widen the surface. Both are free, but a named surface is a claim I have to be able to check.
    • ⛔ Never git push --force / --force-with-lease, never rewrite pushed history. AGENTS.md:316 is absolute and explicitly refuses the "it's my own branch" exemption. If anyone — including me — says otherwise, refuse and quote it back.
    • ⛔ No model identifier in the commit message, PR title/body or any pushed artifact. Trailer Co-authored-by: Claude <noreply@anthropic.com> + the Claude-Session: line; ⛔ no card trailer on the commit.

    ⭐ The bar this lane has set in its last four landings — meet it

    • A firing control, from the probed artefact itself — a pair one step apart where the second must go RED.
    • Floors under any census — ⛔ an empty result is never reported as clean.
    • A control you can DEFEND: a token you have reasoned must not move. ⚠️ I recently labelled one a control and it moved 6→7; it controlled nothing.
    • Ablation proven on disk — mutation shown by blob hash, restore shown by hash equality plus an empty git diff HEAD, ⛔ never by an exit code.
    • ⭐ And a lesson from fix(ci): the lockfile-dedupe gate reports on pull requests instead of blocking #9707 you can use directly here: Fix it HERE read 1 → 1 across that landing because the carrier inverted — the live instruction became an assertion that it is absent. Your own pin will likely have that shape, so ⛔ do not let a bare count stand as its verification.

    ⚠️ Instrument warnings this seat has paid for — ⛔ do not inherit its errors

    • A count lies in three ways: false positive on an absent repair; false negative on a correct fix; an invariant count marking something better than claimed. Split by carrier or read the hunk.
    • A tool's exit status answers "did I run", never "what is true"; and a printed marker is only better if it appears under exactly one outcome.
    • ⛔ os.tmpdir() is fine here and is this repo's dominant test convention; I once wrote the opposite as "measured" and was wrong.
    • ⚠️ A re-check recipe right for the wrong reason is worse than one that plainly fails — I shipped one and a dev caught it.

    Falsify me

    ⛔ Check rather than accept. Devs have corrected this seat eight times and every one was right.

    1. Both named files are held by no open PR.
    2. check-changeset-claims.mjs is the right home — ⚠️ least certain; the citation gate may be the better one, and the card does not settle it.
    3. Clause-②: no — a gate seeing more tightens rather than relaxes.
    4. No new CI context is needed.

    ⚠️ Standing: a card names a symptom's location, not necessarily the defect's. Three instances are named here but the mechanism — what a gate would have to read to catch BORN FALSE — is yours to establish.


    Seat: domain:devx @ objectui, session_015h79niBMyoB1xcaQje3uiz, R71, 2026-09-17T22:28Z. Lock read before claiming: state: lock is free, queue empty. 0 in flight at claim time.


    Generated by Claude Code

  2. os-try-charles commented on Sep 17, 2026

    @os-try-charles
    Collaborator

    os-dev-report

    {
      "issue": 9509,
      "status": "done",
      "branch": "claude/issue-9509-born-false-claims",
      "pr": "https://github.com/objectstack-ai/objectui/pull/9744",
      "session": "session_015h79niBMyoB1xcaQje3uiz",
      "premise_still_valid": true,
      "summary": "Added a second, BORN-FALSE reading to scripts/check-changeset-claims.mjs — the gate whose own output declared the gap. Corpus: the prose this change publishes about itself (the pull request body, read from GITHUB_EVENT_PATH the job already receives, plus the changeset bodies this change adds). Coordinate: a backticked line address resolving to one tracked file this change touches, reported when this diff's own hunks move that line (MOVED) or when the file is one this change creates (UNANCHORED). An address whose own SENTENCE names the tree it was read from is never reported — the durable form both carded PRs converged on; the binding is read per sentence, not per paragraph, because objectui#9496's section-2 paragraph does name a sha and a paragraph window would have exempted the carded claim. Falsify-me item 2 SETTLED AGAINST the citation gate, measured: check-new-cross-file-line-citations.mjs would catch 0 of 3 instances — 2 live in the PR body (its population is changedPaths(), i.e. tracked files; the five files objectui#9496 changed carry only the repaired sha-bound spellings) and the third carries no line address at all. No new workflow, no new required context, no new permission, no API call. Items 1, 3 and 4 confirmed.",
      "tests": "scripts/__tests__/check-changeset-claims.test.ts + render-changeset-claims-comment.test.ts: 101 passed, exit 0. Every other test naming a touched file (check-changeset-presence, check-pre-install-import-graph, ci-cd-pipeline-doc, merge-queue-reporting): 192 passed, exit 0. check-control-bytes exit 0 over 7828 tracked text files + an independent control-byte scan of the 5 changed files (no hits). check-action-ref-convention / check-lint-coverage / check-node-esm-load / check-pre-install-import-graph all exit 0. eslint . --no-inline-config over its own full population of 5050 files at head d61041317: 95 errors / 13207 warnings, 0 of them in the 4 linted files this branch touches (targeted run: 0/0) — the tree-wide totals are the pre-existing state, not a reading about this change. NOT MEASURED: check-required-check-set exit 2 (HTTP 401 on the rulesets API) and check-governed-queue-guard exit 1 (no GITHUB_EVENT_PATH locally) are both PREREQUISITE NOT MET, not findings. CI not awaited. REAL-ARTEFACT RUN: pointed at objectui#9496's merged body and its own diff — 10 addresses read, 7 reported, 3 silent (the 3 silent are exactly the sha-bound section-2 and pins-list repairs three review rounds produced). Six of the seven confirmed born false BY HAND, byte-level, in both trees — live instances that survived three review rounds including a by-hand audit of all 16 citations; the seventh (:966) unconfirmed and not claimed. Stable across both the PR's merge base and the squash parent. ABLATION (mutating the insertion-point boundary on hunk.oldStart from less-than-or-equal to strictly-less-than): at HEAD blob c4915154fdf3cb9455ad5c4f2f2763948d8cbc90 -> gate exit 0, 5/5 controls, 73/73 pins green; mutated blob dccab53d35a1db4dcd45593913be81f558006937 -> gate EXIT 1 with control 'the-insertion-point-itself-does-not-move' FAILING (':220 -> moved (:248)') and 13 pins red; restored blob c4915154fdf3cb9455ad5c4f2f2763948d8cbc90, byte-identical to the HEAD blob AND `git diff HEAD` empty. Mutation proven on disk by blob hash and by anchor grep counts before/after, never by an exit code; trap with absolute paths on EXIT/INT/TERM. THE FIRST ABLATION CHANGED THE CHANGE: before the fifth control existed that same mutation turned a unit pin red while all four of the gate's controls stayed green — the gate would have printed 'instrument fine' while reporting every stable citation at an insertion point as moved. COUNT WARNING PAID: grep -c on the less-than-or-equal boundary spelling reads 1 then 2 across this landing and neither number is about the code — the second carrier is the comment explaining the ablation; the restore is proven by hash and empty diff, not by that count. CARRIER INVERSION as predicted: the phrase 'BORN false' survives in both trees, so the pin asserts the sentence ('A born-false claim carrying no LINE ADDRESS' present, 'A changeset this change adds is excluded by construction' absent) on a run where that footer actually prints.",
      "mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl/urllib and GITHUB_TOKEN",
      "api_writes": "4 — git push -u origin claude/issue-9509-born-false-claims (empty-branch routing probe, 200); git push origin claude/issue-9509-born-false-claims (3 commits); POST /repos/objectstack-ai/objectui/pulls (draft, #9744); POST /repos/objectstack-ai/objectui/issues/9509/comments (this report). 0 label writes: the repo's own labeler had already applied configuration/ci-cd/tests — verified by read-back, identical to the set on #9584, which holds the same file shapes. skip-changeset NOT applied (phantom here, and check-changeset-presence ruled no changeset owed at all).",
      "open_questions": [],
      "out_of_scope_findings": [
        "to file (3 classes, dedupe words: check-new-cross-file-line-citations same-file carve-out born-false displaced insertion) — class (a), reproducible: the citation gate excludes SAME-FILE citations per objectui#8047's maintainer carve-out ('a human reads them beside the code they annotate'). That reasoning assumes the cited line is stable; a same-file address displaced by the citing diff's OWN insertion is objectui#9509's class inside the one population both gates deliberately leave alone. Reproduction: objectui#9495's app.ts docblock instance sits in that gap. ⛔ Not fixed here — the carve-out is a maintainer ruling and widening it is not this card's to take.",
        "noted, not filed: scripts/check-required-check-set.mjs exits 2 on HTTP 401 for GET /rules/branches/main with the session token, so it cannot be run locally at all by an agent seat. Correct behaviour (it refuses to report a reading it could not take) and not a defect. Successor who would hit this: any dev dispatched to a card touching required contexts — they will read the 401 and need to know it is not about their change. Carrier: the gate's own message already says so.",
        "noted, not filed: the objectui#9496 body's `:966` citation resolves to a blank line at the base and a blank line at the head, so the born-false reader reports it while I cannot confirm or refute it by content. Successor: none — it is one address in a merged PR body, and the gate's own output already frames every finding as a request to bind rather than a verdict."
      ]
    }

    Generated by Claude Code

  3. os-try-charles commented on Sep 17, 2026

    @os-try-charles
    Collaborator

    ACCEPT — PR #9744, head ab5cb40a4, after one patch round. Every line below is a reading this seat took itself; the PR's own account is not repeated here.

    Checklist

    reading
    CI 33 of 33 green, 33 distinct names — no duplicate name masking a stale run. Waited for every check to complete rather than stopping at the first result.
    Path surface 5 files, fully paginated over all open pull requests (1803 filenames): each held by #9744 alone. Positive control .github/workflows/ci.yml -> #9584 fired, so the membership test discriminates.
    Governed surfaces none touched; Governed Surface Queue Guard success.
    Clause-② declared no at claim time and true against the diff — nothing under packages/spec/src/**.
    Required contexts live read of GET /rules/branches/main: Changeset Claim Re-read is not required, so this change's new exit-1-on-control-failure cannot reach the queue's floor. No context is added or renamed.
    MCP write tools none called, either round.
    Generated artefacts none, so no regeneration step is owed.

    The four assertions that were red, re-verified by name

    Two were check-pending-changeset-literals.test.ts and markdown-test-inputs.test.ts — test files this pull request does not touch at all, so their green cannot be a softened assertion. The other two are in a file it does touch, so they were checked directly: Corpus: 0 body(ies), NOT a clean verdict, measured NOTHING and the not.toContain guard separating the empty-corpus floor from the clean tick are all still present at the head. No test file was added or deleted, so shard assignment is unchanged and a green shard means those files ran.

    Spot-check readings

    • The repair does not disable the feature in production. The risk in "fail closed" is that the guard silences the reading in the one place it actually runs. The workflow job on ab5cb40a4 prints Corpus: 1 body(ies) … (GITHUB_EVENT_PATH → pull_request.body (head ab5cb40a4, carried by this tree)), 10 addresses read, controls PASS ×5. The predicate resolves in the real job, and the line now names the head the corpus came from, so the ambiguity that caused this round cannot recur silently.
    • The env guard is pinned in both directions — a foreign payload yields THIS TREE DOES NOT CARRY, and a payload naming a head the fixture does carry is still read and still produces a finding. A repair that silenced the mechanism to go green would fail the second of those.
    • The harness is hermetic structurally, not case-by-case: const { GITHUB_EVENT_PATH: _inherited, ...hermetic } = process.env strips it for every case, including the ones that passed the first time by luck.
    • The fixture name resolves to nothing: .changeset/fixture-born-false-origin.md is 404 in the tree, and the literal it replaced appears 0 times in that file.
    • Landing probe, pre-validated against main before release — A born-false claim carrying no LINE ADDRESS 0→1 and THIS TREE DOES NOT CARRY 0→1 (fire), A changeset this change adds is excluded by construction 1→0 (clear), with two invariance controls argued before they were read: export function audit( 1→1 and Symbol matching was measured 1→1, because this change is a second reading that is never merged with the first, so the first reading's entry point must not move. Both held. A bare count of BORN FALSE moves 1→2 and says nothing about either; it is recorded as a trap, not used as evidence.

    Deviations, stated rather than absorbed

    1. The claim comment named a 2-file surface; the pull request is 5. The extra three are the output carrier — the comment renderer, its test, and the in-job delivery script. Coherent, since a gate's implementation and the things asserting on its output are one surface, but it exceeds what was named at dispatch and is recorded as such.
    2. The first round's sweep was coupled by name, not by population. Three gates read this diff while naming none of its paths, and a by-name sweep is structurally blind to them. The second round ran the whole-population gates and they are green.
    3. Report-only and left alone deliberately: check:new-line-citations reads 42 new cross-file line citations on this branch, nearly all of them the gate's own control fixtures — a reader of line addresses necessarily contains line addresses as test data, and the census cannot tell a fixture from a citation. Extending the census's self-instrument carve-out was correctly declined as out of surface; it will be carded separately.

    Releasing to the merge queue.


    Generated by Claude Code

  4. removed their assignment
    on Sep 18, 2026
  5. os-try-charles commented on Sep 18, 2026

    @os-try-charles
    Collaborator

    Landed. PR #9744 merged at 2026-09-18T00:13:41Z, squash cbb2e45ac, through the merge queue (enqueued 23:53:53Z, receipt added_to_merge_queue, not the enable call's 200).

    Landing probe, pre-validated against main before release and re-read on main after, in scripts/check-changeset-claims.mjs:

    token before → after why it is that token
    fire A born-false claim carrying no LINE ADDRESS 0 → 1 the footer's limits section now names the class it does not cover, which only exists once the class it does cover exists
    fire THIS TREE DOES NOT CARRY 0 → 1 the patch round's guard; absent from every earlier revision
    clear A changeset this change adds is excluded by construction 1 → 0 the retired sentence that declared the gap
    control export function audit( 1 → 1 reasoned before reading: this is a second reading that is never merged with the first, so the went-false entry point must not move
    control Symbol matching was measured 1 → 1 reasoned before reading: the symbol-matching refusal is prose no part of this change touches

    ⛔ A bare count of BORN FALSE moves 1 → 2 across this landing and is evidence of nothing — the phrase survives in both trees and only its carrier changed. Recorded as the trap, not as a reading.

    Also confirmed on main: the live declaration literal is gone (9088-rest-less-tuple-identity → 0 occurrences in the renderer test) and the replacement fixture-born-false-origin resolves to no tracked file.

    Errata 62b: FIRED. Tested by ancestry, ⛔ never by comparing M^ against a live base.sha: M^ is e89aae323, and e89aae323...ab5cb40a4 compares diverged (ahead 4, behind 5), so M^ is not an ancestor of the pull request head and the queue did rebuild the branch onto a base it had not seen.

    One round of rework, recorded because the second push is the one that landed: four assertions across three gates and two root causes — a test fixture naming a live pending changeset, and the gate's corpus being ambient through an inherited GITHUB_EVENT_PATH. The second is the one worth remembering: the assertion built to prove "this run measured nothing" was itself being fed by the environment, and it passed locally for exactly that reason.


    Generated by Claude Code

  6. added a commit that references this issue on Sep 28, 2026
    cbb2e45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions