Ruby's require_relative creates no file edge — temp.import_rel selects module GLOB '.*', and Ruby carries relativity in the KEYWORD #194
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#194
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 measuring #189's receiver gate. It is the reason Ruby is not in
lang_profile::RECEIVER_LOCALITY_BLIND_PROFILES, so it is a blocker for a real precision win, not a curiosity.Measured
temp.import_rel— the relation behind tier 1b'stier1b_resolved_relativearm and tier 3's third reachability edge, the only edge that RESOLVES a specifier to a file rather than guessing at it — is built from exactly one selector:and
relative_import_pathsaccepts two dialects: a leading./or../(JS/TS), or Python's leading dots.A Ruby
require_relative "lib/greeter"storesimports.module = 'lib/greeter'. No leading dot, so it matches neither, so noimport_relrow exists and no file edge is created — even thoughrequire_relativeis by definition relative to the requiring file, more unambiguously than./xis in JS.Reproduced on
tests/fixtures/ruby/project:greeter.greet("everyone")inmain.rbresolves toGreeter#greetinlib/greeter.rb— correctly — but it gets there through tier 3's file-KEY edge (the import segmentgreetermatching the basename stem oflib/greeter.rb), not through the relative edge.precision_gate's ruby oracle declares that bind as ground truth.Why it matters beyond tidiness
The file-key edge is a guess: it matches an import's segments against basename stems, which is how #134's cross-package phantom happened and why that edge carries an origin gate. The relative edge is a fact. In Ruby every one of those facts is currently thrown away and the guess is doing the work.
#189 refuses a captured, non-self method-call receiver at tier 2 and at tier 3's two guessing edges. Including Ruby was built and measured:
options.delete(:locals)(Hash#delete) binding Sinatra's DELETE route DSL atlib/sinatra/base.rb:1543,response.body×27 binding a test helper attest/test_helper.rb:86,to_s/respond_to?/inspectbindingObject's;precision_gate's ruby oracle, becausegreeter.greetloses its only evidence:With a real
import_reledge forrequire_relative,greeter.greetwould resolve through the edge #189 leaves open, and Ruby could join the registry.Shape of a fix (not prescriptive)
The selector and
relative_import_pathsboth key on the SPELLING of the specifier. Ruby's relativity is in the keyword, so either:require_relative "lib/greeter"as a module the resolver already recognises as relative, or carry a flag; orrequire "json"resolves to nothing and creates no edge, so a miss is free;require 'sinatra/base'fromlib/sinatra.rbwould resolve tolib/sinatra/base.rb, which is what Ruby's load path actually does.Option 2 needs no extraction change and no re-parse; option 1 is a wire/plugin change.
What must NOT be done
import_relrows create new tier-1btier1b_resolved_relativebinds AND new tier-3 edge-3 reachability, in both directions. Report correct/wrong per rule, adjudicated at source, joined on(path, line, col, kind, occurrence)— position alone fans out.require_relativewould NOT reach, this change removes a bind. Those are the interesting rows and they need reading, not counting.Measured vs inferred
The selector, the missing
import_relrow, theresolved_by = 30attribution of the fixture probe, the -239 and theprecision_gateRED are measured on the pinned corpus and the fixture. That option 2 is safe because a miss creates no edge is inferred from the build loop (hitis only set insideby_path.get(&cand)), and the ruby-sinatra blast radius of either option is not measured — that is the work.🤖 Generated with Claude Code
https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
REFUSED WITH NUMBERS — measured in
38ccbd2, and I am posting it here because the reasoning has been sitting in a commit message where nobody reading the tracker could find it. That is its own small defect: a resolution that exists only in git history is invisible to the person deciding whether to work on this.The measurement
Option 2 alone, on
ruby-sinatra:Sixty-seven new file edges produced zero change in resolution. The edges are real and they bind nothing.
Option 2 combined with #189's ruby row:
So the change does not merely fail to help — it restores what #189 deletes. Shipping it would undo a phantom removal we already paid for, and it would do so silently, because the binds land in the same places.
The part worth keeping beyond this issue
precision_gatereported 7/7 withphantom_count == 0on that change.That is the second demonstration in one week that the gate's green is not evidence about corpus binds. It stayed green across a change that admits 37 wrong binds on a pinned corpus repo, exactly as it stayed 7/7 across the change that admitted 34 wrong binds on
cs-dapper. The reason is structural and already documented: the gate never indexes a corpus repo, and a phantom only scores against a declared decoy, so an unpredicted wrong bind is invisible by construction.Keep requiring the gate — it is cheap and has caught real defects — but this issue is now a second citation for never reading its green as a statement about corpus-scale resolution. When a change moves corpus binds, measure the binds: diff against the prior binary joined on
(path, line, col, kind, occurrence), and split byresolved_by.Disposition
Closing as refuted. The diagnosis in the issue body is correct — Ruby does carry relativity in the keyword, and
temp.import_reldoes selectmodule GLOB '.*'. What is refuted is that fixing it buys anything: the edges materialise, and resolution does not move except to reintroduce known phantoms.If someone wants to reopen this, the bar is a measurement showing binds that are correct, inspected at the source of each new site — not a count that went up. Three cuts earlier this year raised the resolved count and every extra bind was a phantom; that is why the bar is bind inspection rather than a delta.
Rack::MockRequest.newwould bind toRack::Protection::new— so Ruby constant-receiver calls stay unresolved #296rack/protection/basemakeslib/sinatra/base.rbreachable — which blocks treatingautoloadas the dependency it is #297