You ratified (A): build symbol-level sight — so Plato can answer "who references symbol S," not just "who imports file F." The frontier pass says: (A) isn't one build. It's three separable pieces with very different costs, and the tidy "just unify on SCIP" story welds them together in a way that hides a trap.
src/adapters/scip-core/index.mjs · buildScipGraph(). Nodes are file paths; edge de-dup key is ${consumer}\0${producer} (no symbol); the edge literal never carries occ.symbol, even though the loop reads occ.symbol/occ.symbol_roles right there. Downstream reach.mjs has zero symbol awareness (grep-verified: 0 hits). Minimal change: re-key de-dup on (consumer, producer, symbol), attach the symbol, add a symbol filter to reach. Caution: the only prior attempt to surface SCIP symbols in Plato (value-flow-edge.mjs) is 55KB, C#-only, and unwired into any tool (grep-verified). "One line" is a lower bound, not the real effort.scip-typescript" would replace a parser Plato owns and trusts with an external Node indexer that runs out of memory on big repos — for Plato's own home language. The parser we already have visits the imported name and just discards it; keeping it is a ~10-line change.
edge-completeness.mjs), not SCIP. Its ImportDeclaration visitor already walks specifiers but only special-cases createRequire (grep-verified). Retaining ImportSpecifier.imported.name is self-contained, zero new dependency — but non-compiler-verified (no cross-check that the name is truly exported, the one thing SCIP would add). "We already ingest SCIP" is the availability signal, not a fitness argument, for the one language where we own a better-fit tool.S" is wrong 30–60% of the time unless you fence it: dynamic dispatch, reflection, DI containers, codegen, serialization all hide references no static index can see. Plato has this fence pattern at the file level; the symbol level needs its own — and that's a genuine build, not free. Without it, symbol-reach manufactures the exact false "all-clear" Plato exists to kill.
inbound-fence.mjs → buildInboundCompletenessLabel): virtual/interface dispatch · reflection · DI/string registries · serialization · codegen/macros/partials · cross-language FFI · framework attribute magic · non-graph SFC hosts (.razor/.cshtml/.vue/.astro) · unresolved/any-typed occurrences · local-scheme/external omissions · unindexed/parse-failed files · definition-vs-reference role gap · test-only/stale coverage. Contract: never a bare "these are all references" — always PARTIAL / CANNOT-CERTIFY / none-found-but-grep-to-confirm. Evidence: static call-graph recall is 39–70% vs runtime truth; Roslyn (scip-dotnet's base) is imprecise on interface dispatch both directions.symbol_roles over the DePrism .scip fixture (are references cleanly tagged, or is the re-key non-deterministic?). 2. Currency latency — cold index time + delta after a one-line commit (the big one). 3. oxc-vs-scip-typescript spike — lines to extend oxc vs scip-typescript index time/RSS on Plato's own repo. 4. Virtual-dispatch false-complete — an interface + a concrete impl called only via the interface; if "who references ConcreteImpl.Method" misses the dispatching caller, the fence is proven mandatory.