The #650 guard pinned every pageSize call site by absolute line:column, making the registry a function of every OTHER file in the repo. The guard was BORN RED. #651 moved AutoTuneScreen.tsx up ten lines and FillerPresetsScreen.tsx down seventy-two, and merged to main BEFORE the guard's own PR (#675) did -- so the registry, authored against a pre-#651 base, was stale the instant it landed. Its own merge run was CANCELLED, so nothing reported it; the red first surfaced on the next push (#676's merge, which touches no web/src file and is not the cause). One ordering accident, not a recurring pattern -- but the exposure is general, because every PR is green against its own base and the breakage exists only in the merge result. Identity is now (file, kind, value). New/removed/changed sites all still fail. The MULTISET comparison is preserved, so a shared identity must be discovered exactly as many times as it is registered. The scanner's positional pageSizeSiteId is untouched: pageSizeScan.test.ts asserts real AST positions against fixed fixtures, the opposite case, with no churn to remove. The one case this costs is stated rather than implied: a same-identity substitution within one file (delete a registered site, add a different unreviewed one with the same kind and value token) now passes. Narrow, and caught only incidentally before. Named in the guard and the record because "costs no coverage" is a claim that outlives whoever made it. Failure reports still print the discovered line:column -- identity and diagnostics need not share a format, and a bare id was useless in a file holding two such sites. Registry reconciled with #651: Playlists/RerunCollections lost their bounded windows to the shared searchLibraryPickerOptions, now registered in its place. Adds a 'search-bounded' class for that shape. Adds a 'deviation' class rather than laundering a live defect into a compliant-looking label. Reconciling the registry surfaced a §3b violation (#685, filed): CollectionsScreen's AddItemsDialog degrades to an unfiltered whole-type window on an empty query and surfaces nothing. Both existing labels would have been false, and either would have made the guard vouch for behaviour that does not exist. Deviation entries must name a tracking issue, enforced by a structural field -- a #\d+ scrape of the note passed with the reference deleted, because notes legitimately cite historical issues. Corrects SmartCollectionDialog to class-b: it does render a totalCount badge, which is class-b's defining evidence. fixes #684 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6.2 KiB
key, title, status, since, supersedes, superseded-by, rule, signals, mechanics
| key | title | status | since | supersedes | superseded-by | rule | signals | mechanics |
|---|---|---|---|---|---|---|---|---|
| testing.enumerating-guard-identity-not-position | 2026-07-27 — an enumerating allow-list guard keys its registry on IDENTITY, never on a source position (#650, #651) | active | 2026-07-27 | none | none | A guard that cross-checks a hand-reviewed registry against call sites discovered across the whole repo must key each entry on properties INTRINSIC to the site — file, kind, and the value source text — and never on its absolute line or column. A registry keyed on position is a function of every other file in the repo, so a branch that never touches the guard can invalidate it; and because each PR is green against its own base, that failure is structurally invisible pre-merge and lands on `main` after review and after the merge gate. Dropping the position keeps every mutation the guard exists for — a NEW site, a REMOVED site and a CHANGED value each still fail, since each changes the identity multiset — and costs exactly ONE case, which must be stated rather than implied: a SAME-IDENTITY SUBSTITUTION within one file (delete a registered site, add a different unreviewed one with the same kind and value token, net-zero count) now passes. A REPORTED failure still prints the discovered line:column, because identity and diagnostics need not share a format. Comparison stays a MULTISET count rather than set membership, so two sites in one file sharing an identity must be discovered exactly that many times and a third occurrence still fails. A SCANNER test that asserts real AST positions against FIXED inline fixtures is the opposite case and keeps its line/column identity — it has no churn, because its input does not move. | pageSize call-site guard · enumerating allow-list · registry went stale · UNREGISTERED and STALE report · line churn · line drift · semantic merge conflict · guard born red · registry stale on arrival · cancelled run hid a red · registry keyed on line:column · same-identity substitution residual gap · deviation classification · a registry must not launder a defect into a compliant label · multiset count not set membership · `pageSizeSiteId` vs `registryId` · scanner positions vs registry identity · paths: `web/src/api/pageSizeCallSites.guard.test.ts`, `web/src/api/pageSizeScan.ts`, `web/src/api/pageSizeScan.test.ts` · issues: #684, #650, #651, #676, #644 | Registry identity is `${file}:${kind}:${value}` (`registryId` in the guard); the scanner keeps `pageSizeSiteId` (`${line}:${column}:${kind}:${value}`) for `pageSizeScan.test.ts`. |
The #650 guard enumerates every pageSize call site in the SPA and cross-checks it against a
hand-reviewed registry in both directions. That part worked. Its follow-up review (F5/M-6) then made
each entry's identity the site's absolute line:column, to distinguish two pageSize properties on
one line. Sound about disambiguation, wrong about the cost.
The guard was born red, and the sequence is the argument. #651 moved AutoTuneScreen.tsx up ten
lines and FillerPresetsScreen.tsx down seventy-two, and merged before the guard's own PR (#675).
The registry, authored against a pre-#651 base, was stale the instant it landed. A cancelled CI run
is what let it through: the guard's own merge run was cancelled, so nothing reported the red, which
first surfaced on the next push (#676's merge — which touches no web/src file and is not the
cause). ci.cancelled-is-not-a-verdict, paying out.
That is ONE ordering accident, not a recurring pattern; the honest count, because the argument needs
no inflation. The exposure is general anyway, because CI cannot see it coming. Each PR is green
against its own base, so the breakage exists only in the merge result and surfaces on main after
review and after the merge gate.
Identity should be what makes the site the thing being guarded. A site's file, kind and value
source text determine whether it is reviewed; where it sits in the file does not. The multiset
comparison is what preserves what F5/M-6 was actually protecting and must not relax to set
membership: TrashScreen.tsx's two PAGE_SIZE requests must be discovered exactly twice, so a third
occurrence still fails. The converse case is pageSizeScan.test.ts, which correctly KEEPS positions
— verifying real AST positions is its subject and its fixtures cannot drift — which is why this
introduced a separate registryId rather than changing pageSizeSiteId underneath it.
What it costs, stated rather than implied. A same-identity substitution inside one file now passes: delete a registered site, add a different unreviewed one with the same kind and value token, net-zero count. Narrow, and the old identity caught it only incidentally — it fired on every position change, so a reviewer conditioned to re-pin line numbers would likely have waved it through. Accepted knowingly and named in both places, because "costs no coverage" is the kind of claim that outlives whoever made it, and a guard described as exhaustive stops being re-examined.
Diagnostics are not the identity. Dropping position from the comparison key is the fix; dropping
it from the failure message was collateral damage. The discovered direction prints line:column
alongside each unregistered id — no churn, since positions appear only in an already-failing message.
A registry must not launder a defect into a compliant-looking label. Reconciling it surfaced a
live §3b violation (#685): a picker degrading to an unfiltered whole-type window on an empty query,
surfacing nothing. Both labels would have been false — search-bounded asserts a required query,
class-b a rendered totalCount — and either would make the guard vouch for behaviour that does not
exist. Hence a deviation class whose entries must name a tracking issue, enforced by a STRUCTURAL
field rather than a #\d+ scrape of the note: the first version of that test passed with the
tracking reference deleted, because the note legitimately cited two historical issues.
The generalisation: a guard whose input is the whole repository must not encode anything the whole repository can change without meaning to. Position is the common instance; a line count, a file ordering or a byte offset would all fail the same way.