fix(scripts): resolve override members in the symbol-anchor accept set - #16895
Conversation
Refs #16821 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Refs #16821 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
The member matcher in scripts/symbol-anchors.mjs carried a hand-enumerated nine modifiers -- readonly, static, public, private, protected, abstract, declare, async and the generator star -- and `override` was the one TS member modifier absent. A symbol anchor naming a real `override` declaration returned `unresolved-symbol`, and the gate's own remedy text left the author only a weaker, file- or class-level anchor. Adds `override ` to that alternation, a rationale comment recording that the enumeration is hand-made and has now been demonstrated incomplete once, and twelve self-test cases -- one per member spelling, each paired with its own negative control -- with the battery floor moved 69 to 93. Purely additive: nothing that resolved before stops resolving. All three corpora that register against this resolver were re-measured before and after and their output is byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
ACCEPT in substance — PR #16895, reviewed against GitHub and the tree, ⛔ not against the reportFlip + arm withheld until the running checks report. One correction below, and it goes the card's way. What I re-drove myself
⭐ The self-test is the real deliverable here, not the token. One token would have closed the reported miss and left the enumeration exactly as unprovoked as it was. Twelve spellings each paired with a control means the next absent modifier fails at the list, which is the defect class the card is actually about. ⛔ Correction — one out-of-scope note in the report is WRONG, and the card is rightThe report says the card's illustrative anchor Measured on The declaration is in that file, spelled ⇒ Nothing in the fix changes; the note is struck so the card's evidence is not retired by mistake.
|
Part of #16821
symbolResolutionClassresolved a member declaredasync/public/protected/private, and returnedunresolved-symbolfor the same member declaredoverride. Onetoken is added to the member matcher's accept set. Nothing that resolved before stops
resolving.
What the regex actually says — read in this branch, not inherited
Line numbers rot, so the site is identified by its token sequence, not its number. In
scripts/symbol-anchors.mjs,scriptSymbolClassbuilds five declaration matchers; the thirdis the member matcher, and its modifier alternation read, verbatim:
Nine alternatives, hand-written, and
overrideis the one TS member modifier absent fromthem. Triage named line 336 for that matcher; in this branch it now sits at line 371 because
the explanatory comment landed above it. The alternation above is what identifies it.
The matcher one line up — the
function/class/interface/type/enum/namespace/moduledeclaration matcher — does not carryoverrideeither, and correctlyso:
overrideis not a legal modifier on any of those keywords. Only the member matcher waswrong, and only the member matcher is touched.
Why this is a bug and not a shrug
The enumeration is hand-made and has now been demonstrated incomplete once. The card
raised "should the accept set be derived rather than enumerated?" as an open worry; reading
the source turns it into evidence. A hand-written list of nine that is missing one of the
things it is a list of is not a hypothetical failure mode — it is the observed one.
Two more facts that set the grade, both restated from the card and triage:
overridemember is wherea subclass restates a base contract, which is where a citation most needs to be checkable.
that the corpus censuses were structurally unable to contain an
overridemember, and acensus number reads like coverage.
276
overridemember declarations underpackages/, of which 207 are thestatic overridemetadata on CLI command classes (flags,examples,args,description). Every one of them was unanchorable by a symbol anchor.The change
Plus the rationale comment above it, twelve self-test cases, and the battery-floor ledger row.
The alternation stays a free-order
*group: TS fixes the written order (accessibility,static,override,readonly,abstract), but pinning that order here would refuse aspelling for being unidiomatic rather than for being absent, which is not this rule's job.
Census: every corpus that resolves through this matcher, before and after, same stroke
The matcher is shared, so a widened accept set moves every registered census at once. Three
gates import the resolver, not two —
check-system-context-censusregisters a corpus throughthe same
symbolResolutionClass, and it has a--fixwriter, so it is measured here too.Taken on this branch at commit
1a33d4f4b3(message-only amend to7185a8bc1c, identicaltree), all three in one stroke before the edit and again after it:
check-adr-symbol-anchorscheck-scripts-symbol-anchorscheck-system-context-censusdiffover the full captured output of each gate reports no change. All three exit 0 beforeand after.
Why nothing moved, stated rather than assumed. All three gates were already green, so
every anchor in every corpus already resolved; a purely additive accept set can only move an
anchor's class upward —
unresolvedtodeclaration, orliteraltodeclaration— neverdown. The only reclassification available was
literaltodeclaration, and none of the ADRcorpus's 26
literalanchors names anoverridemember, so no class changed anywhere.And the
34will not move by itself. The fix removes a structural exclusion; it does notadd anchors. That number rises only when an author writes an anchor naming an
overridemember — which, until this lands, they could not do.
Evidence
In place, in the worktree. The card's own repro copies the module to
/tmpand breaks itsrelative import of
git-env.mjs; that failure is unrelated to the finding, so the resolver wasdriven in place instead.
Before the change, against real tree files (not synthetic sources), the same probe after:
The control refuses in both legs, so the instrument is live in both. The
bulkCreaterow isthe class-upgrade case in the wild: it was resolving as
literal, the weaker class, becauseits name happens to appear as a quoted string in that file. That is the mechanism by which a
census could have moved — and the table above is the measurement showing that in these three
corpora it did not.
Ablation. With the twelve new self-test cases in place,
overridewas removed from thealternation again and the on-disk mutation verified by counting both the injected and the
removed token before running:
Restored from
HEAD(not from the index) and re-verified: restored blob hash734d554f099e8a583ba084774d058c12d692c532equals theHEADblob,git diff HEADempty, andthe restore leg is green again. Predicted direction was "turns red"; observed direction was
"turns red".
Self-test. Twelve member spellings, each driven by name, each paired with its own negative
control riding the same source —
plain,async,public async,protected async,private,override,override async,protected override async,public override,static override,override get,override readonly property. One token per finding is notthe repair: an enumeration is worth exactly what it is provoked with. The battery floor moves
69 to 93 with its ledger row, measured, not guessed.
Gates. All 31 families derived by
scripts/pm/dispatch-gates.mjsfor this change set wererun; 31 derived, 31 run, 0 NOT-MEASURED, 0 UNRUN, every one exit 0 — reconciled with
--ran. The three census gates above were run on top of that, since two of them are not inthe derived set for a
scripts/**diff.Card relation, and one residue to name
The relation is declared here in the body only, as
Part of #16821— the implementation iscomplete and the card is left for the dispatching seat to close deliberately rather than by
merge parser.
wip:commits at the base of thisbranch carry a
Refs #16821trailer, written under a brief that turned out to be wrong:check:partof-closing-keywordRULE 2 forbids any card-relation trailer in a commitmessage,
Refsincluded. The tip commit was amended clean before it was pushed(
commitRelations()returns[]for it); the two earlier ones were already pushed, andrewriting a pushed branch is not an action this seat takes. They are named here so nobody has
to discover them.
Two things this PR does not do
shared resolver whose governing ruling is that a corpus joins by registration with no
second implementation. That is a maintainer's call and is left open, with the evidence for
it written into the comment at the site so the next author reads it there. The argument is
in the report back to the dispatching seat.
SqlDriverreads keys off caller objects through(obj as any)at 7 sites while 3 parameter types declare none of them — a class, not a third coincidence (after #4311tenancy, #16570indexes) #16711's gate header keeps its class-level#TursoDriveranchor; this only makes the member-level form possible. Whether that header should be
revisited is not decided here and is not touched.
Note on the illustration: the card's example anchor named
initObjectsinpackages/drivers/driver-turso/src/turso-driver.ts. On today'smainthat file namesinitObjectsonly in comments — the declaration lives in theSqlDriverbase — so that exactanchor would not resolve regardless. The finding itself is unaffected and was re-verified
independently: that file still declares 27
overridemembers of its own, three of which areused as the live probe above.
No changeset: nothing published moves. Repo-root
scripts/is shipped by no package'sfiles[], the root manifest isprivate, and the resolver's exported names appear nowhereunder
packages/(checked with a firing positive control).skip-changesetapplied.Generated by Claude Code