Uh oh!
There was an error while loading. Please reload this page.
fix(devx): compare the census+ledger UNION in --fix, so a shifted citation is not a population change - #13575
Merged
os-project-manager merged 1 commit intoAug 31, 2026
Conversation
…ation is not a population change `check-system-context-census --fix` partitioned a file's page anchors into read anchors and ledger-excused citations by LINE: an anchor was a read anchor unless it sat on a line `NON_READ_ANCHORS` currently locates. That compares two different coordinate systems. The page's anchors are pre-shift by construction -- rot is the only reason `--fix` runs -- while a ledger row locates itself by NEEDLE in the current tree. So a file whose ledger-excused citation also moved had that citation counted as a read anchor, and the gate reported a POPULATION change over a population that never moved. Measured twice, in two lanes, on two files: `security-plugin.ts` (7 reads + 1 citation, all displaced +20/+19 by an unrelated bootstrap edit, zero `isSystem` lines added or removed) refused with "page anchors 8 distinct read line(s), census finds 7"; `rest-server.ts` (6 + 2) with "7 ... finds 6", the contradicting `[ledger-row-unused]` line sitting in the same run's output both times. The remedy compares the page's DISTINCT anchor lines against the UNION of the two classes of line the page may anchor -- census read sites and ledger citations -- and aligns them by order. That removes the miscount in both directions, including the dangerous one, and buys a postcondition the per-class comparison cannot state: the rewrite is a bijection onto the file's anchorable lines, so every file `--fix` touches evaluates clean by construction. ⛔ The refusal itself is unchanged as a safety property: a site that genuinely arrives or vanishes is still refused, now with both compared counts, both target classes and the set difference printed, so an author can tell a displacement from an arrival without running the census in two trees by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
This was referenced Aug 31, 2026
os-project-manager
marked this pull request as ready for review
August 31, 2026 02:13
os-project-manager
enabled auto-merge
August 31, 2026 02:13
Uh oh!
There was an error while loading. Please reload this page.
os-project-manager
deleted the
claude/issue-13490-census-fix-false-population
branch
August 31, 2026 02:30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#13490
The defect, in one sentence
--fixdecided whether a page anchor was a read anchor or a ledger-excused citation by asking whether its line is oneNON_READ_ANCHORScurrently locates — and those two numbers live in different coordinate systems. The page's anchors are pre-shift by construction (rot is the only reason--fixis running); a ledger row locates itself by NEEDLE in the current tree. So a file whose ledger-excused citation also moved had that citation counted as a read anchor, and the gate announced a POPULATION change over a population that never moved.Reproduced exactly, then fixed
The
domain:servicesdisplacement table from PR #13514, replayed on this tree by inserting 20 lines abovesecurity-plugin.ts:1392and deleting one line between 2491 and 3808 — mutation proved on disk by marker count andgit hash-objectagainst the HEAD blob, restored the same way.Census lines after the displacement, byte-identical to the reported table:
16 problem(s) over 145 anchors and 109 census sites--fixpage anchors 8 distinct read line(s), census finds 7· 0 rewritten--fixOK — 109 elevation read sites in 20 packages across 45 files, all anchored; 145 anchors resolve, 27 declared non-readThe nine rewrites are exactly the renumbers the other lane had to hand-apply.
domain:servicescomment annotates4524 → 4543as "theNON_READ_ANCHORSrow-2 needle". It is not:4524is a real census read (if (context?.isSystem) return true;), and the row-2 needle (3.5. [#3004]) is at 2491 → 2511. Both are in the table, so the reproduction and the diagnosis are unaffected — only the annotation was wrong.The safety property is measured, not assumed
Three genuine population changes, each mutation proved on disk, each still REFUSED with 0 anchors rewritten and the gate left red:
already anchored 8 of 9·target, NO anchor: 4525already anchored 0 of 9— uniform displacement and a count mismatchalready anchored 7 of 7·anchor, NO target: 4524That last column is the card's central complaint answered: an author can now tell a displacement from an arrival from the refusal itself, instead of running
isystem-census.mjs --jsonin two trees by hand.⭐ A second failure direction, previously unreported
Ablating the fix (old counting arm restored, new cases kept) turned up the half nobody had measured: the old arm did not only refuse falsely, it also silently ACCEPTED a real population change. When a read site arrives while the ledger citation is stale, the miscounted citation makes the two counts agree by cancellation —
refusedcomes back empty and--fixrewrites the ledger citation onto the newly arrived elevation read site, reporting it as a successful re-anchor on a security-documentation page. The refusal H3 calls the safety property was defeated by the same defect, in the direction that leaves no error message.Why the union, and not "count the reads correctly"
Two candidate shapes, and they differ:
The union is the safer one and is what landed. It also buys a postcondition the per-class comparison cannot state: the rewrite is a bijection from the page's distinct anchor lines onto the file's anchorable lines, so for every file
--fixtouches,evaluateis clean by construction — no missing site, no unexplained anchor, no unused ledger row. That is pinned behaviourally, not argued in a comment.It also removes the miscount in the dangerous direction, which exclusion-by-line got wrong too: a stale READ anchor that lands on a line the ledger now occupies was subtracted, the counts could agree by cancellation, and the rewrite mapped the survivors onto each other's rows — a page that is wrong and GREEN, because both classes stay covered. That crossing is real: on the second occurrence
rest-server.ts:1267was simultaneously the second inbound seam's new home and a read row's stale anchor.⛔ Stated rather than papered over: alignment is by ORDER, so a pure displacement is reconstructed exactly, but a REORDERING that moves a cited construct past another one inside the same file is indistinguishable from a shift on line numbers alone. No line-only tool can tell those apart, and
evaluatecannot either. That limit is written into the code.Self-test
Seven new cases (35 total, was 28). Ablation: with the old counting arm restored and the new cases kept — mutation proved on disk by injected-marker count 1, live
describeRefusalcall count 0, and a blob hash differing from HEAD; restored and proved bygit diff HEADempty plus a matching blob hash — six of the seven go red:The seventh (
an unanchored site in the crossing file is REFUSED too) stays green under ablation by design — it is the safety control that must hold in both arms, so it is not expected to discriminate.Scope
Only the counting arm of
--fix, plus its refusal message and self-test.NON_READ_ANCHORSis untouched — it behaved correctly throughout both occurrences, locating by needle and re-resolving on its own.scripts/isystem-census.mjsis untouched (#13548's fence).Verification, at
afd593b48node scripts/check-system-context-census.mjs --self-test→all cases passed(35 cases)node scripts/check-system-context-census.mjs→OK — 109 elevation read sites in 20 packages across 45 files, all anchored; 145 anchors resolve, 27 declared non-readscripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackderives from the real change set, plus both convention-triggered obligations for editing a gate script (bare-root-worklist.mjs --self-test,check:pm-dispatch-gates) — all exit 0pnpm lint(full repo, 5542-file ESLint population read from its own config) under the shared verify lock →VERDICT command-exit 0check:nul-bytesexit 0, plus a direct control-byte scan of the diff: no hitscheck-test-completeness.mjsexit 3 = NOT MEASURED, by the gate's own words — neither red nor greenNo changeset: this publishes from no package. Repo precedent for
scripts/-only gate changes isskip-changeset, which is applied to this PR.Generated by Claude Code