fix(ingestion): qualify Ruby same-tail nested mixin modules + route IMPLEMENTS by scope (#1991)#2006
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Tri-review — approve-with-nitsMethod: per-PR multi-lens review → independent adversarial re-verification → static-analysis alignment (tsc/eslint/prettier, all clean) → cross-stack synthesis + critic gate. Review: PR #2006 — Ruby same-tail nested mixin-module Trait qualification (#1991)Verdict: Approve with nits. This is a clean, well-scoped fix that lands on the real runtime path, holds the language-neutrality line, and ships the right tests (positive node+edge identity, not dangle-only, with worker-parity and exact-match legacy-parity skips). What I verified
Strengths
Findings (all minor/nit)
Note on verification limitsThe worker tests in this worktree would run against a stale compiled |
ac40fb6 to
e94822c
Compare
Operational note: embedding reindex after this stack lands
|
355f08d to
017cf27
Compare
…MPLEMENTS by scope (#1991) A Ruby `module` maps to the Trait label but is not a typeDeclaration, so the structure phase never qualified its node id: two same-tail nested mixin modules (App::Loggable / Web::Loggable) collapsed onto one Trait:f.rb:Loggable node and the bare-name `include Loggable` cross-wired IMPLEMENTS (first-wins tail). Structure phase: expose buildQualifiedName as a `qualifyScopeName` ClassExtractor hook and thread it for Trait nodes in parsing-processor + parse-worker (lockstep), so a module node keys by its qualified scope path (App.Loggable). Not Option A — `Trait` is not in CLASS_LIKE_LABELS and the qualified-id selection gates it out; qualifyScopeName bypasses the typeDeclaration gate that makes extractQualifiedName bail on modules. getQualifiedOwnerName also falls back to qualifyScopeName so methods inside a nested module own through the same qualified Trait id (no dangling HAS_METHOD). Resolution: emitRubyMixinEdges resolves a bare mixin reference lexically by the including class's enclosing scope (`App::S` + `Loggable` -> `App::Loggable`), and the simple-tail fallback is now delete-on-collision (refuse to guess on a same-tail tie) instead of first-wins. New single-file fixture + tests: two distinct Trait nodes, S IMPLEMENTS App.Loggable only, T IMPLEMENTS Web.Loggable only, no dangling HAS_METHOD; both resolver legs + worker path. Module->Trait preserved; Trait NOT added to CLASS_LIKE_LABELS. ruby-captures-golden regenerated additively. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…te; regen ruby bench baseline (#1991) F5 follow-up to #1991: replace the four hardcoded `nodeLabel === 'Trait'` checks (two each in the sequential parsing-processor.ts and worker parse-worker.ts definition paths) with a single isQualifiableScopeLabel() in ast-helpers.ts so the lockstep paths can't drift. Value-identical predicate — no behavior change. Also regenerate the ruby scope-capture bench baseline: #1991 added the ruby-nested-mixin-tail-collision fixture (and updated the ruby captures-golden), but the bench baseline was never regenerated, so the order-independent fingerprint drifts (bf6b13a -> f0d9b4c6, fixture_count 85 -> 86). Pure fixture-corpus drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e94822c to
939c7ae
Compare
CI Report✅ All checks passed Pipeline Status
Test Results
✅ All 10573 tests passed 16 test(s) skipped — expand for details
Code CoverageTests
📋 View full run · Generated by CI |
… legacy-DAG removal) Resolved conflicts from #2023 (delete legacy call-resolution DAG + heritage processor, #942): - test/integration/resolvers/helpers.ts: took #942's removal of LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES + the createResolverParityIt / isLegacyResolverParity* helpers (no legacy leg remains; only a kotlin doc comment referenced the list). - test/integration/resolvers/ruby.test.ts: converted the three #1991 `pit(...)` (parity-gated `it`) calls to plain `it(...)`, matching #942's single-leg harness. - bench/scope-capture/baselines.json (ruby): both sides changed it — #942 reworded fixture comments (shifting capture byte-positions; capture LOGIC unchanged) and #1991 added the ruby-nested-mixin-tail-collision fixture. Recomputed the ruby fingerprint on the merged tree: bf6b13a -> b5ea93bb (86 fixtures). Verified on the merged tree: bench --check PASS (14 languages), tsc 0 errors, ruby resolver + ruby-captures-golden 159/159, prettier clean. F5 predicate + swaps intact through the auto-merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Follow-up to #1981 / #1982. A Ruby
modulemaps to theTraitlabel but is not a typeDeclaration, so the structure phase never qualified its node id. Two same-tail nested mixin modules (App::Loggable+Web::Loggable) collapsed onto oneTrait:f.rb:Loggablenode, and the bare-nameinclude Loggablereference cross-wiredIMPLEMENTSvia the first-wins tail fallback (dangle-free but wrong).Fix
Structure phase (node identity). Exposed
buildQualifiedNameas aqualifyScopeNameClassExtractorhook and threaded it forTraitnodes inparsing-processor.ts+parse-worker.ts(lockstep), so a module node keys by its qualified scope path (App.Loggable). This is not Option A —Traitis not inCLASS_LIKE_LABELSand the qualified-id selection gates it out, andextractQualifiedNamebails on non-typeDeclarations;qualifyScopeNamebypasses that gate.getQualifiedOwnerNamealso falls back toqualifyScopeName, so methods inside a nested module own through the same qualified Trait id (no danglingHAS_METHOD).Resolution (cross-wire).
emitRubyMixinEdgesnow resolves a bare mixin reference lexically by the including class's enclosing scope (App::S+Loggable→App::Loggable), and the simple-tail fallback is delete-on-collision (refuse to guess on a same-tail tie) instead of first-wins.Module → Traitis preserved;Traitis not added toCLASS_LIKE_LABELS. No language-specific names in shared code —qualifyScopeNameis a neutral hook gated onqualifiedNodeId+ theTraitlabel.Tests
New single-file
ruby-nested-mixin-tail-collisionfixture + tests:Traitnodes (App.Loggable/Web.Loggable).SIMPLEMENTSApp.Loggableonly,TIMPLEMENTSWeb.Loggableonly; one edge each; no danglingIMPLEMENTS/HAS_METHOD.Verification
rubyresolver suite: registry-primary 149/149, legacy 137 + 12 by-design skips (the registry-primary-only mixin routing is listed inLEGACY_RESOLVER_PARITY_EXPECTED_FAILURES, like the Qualified nested-type node identity — resolution-side follow-up (Ruby same-tail routed-props/mixins + Rust inherent impls) #1982 mixin tests) — both green, including the worker path.tsc --noEmitclean; prettier clean;ruby-captures-goldenregenerated additively (4 insertions — the new fixture only);ruby-scope-capture-tripwireunaffected;ruby-nested-mixin-shortname/ruby-qualified-mixin/ruby-nested-tail-collisionstay green.Closes #1991.
🤖 Generated with Claude Code