honesty: no PROJECT-scoped asserted-name census (project_overview / resolution_gaps) and find_callees has no aggregate at any scope — the per-row marker and page count shipped in d0279c0 #93
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#93
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?
Found by the #86 lane, confirmed independently by the #89 lane. Both agree it belongs on its own rather than folded into either.
The fact exists and never reaches the reader
#86 gap 1 relaxed the ABI's strongest check. Normally a package's ref must satisfy
name == src[name_span]— that is what stops a package inventing a binding at a span it does not own. A ref may now setderived_nameand be exempt, so that its span means "where this fact came from" rather than "the bytes of this name". Ruby's Rails DSL needs it:has_many :postsemits atyperef namedPostat the span of the literal:posts.The exemption is properly gated — a split grant word, a capability the manifest must request, a role bit outside
GRANTABLE_ROLESso it is unforgeable in both directions, folded into the activation digest. None of that is the problem.The problem is what a reader is told afterwards:
crates/indexer/src/packages.rs:4026SCIP_MASKat all threeRefRowsites incrates/daemon/src/local_index.rs(2747, 2951, 3095)grep -rn 'derived_name|DERIVED_NAME|524288' crates/mcp-server/src/returns nothingSo
find_callers,find_referencesandchange_impactreport such an edge as resolved fact, with nothing in the payload saying the binding was ASSERTED rather than READ.core/src/raw.rsdocuments it as an operator SQL query (roles & 524288) — honest for an operator holding the database, invisible to the agent that actually consumes the row.Why this is the shape this repo keeps fixing
It is
name_fallback_unmeasuredandpackage_set_consultedagain: a fact we hold, that changes how a row should be read, that never reaches the reader. An agent deciding whether to rename a symbol is entitled to know that one of its call sites was asserted by a plugin rather than found in the bytes — that is precisely the edge acheck_renameshould be least confident about.The timing argument
No shipped package uses derived names, so the population is zero today. That makes this the cheapest possible moment to add the disclosure and the worst possible moment to defer it: the first package that uses the capability will be believed without qualification, and by then the payload contract will have shipped without the field.
Suggested shape (from the #89 lane, who owns that surface)
Both halves are needed, or the empty case is vacuous:
SCIP_MASK's strip for our own DTO (or carry a separate boolean), and emitname_asserted: trueonly when set. A row without it is unremarkable; a row with it says the name was not read from the span.project_overviewand/orresolution_gaps, so ABSENT is distinguishable from ZERO. Without it, "no such edges here" and "this build does not report them" render identically, which is the exact vacuity the three-state rule exists to prevent.Acceptance
Triage 2026-09-06: LEFT OPEN — PARTIAL. The per-row marker shipped; the aggregate shipped page-scoped only, and the project-wide census was deliberately reverted.
Reported as fixed in
d0279c0; on verification, half 1 is met and half 2 is met at a narrower scope than this issue specifies.Half 1 — SHIPPED
pub name_asserted: Option<bool>—crates/daemon/src/local_index.rs:232, emitted asSome(true)only, neverSome(false)(reasoning at:215-231), stamped at all threeRefRowsites (:3451/3462,:3696/3707,:3854/3865), read from the unmasked column and deliberately not throughSCIP_MASK(crates/core/src/raw.rs:145). That last detail is the one that matters: the marker is a local fact and must not leak into the SCIP roles field.Half 2 — SHIPPED PAGE-SCOPED, and the project-wide census was REVERTED
pub page_name_asserted: Option<u64>—local_index.rs:512, withstamp_page_name_assertedat:539.Its own doc (
:499-511) records why the project-wide version did not ship:SELECT COUNT(*) FROM refs WHERE (roles & 524288) != 0is un-indexable, so it is aSCAN refsonproject_overview— the first call an agent makes — and it was caught byrefs_rollup_e2e::the_overview_path_runs_no_scan_of_refs. Reverted rather than shipped as a scan. That is the right call and the right gate catching it.But this issue's acceptance bullet 2 asks for the aggregate on
project_overviewand/orresolution_gaps, precisely so that ABSENT ≠ ZERO at project scope.search_text("asserted")overcrates/mcp-server/src/finds noproject_overvieworresolution_gapsaggregate — the onlyname_assertedhits in that crate are the three test sites inserver.rs.So today:
project_overviewandresolution_gapscannot distinguish "no asserted names in this project" from "not reported." That is the exact condition bullet 2 exists to prevent.Bullets 1, 3 and 4 are met. Wire-skew and anti-vacuity are handled properly —
RefCounts::default()is hand-written soSome(0)is a measurement rather than the skew sentinel (local_index.rs:517-536).incl.
asserted_row_says_so_and_the_read_row_does_not,every_ref_serving_tool_states_it,the_page_count_separates_measured_zero_from_not_reported,the_local_bit_never_reaches_the_scip_roles_field.The exact follow-up, so it is not re-derived
A project-wide asserted-name census needs a partial index on
roles & 524288, or a roles dimension onrefs_rollup. Both are schema changes.There is now a worked precedent for judging that trade: m0061 (
crates/indexer/src/migrations/m0061_file_refs_rollup.rs) took a per-row-inserted trigger cost and showed it repaid by the secondproject_overview— break-even 1.24 calls on this repo, 1.08 on rust-analyzer. And #143 shows the opposite verdict on a cost proportional to a full sweep per watcher re-index. Whoever takes this should measure against those two, not guess.Until then the honest state is what shipped: a page count that is real, and a project count that is absent rather than wrong.
🤖 Triage lane, 2026-09-06, master
45cf6e4STAYING OPEN, NARROWED — three of four acceptance bullets shipped; the project-scoped census did not, and
find_calleeshas no aggregate at any scopeClose-out lane, master
552e3a2. Title corrected.Shipped by
d0279c0"feat: six silent-loss surfaces render the fact the process already held"Per-row marker, in the payload rather than in prose:
crates/daemon/src/local_index.rs:274—pub name_asserted: Option<bool>withskip_serializing_if, stamped at all threeRefRowsites (:3511,:3756,:3914) vianame_asserted_of(roles)reading the unmasked roles column, soSCIP_MASKno longer eats thederived_namebit on the way out.Page-scoped aggregate:
local_index.rs:554pub page_name_asserted: Option<u64>,stamp_page_name_assertedat:590, called at:3584and:3770.RefCounts::default()is hand-written soSome(0)is a measurement, andRefCounts::legacy()(old-daemon tuple decode) correctly yieldsNone— measured zero and not-reported are different values, which is the whole point.cargo test -p code-index-daemon --test asserted_name_marker_e2e→ EXIT=0, 4 passed (asserted_row_says_so_and_the_read_row_does_not,every_ref_serving_tool_states_it,the_page_count_separates_measured_zero_from_not_reported,the_local_bit_never_reaches_the_scip_roles_field).Not shipped — acceptance bullet 2 at project scope
Live
project_overview()on this repo carries no asserted-name field anywhere in the payload, andresolution_gapshas none either (ResolutionGapsResp,crates/daemon/src/graph.rs:1311). The tree says why, in its own words atcrates/daemon/tests/asserted_name_marker_e2e.rs:214-236: the project-wideStats::name_asserted_refscensus was written and then reverted, becauseSELECT COUNT(*) FROM refs WHERE (roles & 524288) != 0is un-indexable andrefs_rollup_e2e::the_overview_path_runs_no_scan_of_refswent red on the resultingSCAN refs. That was the right call and it is still an open half.Also not shipped, and not named by the implementing lane
find_calleeshas no aggregate at any scope. Its DTO path (local_index.rs:3855-3927) returns(Vec<RefRow>, total, unresolved_count)— noRefCountsat all. So it is the one ref-serving tool where a page of unmarked rows is genuinely ambiguous between "no asserted names here" and "this build does not report them" — exactly the vacuitylocal_index.rs:532says the companion field exists to prevent.every_ref_serving_tool_states_itasserts the per-row marker on callees but has no aggregate half to assert.Corrected scope
Mark bullets 1, 3 and 4 done in
d0279c0. What remains:project_overviewand/orresolution_gaps;find_callees, which has none.Carry the recorded cost note forward: it needs a partial index on
roles & 524288, or a roles dimension onrefs_rollup— judged against m0061's measured break-even and #143's opposite verdict. Do not close this by adding the count on a path that scansrefs; that gate is there for a reason.honesty: a ref whose name a package ASSERTED is reported as resolved fact, and no payload says soto honesty: no PROJECT-scoped asserted-name census (project_overview / resolution_gaps) and find_callees has no aggregate at any scope — the per-row marker and page count shipped in d0279c0FIXED, merged as
a80eb61.name_asserted_refsships onresolution_gaps(graph.rs:1470,:2028).The recorded blocker was wrong, and that is the useful part. The tree recorded that this needed a partial index or a
refs_rolluproles dimension — both schema changes. It needs neither:resolution_gaps'ssummaryquery is already a full grouped pass over those rows, so the census is a sixth aggregate on a scan that happens regardless. No new statement, no new pass, no migration.It also lands on
resolution_gapsrather thanproject_overview, which matters for a reason unrelated to this issue:project_overviewhas 30 estimated tokens of headroom against a ceiling whose standing rule is "TRIM, do not raise". Putting a census there would have cost the ratchet.resolution_gapsis also the tool a caller reaches for when they care about this number.Two mutations run: census as a constant →
left: Some(0) / right: Some(1); field never reported →left: None / right: Some(1). The second is the one that matters — it separates "measured, and the answer is zero" from "this build did not report", which is the whole point of the field.Closing.