Five hardening fixes to the #1136 MEDIUM (graph-derived) tier:
- #1141: updateNode() now writes the segment vocabulary like insertNode()
does — framework post-extract renames (NestJS route prefixing) left the
new name permanently unsearchable (the old rows orphaned, the backfill
gated on an EMPTY vocab, so even a full re-index re-created the drift).
- #1142: new CodeGraph.healSegmentVocabIfEmpty() — the hook opens the
graph without sync, so a database migrated from pre-vocab schema kept
the MEDIUM tier dormant until some unrelated sync ran. The hook heals
on first use (one SELECT when populated; lock-aware, defers to a
running sync) and records noop-vocab-empty when it can't.
- #1144: a name whose only nodes are file/import kind is skipped instead
of falling back to surfacing an import statement as a matched symbol;
import specifiers no longer enter the vocab at all (shared
isSegmentableKind gate across insertNode/updateNode/rebuild page query)
since they can never be surfaced and only inflate rarity statistics.
- #1145: plural variant folding is keyed on English plural spelling —
bare-s plurals no longer mint a bogus -es sibling (services→servic),
unambiguous sibilant-es plurals no longer mint a bogus -s sibling
(classes→classe), trailing -ss singulars no longer strip (class→clas);
genuinely ambiguous endings (caches/databases) still emit both keys.
- #1146: getSegmentCoOccurrence folds variants to their original word
inside the SQL (CASE mapping + COUNT(DISTINCT word)) so a plural pair
of ONE word can't tie with a genuine two-word match and crowd it past
the pre-fold ORDER BY/LIMIT; the JS re-check stays as the honesty layer.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>