Skip to content

fix(ingestion): qualify Ruby same-tail nested mixin modules + route IMPLEMENTS by scope (#1991)#1999

Closed
magyargergo wants to merge 2 commits into
abhigyanpatwari:mainfrom
magyargergo:fix/1991-ruby-mixin-module-trait
Closed

fix(ingestion): qualify Ruby same-tail nested mixin modules + route IMPLEMENTS by scope (#1991)#1999
magyargergo wants to merge 2 commits into
abhigyanpatwari:mainfrom
magyargergo:fix/1991-ruby-mixin-module-trait

Conversation

@magyargergo

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #1981 / #1982. 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 reference cross-wired IMPLEMENTS via the first-wins tail fallback (dangle-free but wrong).

Fix

Structure phase (node identity). Exposed buildQualifiedName as a qualifyScopeName ClassExtractor hook and threaded it for Trait nodes in parsing-processor.ts + parse-worker.ts (lockstep), so a module node keys by its qualified scope path (App.Loggable). This is not Option A — Trait is not in CLASS_LIKE_LABELS and the qualified-id selection gates it out, and extractQualifiedName bails on non-typeDeclarations; qualifyScopeName bypasses that gate. getQualifiedOwnerName also falls back to qualifyScopeName, so methods inside a nested module own through the same qualified Trait id (no dangling HAS_METHOD).

Resolution (cross-wire). emitRubyMixinEdges now resolves a bare mixin reference lexically by the including class's enclosing scope (App::S + LoggableApp::Loggable), and the simple-tail fallback is delete-on-collision (refuse to guess on a same-tail tie) instead of first-wins.

Module → Trait is preserved; Trait is not added to CLASS_LIKE_LABELS. No language-specific names in shared code — qualifyScopeName is a neutral hook gated on qualifiedNodeId + the Trait label.

Tests

New single-file ruby-nested-mixin-tail-collision fixture + tests:

  • Two distinct Trait nodes (App.Loggable / Web.Loggable).
  • S IMPLEMENTS App.Loggable only, T IMPLEMENTS Web.Loggable only; one edge each; no dangling IMPLEMENTS / HAS_METHOD.
  • Worker-path parity block.

Verification

Closes #1991.

🤖 Generated with Claude Code

…MPLEMENTS by scope (abhigyanpatwari#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>
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

@magyargergo is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

CI Report

Some checks failed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
❌ Tests failure unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
11060 11044 0 16 682s

✅ All 11044 tests passed

16 test(s) skipped — expand for details
  • COBOL pipeline benchmark > scales with file count
  • C++ ADL emit benchmark > emit phase scales sub-quadratically with co-scaled files and sites
  • C++ pipeline benchmark > scales with file count
  • C# pipeline benchmark > scales with file count — namespaces spread across the solution
  • C# pipeline benchmark > scales with file count — all types in one (global) namespace bucket
  • C# pipeline benchmark > scales with file count — all types in one (named) namespace bucket
  • Go pipeline benchmark > scales with file count (workers enabled)
  • Go pipeline benchmark — worker pool (issue Worker idle timeout kills long Go scope extraction and surfaces as Napi::Error during analyze #1848) > does not quarantine the large generated Go file on sub-batch idle timeout
  • Go structural interface detection benchmark > scales linearly with interface × struct count
  • Go structural interface detection split-phase benchmark > separates index-build and detection time
  • PHP pipeline benchmark > scales with file count (workers enabled)
  • Ruby pipeline benchmark > scales with file count (workers enabled)
  • Rust pipeline benchmark > scales with file count (workers enabled)
  • Vue pipeline benchmark > scales with component count
  • run.cjs direct-exec entrypoint (fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939) #1945) > resolves a .cmd shim via the Windows shell branch, passing args and exit code
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 80.32% 38706/48186 79.84% 📈 +0.5 🟢 ████████████████░░░░
Branches 68.84% 24597/35730 68.5% 📈 +0.3 🟢 █████████████░░░░░░░
Functions 85.55% 4035/4716 84.94% 📈 +0.6 🟢 █████████████████░░░
Lines 83.93% 34821/41484 83.36% 📈 +0.6 🟢 ████████████████░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator Author

Superseded by same-repo stacked PR #2006.

@magyargergo magyargergo closed this Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ingestion): Ruby same-tail nested mixin-MODULE (Trait) node collision — structure phase never qualifies module ids (#1982 / #1978 follow-up)

1 participant