recall: emit field/member facts for Rust and PHP across builtin and package paths #68
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.
Depends on
Reference
h-dv/code-index#68
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 while building
name_fallback_count(I040). A fixture with a property/field accessed from several files, run across all six languages, splits into two classes:x.propreadmethod, @property)type, qualified)field)rust.rshandlesfield_expressiononly as a method-call receiver;php.rshandlesproperty_declarationonly for its type annotation. Neither emits a field-read ref, and onlyruby.rsandcsharp.rsemitSymbolKind::FIELDat all.Consequence: a Rust
pubstruct field's blast radius is currently unaskable.find_referenceson it returns nothing because there is no symbol to ask about, andname_fallback_countreports 0 — which reads as "provably tight" when the truth is "invisible to us".This is the standing reason the
name_fallback_countdisclosure must never harden into a claim of exactness, and that reason is now written into its field doc and intocode-index://docs/ref-kinds.Violates the cross-language parity rule: 4 of 6 languages emit something here, 2 emit nothing.
Scope note: fixing this is emitting the symbols and refs. Whether they then RESOLVE is #69.
Runtime-plugin architecture revision
This extraction gap becomes a fact-ABI parity case for #76/#80. The fix should define the language-independent fact shape first, then implement Rust/PHP builtin emission against it:
When a complete builtin plugin is migrated through the package ABI, its field/member projection must remain equivalent. Dynamic plugins default to searchable/inert member facts until #69/#77 capabilities admit them.
Acceptance must separately prove emission, searchability, name_fallback accounting and optional resolution; a resolver improvement cannot make a missing-extraction test pass vacuously.
recall: rust and php emit no field symbols and no field-read refsto recall: emit field/member facts for Rust and PHP across builtin and package pathsTriage 2026-09-06: CLOSING. This issue's headline table is stale — rust and php ship both field symbols and field-access refs, and have since I059 phases 1/2.
Verified against master. This is exactly the case the triage pass was run to find: the work was done, the issue was never closed, and the body still reads as a live gap.
Field SYMBOLS
crates/plugins/src/rust.rs:655,fn emit_field→SymbolKind::FIELD(doc at:644)crates/plugins/src/php.rs:516,:547,:559,:593(SymbolKind::FIELDperproperty_declaration)Field-ACCESS refs
rust.rs:271("field_expression" => emit_field_access), plus:293forfield_initializer/shorthand_field_initializerphp.rs:249("member_access_expression" | "nullsafe_member_access_expression" => emit_property_access) and:253forscoped_property_access_expressionThey are
read/writerefs viacrates/plugins/src/common.rs:396access_ref,qualified: true, rolesREAD/WRITE— not the oldtypekind. That distinction matters and is the reason #69's stated input predicate is now dead; see below.It is graded across all seven languages, not just these two
MEMBER_ACCESS_BY_LANGUAGE—crates/plugins/src/lib.rs:678— withan_access_ref_is_always_qualified_in_every_language. Andcrates/indexer/tests/producer_coverage_matrix.rs:86records the movement: I059 phase 1 closed seven cells, phase 2 closed fourteen (read/writefor all seven languages); Emitted 91 → 103, Missing 42 → 30.The rust/php cells are asserted individually, so this is not a matrix aggregate hiding a per-language hole:
crates/mcp-server/tests/field_symbols_e2e.rs:107-109pins("radius_e056", "rust")and("name_e056", "php"), andcrates/mcp-server/tests/field_access_e2e.rs:72-74pins the same pair.Run (exit 0)
Residual
None for emission. The issue's
name_fallbackframing paragraph is now historical rather than descriptive.Where the remaining work actually lives: #69 (profile-driven member/property binding) is still open, and its own stated input —
kind='type' AND qualified=1— is dead precisely because of the change that closes this issue. #69 needs rewriting against theread/writeworld; I have said so there.🤖 Triage lane, 2026-09-06, master
45cf6e4Could not close mechanically — and the reason is itself a triage finding
issue_state_change → closedwas refused:list_issue_dependencies(68)returns #80 (open) and #76 (closed). So this issue is registered as depending on #80, the plugin production-readiness release gate.That looks backwards. #68 is a recall feature — emit field/member facts for Rust and PHP — and it is done, verified above. #80 is a gate that will not close for some time. As registered, a finished feature is pinned open by a gate that does not depend on it, and it will keep appearing on the open list and in blocker scans.
I have deliberately not removed the dependency. Editing the dependency graph is a project-structure decision, not a triage one, and this lane's remit is to make the list true by evidence, not to rewire it. Flagging it for a decision instead:
Either way, treat this issue as resolved when scoping work. The verification is in the comment above: field symbols and field-access refs ship for rust and php, graded per-language,
field_symbols_e2e+field_access_e2egreen at exit 0.🤖 Triage lane, 2026-09-06, master
45cf6e4CLOSING — verified independently on the pinned CORPUS, not only on fixtures, and the resolution half named in the scope note now ships too
Resolver-recall lane, worktree off master
fc329a8.The headline table is stale, and here is the measurement that says so
The triage comment above verified emission against the two e2e fixtures. Those are the right tests but they are 54 fixture files, so I re-asked the question against real repositories indexed with a binary built from
fc329a8:SymbolKind::FIELDsymbolsread/write)The issue's table records
no symbol/ZERO refsfor both languages. Neither is true and has not been since I059 phases 1/2.The scope note — "whether they then RESOLVE is #69" — is now answered
That was the one part of this issue still pointing at live work, and it pointed at #69. #69 has been implemented in this lane: tier 1R's input widened from
method_callto the MEMBER pool, so aread/writethrough a receiver a binding row types now binds. Measured bind-for-bind against afc329a8binary over nine pinned repos: +4 922 binds, 0 lost, including +326 on rust-ripgrep and +37 on php-guzzle — this issue's two dark languages. Details on #69.So both halves of this issue's own scope are shipped and measured.
The dependency that blocked the mechanical close
issue_state_changewas refused because #68 was registered as depending on #80 (the plugin production-readiness release gate). That is backwards: #68 is a recall feature that is done and graded, #80 is a gate that will not close for some time, and as registered a finished feature was pinned open by a gate that does not depend on it — it would have kept surfacing in every blocker scan.I have removed the #80 dependency and closed this issue. It is one API call to restore if the link was intentional; if it was, the reason belongs in #80's body, because nothing anywhere explained why a shipped, per-language-graded feature was held open. #76, the other dependency, is already closed.
Residual
None. The
name_fallback_countframing paragraph in the body is historical rather than descriptive, and the two-language parity violation the issue was filed about no longer exists.