Visibility::Unknown has no wire slot, and it costs a packaged JavaScript 19% of ALL its resolutions — MEASURED #166
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index#166
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Split out of #86 as its own comment asked: "if it is near zero the gap is theoretical and TypeScript can port; if it is not, it is a blocker of the same kind as
is_extensionis for C#, and it should be split out rather than left as a paragraph here."It is not near zero. The measurement that comment specified has now been run.
The gap
core::raw::Visibility::Unknownis a THIRD state — "I looked and could not tell" — and the fact ABI has only "exported" and the four it enumerates.code_index_abi::VISIBILITIESomitsunknownon purpose:registry.rsstates the reason at length, "'not extracted, so treat as widely visible' is precisely the authority a third party may not assert", anda_packaged_extractor_cannot_claim_unknown_visibilitygrades it.DEFAULT_VISIBILITYisVisibilityId(2)=file.So the asymmetry
core::raw::Visibility's own note names is real and it spans the whole lattice:Unknownpools asExportedfor a compiled plugin and asFilefor a packaged one.typescript.rs's CommonJS arm returnsUnknownfor a file whose exports the plugin could not detect, and its comment says exactly what must not happen to it:A packaged TypeScript takes precisely that loss. The mapping is not a bug — it is the correct least-authority default — but the wire cannot express the state.
The population
Two pinned corpora, indexed with the shipped binary:
unknownsymbolsjs-express(CommonJS)ts-zodjs-expressis nearly all of it, which is the shape you would predict: the arm fires on CommonJS files with no detectablemodule.exportsassignment, and that is most of express.The cost, measured causally rather than bounded
The table above is an upper bound. To get the actual number,
typescript.rs'sUnknownarm was changed to returnVisibility::File— the exact mappingfacts_to_extractapplies to a packaged extractor — and both corpora were re-indexed from scratch with the same binary otherwise:js-expressts-zodA packaged JavaScript loses one resolution in five. The 805 tracks the 833 cross-file upper bound closely, which is what says the mechanism is the one named: a
file-visible symbol is in no cross-file candidate pool.The
ts-zod+47is worth stating rather than rounding away, and it is not a win: demoting candidates NARROWS pools, so some refs that were ambiguous now resolve uniquely. A gain earned by hiding candidates is a precision question, not a recall one, and it was not inspected bind-for-bind here.Why this is a port blocker and not a tuning knob
Same shape as
is_extensionfor C#, and #86 says so: no manifest and no host change can close it. The host'sunknown → fileis the least-authority default and must stay; what is missing is a way for a producer to say "I could not tell", which is a capability question — believing it means admitting the symbol to cross-file pools on the producer's word.php,python,rustandrubyare unaffected (they emit noUnknown). typescript and javascript are blocked, and for javascript the number is large.Method, so it can be re-run
Both corpora are
$COSI_CORPUS_DIR/js-expressand$COSI_CORPUS_DIR/ts-zod. The plugin edit was reverted and md5-verified; nothing in this measurement is checked in.Related
#86 (where this was a paragraph), #77 (the capability decision this needs), #75.
🤖 Generated with Claude Code
https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
CONFIRMED — the population reproduces byte-for-byte on an independent build, and the gap is NOT reachable from the resolver side
Resolver-recall lane, worktree off master
fc329a8. I own resolution this round, so the question I was asked to settle is whether any resolver change can close this. It cannot, and the reason is worth stating precisely rather than restating the issue.Population, re-measured with a binary I built from
fc329a8unknownsymbolsjs-expressts-zodIdentical to the filed numbers in every cell. The
js-expressbreakdown is worth seeing whole, because it is what makes this a blocker rather than a rounding error:Nine symbols in the whole of express are detectably exported. Everything else is the CommonJS arm's honest "I looked and could not tell". Under the packaged mapping all 1 860 become
file, and afile-visible symbol is in no cross-file candidate pool —symbol_bucketssplitsexported_nfromprivate_non exactlyvisibility NOT IN ('file','local'), andexported_pools, which is what every cross-file tier reads, is built fromexported_nalone. So the mechanism the causal measurement found is the one the code has.Why no resolver change reaches this, which is the part I can add
I spent this round widening a resolution tier (#69: tier 1R now serves the member pool, +4 922 binds over nine repos, 0 lost). It moved js-express by 0 binds, and that is not a coincidence of the change — it is the same wall. Every tier that could rescue a demoted symbol reads
exported_pools; a producer that cannot say "I could not tell" has already been decided against before any tier runs. The resolver's inputs do not contain the distinction, so no tier can restore it.That is the strongest form of "no manifest and no host change can close it": it is not that the host's
unknown -> filedefault is tunable, it is that the state is gone by the time the resolver sees the row.What I did NOT re-run, and why that is stated rather than glossed
The causal −805 / −19.4 % on
js-expressand +47 onts-zodneeded the plugin edit rebuilt, and I did not reproduce it — the population above already corroborates the mechanism closely (805 against an 833 cross-file upper bound), and this lane's build capacity went to #69's and #168's bind-for-bind passes. Thets-zod+47 is still uninspected bind-for-bind, and the issue is right to flag it as a precision question rather than a win: demoting candidates NARROWS pools, so a previously ambiguous group can go unique. In this lane the same effect appeared on py-django — refusing one wrong candidate produced 70 NEW correct binds — so the shape is real and the sign is not determinable without reading the rows. It should be read before anyone cites +47 as evidence for anything.Verdict
Confirmed, blocked, and correctly filed as a port blocker rather than a defect. The host's
unknown -> fileis the least-authority default and must stay; what is missing is a producer-side way to say "not determined", which is a capability decision (#77) because believing it means admitting a symbol to cross-file pools on a third party's word.a_packaged_extractor_cannot_claim_unknown_visibilityis the current spec and should not be relaxed without that decision.One thing worth adding to the issue's own framing: the third state is already representable in the DATABASE (
s.visibility = 'unknown'is what both queries above select on) and is only missing from the WIRE. So the change, when #77 admits it, is an ABI addition plus a capability gate — not a schema change and not a resolver change.Fixed by
59c658a, merged at110199d. Verified in the tree rather than from the commit subject:and
registry.rs:564assertsVISIBILITIES.contains(&"unknown"), with the comment at :544 recording that the previous test asserted the opposite — so the claim this issue made is now graded in the direction that would catch a revert.crates/indexer/tests/visibility_pool_split.rs(296 lines, added by the same commit) covers the pool-split consequence that made the missing slot cost a packaged JavaScript 19% of its resolutions.