…ot remembered (#8659)
`widget-dom-leak-sweep.test.tsx` states its reading in prose in ten places.
Every row it records is derived from the arrays and is therefore always current
truth; the COUNTS were not — they were a second, hand-written copy of the same
facts that nothing read. objectui#8659 measured six of them wrong at once,
produced by three correctly scoped PRs (#7564, #7958, #8034 plus the `element:*`
additions) that each moved an array and left the paragraph quoting it behind.
⛔ The harm is realised, not potential: this docblock is the first thing a
burn-down slice reads, and a dispatch had already quoted `95 of 158` out of it
as a baseline — a slice believing it would have reported `95 -> 94` where the
truth was `90 -> 89`.
objectui#5632's slice corrected all six texts once. This change is deliberately
NOT a seventh correction: it is the mechanism, in two legs, because the obvious
implementation stops after the first.
1. `DOCBLOCK_COUNTS` (hand-written, the numbers as the prose writes them) against
`MEASURED_COUNTS`, re-derived on every run from `COMPONENTS_PLAIN_TYPES`,
`COMPONENTS_SPECIAL_TARGETS`, `COMPONENTS_LEAK_GROUPS`, `OWNED_NAMESPACES`,
`ALL_TARGETS` and `LEAK_LEDGER`. One side is DERIVED; a constant compared
against another constant asserts nothing.
2. The prose against `DOCBLOCK_COUNTS`. Ten `QUOTED_COUNTS` statements plus the
five-row reading table are read back out of this file's own source, rooted at
`import.meta.url` (never the cwd — `scripts/check-test-path-roots.mjs`), and
checked. Without this leg the first only MOVES the hand-written copy from the
prose into a constant: a slice reddened by leg 1 repairs the constant, the
paragraph stays wrong, and the defect is where it was with one more place to
forget.
Move an array and leg 1 is red; repair only the constant and leg 2 is red.
Re-derived on today's `origin/main` (704e695), NOT copied from the card, whose
table was measured at 51f3d8d: components 159 targets / 89 ledgered rows / 70
clean / 4 shapes / 140 plain types / 155 readiness matches, and 91 of 182 across
the whole sweep. All ten statements already agreed, so the six corrected by
objectui#5632 have held — with ONE exception, which is the card's own argument
arriving a seventh time: `READY_OVERRIDE_REASONS` shrank from six entries to
four and the sentence pointing at it still said "the other six".
Prose changes are confined to that class. The stale live reading gains today's
figure; two incidental `158`s that were counting nothing lose their literal; one
arrival reading that read as present tense ("registers **158 types across
five**") is put in the past. Counts that SAY they are historical — `119 of 158
ON ARRIVAL`, the phantom-clean census, the per-PR burn-down rows — are left
exactly as they are, and the new section states that rule for the next number
added here.
⛔ No ledger row is edited and no existing assertion is weakened: 203 pre-existing
cases pass unchanged alongside the 27 new ones.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
Part of #8659
Implemented by the
domain:uios-devseat, sessionsession_01UzHd6hDYatoDn17BuwKxnZ(recorded in prose as well as in the footer, because a body edit downgrades the footer's session form).What this delivers, and what it deliberately is NOT
⛔ Not a seventh text correction. objectui#5632's slice already corrected all six statements once, and that is precisely why this card exists — the next slice inherits the same unasserted prose and it rots again.
⭐ The deliverable is the mechanism: after this change, an edit that moves an array without moving the paragraph that quotes it goes RED. Triage's route 1 (comment 5643087570), in the same file, with the counts re-derived from
COMPONENTS_PLAIN_TYPES,COMPONENTS_SPECIAL_TARGETSandCOMPONENTS_LEAK_GROUPS.Two legs, because the obvious implementation stops after the first
DOCBLOCK_COUNTSagainstMEASURED_COUNTS— the named constants (the numbers as the prose writes them, hand-written on purpose) against the value re-derived on every run from the arrays, plusOWNED_NAMESPACES,ALL_TARGETSandLEAK_LEDGER. One side is DERIVED; a constant compared against another constant asserts nothing.DOCBLOCK_COUNTS— tenQUOTED_COUNTSstatements plus the five-row reading table are read back out of this file's own source (rooted atimport.meta.url, never the cwd, perscripts/check-test-path-roots.mjs) and checked.⇒ Move an array and leg 1 is red. Repair only the constant and leg 2 is red. The paragraph has to move.
The counts, re-derived on today's
origin/main⛔ Not copied from the card, whose table was measured at
51f3d8d66. Measured on704e69593(the merge base of this branch) and unchanged after mergingorigin/main:packages/componentstargetsCOMPONENTS_PLAIN_TYPES(140) +COMPONENTS_SPECIAL_TARGETS(19)COMPONENTS_LEAK_GROUPStarget lists (86 + 1 + 1 + 1)COMPONENTS_LEAK_GROUPS.lengthCOMPONENTS_PLAIN_TYPES.lengthreadyis the shared selectorREADY_OVERRIDE_REASONSOWNED_NAMESPACES.componentsLEAK_LEDGER/ALL_TARGETSAcceptance item 3 — the six statements have held, with one exception, and it is the card's own argument arriving a seventh time
The six texts objectui#5632 corrected all still read the measured value today. One count the card's table did not list had drifted since:
READY_OVERRIDE_REASONSshrank from six entries to four (objectui#5630), and the sentence pointing at it still said "The other six are inREADY_OVERRIDE_REASONS". That is the seventh instance, found the same way as the first six — by someone happening to read the paragraph — and it is now pinned.Prose changes, and the rule they follow
Confined to the class the card names. Every count in this docblock is now one of two things:
DOCBLOCK_COUNTSwith a statement inQUOTED_COUNTS, or it is the next drift;119 of 158 ON ARRIVAL, the phantom-clean census, the per-PR burn-down rows) — it must SAY it is historical, in the sentence itself.So: the readiness sentence gains today's figure (
Today 155 of 159 targets match it; the other four ...); one arrival reading that read as present tense (registers **158 types across five**) is put in the past, because a live-sounding tense on a frozen number is exactly how158came to be quoted as a current target count; and two incidental158s that were counting nothing ("doing that 158 times would be 158 hand-transcribed strings") lose their literal rather than gaining a pin.⛔ No ledger row is edited, and no existing assertion is weakened or relaxed. 203 pre-existing cases pass unchanged alongside the 27 new ones.
Ablation — both legs, RED and GREEN arms
Script:
trap-restored, absolute-rooted, mutation proved on disk by anchor count before the run, restoration proved by blob hash. Run from the committed implementation (5707dff1f), so the restore leg points at aHEADthat already carries it.GREEN arm (both legs):
Test Files 1 passed (1) / Tests 230 passed (230)at5707dff1fand again at the merged head55b2ccee5.RED arm, leg 1 — one array moved, constants and prose untouched. 8 cases red, each naming the new number:
RED arm, leg 2 — the same mutation, with
DOCBLOCK_COUNTSrepaired to the new truth and the prose left behind. Leg 1 goes GREEN (noevery named count is the measured onefailure survives) and the prose leg takes over, in seven statements plus the table:tsc: every assertion added is a runtime one, so nothing in this pin is erased before it runs.type-checkis reported separately below and is green on bothtscpasses.Verification
All heavy runs went through the shared verify lock; exit codes captured by redirect-then-capture, never through a pipe.
pnpm exec vitest run packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsxTests 230 passed (230), lockVERDICT command-exit 0(re-run on the merged head)pnpm --filter @object-ui/app-shell type-checkVERDICT command-exit 0— bothtsc --noEmitandtsc -p tsconfig.test.jsontsc -p tsconfig.test.json --listFilestype-checkreally covers itpnpm --filter '@object-ui/app-shell^...' buildVERDICT command-exit 0(the closuretype-checkneeds)node scripts/check-changeset-presence.mjs1 source file(s) ... declares 1 changeset(s) ... Every one of them has an EMPTY frontmatter — declared as releasing nothingnode scripts/check-changeset-fixed.mjs/check-changeset-no-major.mjsAll workspace packages are in the changeset fixed group./No changeset declares a major bump.node scripts/check-test-path-roots.mjs --self-testthen the gate22 cases pass/OK (1899 filesystem call(s) ... 963 self ...)node scripts/check-entry-guard.mjs --self-testthen the gate63 cases pass/93 scripts/ file(s) — no entry guard outside the baselinenode scripts/check-control-bytes.mjsOK (scanned 7438 tracked text file(s); skipped 85 binary)node scripts/check-doc-snippet-types.mjsEvery covered documentation snippet compiles against the built types.(after the scoped 35-task build; unbuilt it is exit 2 = PREREQUISITE NOT MET, not a pass)node scripts/check-doc-example-types.mjsEvery covered @example compiles, or fails exactly as its ledger row declares.node scripts/check-lint-coverage.mjs46/46 packages linted, 0 with outstanding errorsnode scripts/check-vi-mock-override-shape.mjs/-specifiers/-inheritOKnode scripts/check-unreferenced-sources.mjsEvery shipped source file in every covered package is reachable.node scripts/check-new-cross-file-line-citations.mjs0 new citation(s)node scripts/markdown-test-inputs.mjs --audit47 candidate test files, all adjudicated— this pin reads a.tsx, not a markdown document, so no registry entry is owednode scripts/check-governed-queue-guard.mjs --test ...NOT GOVERNED — 2 path(s) checked against 5 governed surface(s); none matched.Lint, as a declared narrowing. Repo-wide
eslint .is CI's run, not this PR's. Targeted:npx eslint --no-inline-config --format jsonon the changed file reports 1 file, 0 errors, 0 warnings, exit 0 — the file count read from eslint's own JSON output. The population that file belongs to is read from eslint's own config rather than guessed:eslint --print-configon it resolves 118 rules and an emptyparserOptions. The config declares noprojectand noprojectServiceanywhere, so type-aware linting is not enabled and this diff cannot move the verdict of any file it did not touch. Measured on the final commit,55b2ccee5.Acceptance notes
domain:devxtodomain:uire-grade is triage's and stands; nothing here touches it.needs:contract-reviewis carried because Clause-② was declaredyesat dispatch under the "not sure ⇒ yes" rule. ⛔ Not revised on delivery, though the measured answer is that nothing published moves: the only file changed is undersrc/__tests__/**, and@object-ui/app-shellpublishesdist,src/styles.css,README.md,CHANGELOG.mdandLICENSE—dist/__tests__does not exist.@objectstack/specdoes resolve from this worktree (node_modules/@objectstack/spec, 17.4.0); nothing in this change needed to read it.origin/mainwas merged at55b2ccee5and the merge touchedpackages/data-objectstackandscripts/, with no conflict and no overlap on the one test file this PR edits.Noted, not filed
scripts/markdown-test-inputs.mjsadjudicates markdown test inputs only. This pin establishes the same hazard for a source file read as a test's input, and nothing enumerates that class. Not filed: no queued card and no PR is heading for that file, and the class has exactly one member today (this one) — a card with no carrier would be the tenth hand-written artefact this card's own triage declined to create. Carrier if one appears: the next pin that reads a non-markdown file as data.check-doc-component-types' header records the package-README census as "26 across 12 files" — PR #8111's own per-file table lists 8, and a re-run on today's tree reads 8 #8484, docs(guide): two more live document/file counts on ci-cd-pipeline.md declare no measurement point —181 docs pagesand16 files, plus a third copied from a source constant #8629, finding(docs): a fourth stated docs-page population on ci-cd-pipeline.md —556-page docs build, hyphenated so objectui#7448's noun pattern misses it, and copied a second time into the gate's own header #9004 and this one). What made it mechanisable is that the file holding the prose also holds the arrays, so no cross-file rooting was needed. Where that is true elsewhere, this is the technique; where it is not, the pin needs a path and the path needs a root.Generated by Claude Code