* fix: resolve false 404s and stale repo context during multi-repo switching on Windows
* test(e2e): add repo-switching tests — hold-queue 503, ?project= URL, Windows path normalization
* test(e2e): fix repo-switching specs — use live backend with ?server= param
* Initial plan
* Update test files for SymbolTable interface changes
Remove lookupFuzzy, lookupFuzzyCallable, globalIndex, and callableIndex
references from all test files. Replace lookupFuzzyCallable with
lookupCallableByName. Update getStats assertions to only expect
{ fileCount }. Remove tests that exclusively tested removed methods.
Files updated:
- symbol-table.test.ts: Remove lookupFuzzy describe block and all
globalIndex/callableIndex tests, update callable method references
- symbol-resolver.test.ts: Remove SM-16 lookupFuzzy test block,
update Tier 3 describe title
- type-env.test.ts: Update all mock SymbolTable objects and spy
variable names
- call-form.test.ts: Update ownerId propagation test
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(SM-18): Remove lookupFuzzy, lookupFuzzyCallable, globalIndex, callableIndex
Remove from SymbolTable interface and implementation:
- lookupFuzzy method
- lookupFuzzyCallable method
- globalIndex Map
- callableIndex Map (renamed to callableByName, backing lookupCallableByName)
Add lookupCallableByName as the targeted replacement for fuzzy callable
lookups. Migrate all production callers:
- resolution-context.ts: lookupFuzzyCallable → lookupCallableByName
- type-env.ts: lookupFuzzyCallable → lookupCallableByName
- call-processor.ts: lookupFuzzy → lookupCallableByName (D2 widen paths)
Remove fuzzyCallCount/fuzzyCallableCallCount stats and globalSymbolCount
from getStats(). Update pipeline.ts logging accordingly.
Memory savings: globalIndex stored every non-Property symbol (typically
the largest index by entry count). Removing it eliminates one Map plus
all its per-name arrays — net savings proportional to unique symbol
count in the project.
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/4a658c69-41a9-4d57-8527-50ca544ca967
* fix(SM-18): address all PR #769 review findings
1. type-env.test.ts mock: add missing lookupImplByName + getFiles methods.
2. Macro/Delegate tests: 2 new tests confirm C/C++ Macro and C# Delegate
are indexed in callableByName.
3. D2 widen path test: module-alias scenario verifying lookupCallableByName
resolves methods in aliased files that shadow same-file definitions.
4. CALLABLE_TYPES unified: exported from symbol-table.ts (single source of
truth), imported in call-processor.ts. Removed duplicate
CALLABLE_SYMBOL_TYPES constant.
5. getStats() observability restored: tier hit counters (tierSameFile,
tierImportScoped, tierGlobal, tierMiss) replace the removed
fuzzyCallCount diagnostic.
* chore(SM-18): remove unnecessary `as any` casts on valid NodeLabel types
Macro, Delegate, TypeAlias, Const, and Variable are all valid NodeLabel
values in gitnexus-shared. The casts suppressed type checking without
purpose and signaled false uncertainty.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* chore: initial plan for SM-16 resolveUncached refactor
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/0f505332-25be-46a7-b78e-fde58c1fc6fd
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* feat(SM-16): restructure resolveUncached — replace lookupFuzzy with targeted index lookups
- Remove single lookupFuzzy call that fed all Tier 2a/2b/3 in resolveUncached
- Tier 2a: iterate importedFiles with lookupExactAll per file (O(imports) × O(1))
- Tier 2b: iterate symbols.getFiles() filtered by isFileInPackageDir + lookupExactAll
(O(files) × O(1), avoids global name scan)
- Tier 3: replace with lookupClassByName + lookupImplByName + lookupFuzzyCallable
(three O(1) index lookups covering class-like, Rust impl blocks, and callables)
- Add getFiles() to SymbolTable interface (exposes fileIndex.keys() for Tier 2b)
- Add lookupImplByName() to SymbolTable — dedicated Rust Impl index kept separate
from classByName to preserve correct heritage-map resolution
- Remove allDefs parameter from walkBindingChain; always use lookupExactAll directly
- Add 29 new unit tests covering SM-16 changes and per-language fixtures
- fuzzyCallCount in getStats() is now 0 for all resolve() calls (acceptance criterion)
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/0f505332-25be-46a7-b78e-fde58c1fc6fd
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix(SM-16): clean up — readable Tier 3 if-else, correct doc comment, remove unused import
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/0f505332-25be-46a7-b78e-fde58c1fc6fd
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix(SM-16): address all PR #764 review findings
1. Eager callableIndex — maintained on add() like classByName/implByName,
removing the O(globalIndex) lazy rebuild on the Tier 3 hot path.
2. Tier 2b inverted index — packageDirSuffix→Set<filePath> built lazily on
first Tier 2b hit. Changes O(allFiles×packages) per resolution to
O(packages×filesInPackage).
3. Tier 3 type exclusion documented — TypeAlias, Const, Variable are
intentionally not reachable at Tier 3. 4 negative/positive tests added.
4. Tier 3 allocation guard simplified — single spread replaces 4-way if-else.
5. getFiles() live iterator documented with safety contract.
6. Remaining lookupFuzzy callers in call-processor.ts documented in the
Tier 3 comment block.
7. Tier 2b language fixtures — added Rust, Kotlin, PHP tests (3 new).
Also merges origin/main (SM-15 accumulator fixes).
* fix(SM-16): address Codex adversarial review — Tier 2b cache lifecycle + Macro/Delegate at Tier 3
1. Tier 2b packageDirIndex now invalidated in clearCache() and clear(),
preventing stale snapshots when symbols/packages are added between
chunk processing phases.
2. Macro (C/C++) and Delegate (C#) added to CALLABLE_TYPES in the eager
callableIndex, restoring Tier 3 reachability for these call targets
that the old lookupFuzzy returned.
* fix(SM-16): address ce:review findings — Tier 2b cache lifecycle + Tier 3 perf + test gaps
1. packageDirIndex no longer invalidated in clearCache() — the index
persists across file boundaries since packageMap and symbols are
append-only during the calls phase. Only clear() (pipeline reset)
invalidates. Prevents O(files×dirs) rebuild per-file.
2. Tier 3 short-circuit: return null before spread when all three
indexes are empty, avoiding allocation on the common miss path.
3. Add Macro (C/C++) and Delegate (C#) Tier 3 regression tests —
the only newly-added CALLABLE_TYPES were completely untested.
4. Add packageDirIndex invalidation regression test — verifies clear()
resets the index and newly-added symbols are visible.
* fix(SM-16): address final review — deduplicate NamedImportMap + doc fixes
1. NamedImportMap: removed duplicate definition from resolution-context.ts,
now imported directly from import-processor.ts (no re-export needed —
no consumers imported it from resolution-context).
2. packageDirIndex build cost documented accurately in comment.
3. fuzzyCallCount scope documented in test comment.
4. Tier 2a test suite: added comment about Go/Kotlin/PHP coverage.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* Initial setup - Phase 9 BindingAccumulator cross-file return type wiring
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/7cee6490-090d-4714-8cb5-a704168ff47a
* feat(SM-15): wire BindingAccumulator into processCallsFromExtracted for Phase 9 cross-file return type propagation
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/7cee6490-090d-4714-8cb5-a704168ff47a
* fix(SM-15): address all PR #763 review findings
Performance (R1)
- Changed _fileScopeByFile from Map<string, [string,string][]> to
Map<string, Map<string,string>>. fileScopeGet(filePath, name) is
now O(1) — replaces the O(n) linear scan + defensive-copy alloc
that ran once per ConstructorBinding entry. fileScopeEntries()
reconstructs tuples from Map.entries() for backward compat.
- Updated finalize() dev-mode invariant to compare deduplicated Map
size rather than raw array length (Map.set deduplicates same-name).
Lifecycle (R2)
- Documented that Phase 9 intentionally reads pre-finalize because
finalize() cannot move before both the worker consumer (line 984)
AND the sequential-path writer (line 1061). Pre-finalize reads are
safe because finalize() is write-lock-only with no side effects.
Replaced the ambiguous "populated but not yet finalized" comment
with the full lifecycle ordering explanation.
Sequential-path parity (R3)
- Wired bindingAccumulator into processCalls at line 797 (sequential
path) so verifyConstructorBindings gets the Phase 9 fallback.
- Added bindingAccumulator parameter to processAssignmentsFromExtracted
signature and wired it at the pipeline.ts call site (line 1026).
- Both paths now produce identical Phase 9 behavior for the same code.
Tracking comments (R4)
- Added "Overlapping mechanism (N of 3)" cross-references at:
1. buildImportedReturnTypes (~line 109)
2. collectExportedBindings (~line 168)
3. Phase 9 fallback in verifyConstructorBindings (~line 563)
Each links to the other two and notes future unification.
Language coverage (R5)
- Added 5 new Phase 9 integration test suites in cross-file-binding.test.ts:
JavaScript, C++, C#, PHP, Ruby. Each uses the existing fixture
directories and asserts getUser() → User → user.save() resolves.
Total cross-file binding tests: 52 (was 37).
Quality asymmetry (R6)
- Added inline comment at the Phase 9 fallback noting worker-path
entries are Tier 0/1 only and that binding accuracy is structurally
lower for large repos where the worker path dominates.
Tests (+21 new)
- 6 fileScopeGet unit tests (happy path, unknown file/name, mixed
scopes, post-dispose, duplicate varName last-write-wins)
- 15 integration tests across 5 new language suites
Verification
- tsc --noEmit clean
- 3147 unit tests pass (+6 new)
- 52 cross-file binding integration tests pass (+15 new)
- 1766 resolver integration tests pass
- Zero regressions
Plan: docs/plans/2026-04-10-001-fix-sm15-review-findings-plan.md
Review: https://github.com/abhigyanpatwari/GitNexus/pull/763#issuecomment-4220354242
* fix(SM-15): gate accumulator fallback on resolution tier and fix sequential file-order dependency
Two Codex adversarial reviews identified medium-severity bugs in the Phase 9
BindingAccumulator fallback:
1. Local-first violation: the fallback fired regardless of whether ctx.resolve()
found same-file candidates, letting an imported callee shadow a local one
and produce false CALLS edges. Fixed by gating on tiered.tier !== 'same-file'
and callableDefs.length <= 1.
2. Sequential file-order dependency: processCalls flushed and verified per-file,
so consumer files processed before their providers missed accumulator bindings.
Fixed by splitting into a flush pre-pass (all files) then a resolution loop,
mirroring the worker path's "all appends before any reads" pattern.
Also adds 11 consumer-before-provider integration test fixtures (one per
supported language) and 4 unit tests for tier gating edge cases.
* refactor(SM-15): eliminate duplicated prepare logic in processCalls two-pass split
Replace the duplicated pre-pass + legacy-path code (parse → query → heritage
→ TypeEnv → exports) with a single preparation loop followed by a resolution
loop. Both paths now share the same preparation code — the only conditional
is the accumulator flush.
Side benefit: globalParentMap is now fully populated before any resolution
runs, improving cross-file isSubclassOf accuracy regardless of file order.
Net -118 lines (226 removed, 108 added).
* fix(SM-15): address PR #763 third-pass review findings
1. Update stale dispose() JSDoc — remove forward-reference to Phase 9
wiring that is now complete; document actual consumers.
2. Add processAssignmentsFromExtracted Phase 9 unit test — verifies the
accumulator fallback produces ACCESSES write edges when the SymbolTable
has no returnType for the callee.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* feat(SM-13): extract resolveFreeCall from resolveCallTarget
Extract the free-function call resolution path into a dedicated
`resolveFreeCall(calledName, filePath, ctx)` function that uses
`lookupExact` + import-scoped resolution via `ctx.resolve()`.
- Free function calls (foo()) now route through `resolveFreeCall`
- Swift/Kotlin implicit constructors (User()) delegate to
`resolveStaticCall` within `resolveFreeCall`
- `resolveCallTarget` dispatches `callForm === 'free'` early,
removing the inline freeFormHasClassTarget logic
- S0 block simplified to only handle `callForm === 'constructor'`
- Global (Tier 3) fallthrough preserved via ctx.resolve() until Phase 5
- 9 new unit tests for resolveFreeCall
- All 163 unit tests pass, all 1199 integration resolver tests pass
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c5f2e73a-259a-438c-b5c8-286b82e3c215
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* chore: revert unrelated package-lock.json change
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c5f2e73a-259a-438c-b5c8-286b82e3c215
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix(SM-13): address PR #756 review findings on resolveFreeCall
Addresses all 7 findings from the PR #756 review comment.
Code (R1, finding #1)
- Replace the literal `'Class' | 'Struct' | 'Record'` check in
`hasClassTarget` with `INSTANTIABLE_CLASS_TYPES.has(c.type)`. Converts
an invariant that was previously comment-enforced ("keep this list
aligned with INSTANTIABLE_CLASS_TYPES") into one enforced structurally.
Any future extension of the set propagates here automatically. The
narrower Swift extension dedup block below still uses literal
`'Class' | 'Struct'` by design — Swift extensions only produce Class
duplicates in practice, Record is deliberately excluded there, and
the inline comment now documents that asymmetry.
Tests (+12 regression scenarios)
Finding #2 — language coverage
- Go free function (doStuff())
- Python free function (def helper(): ... helper())
- Rust free function outside any impl block
- Java statically-imported function
- JavaScript module-level function
Each exercises `_resolveCallTargetForTesting` with `callForm='free'`
and the language-specific file extension. `resolveFreeCall` has no
file-extension branching, so these guard the dispatch chain per
language without assuming extractor-specific symbol shapes.
Finding #3 — argCount threading
- 2-arg overload selected when argCount=2
- 0-arg overload selected when argCount=0
Finding #5 — Tier 3 (global) resolution
- Function globally visible but not imported. Asserts exact
`TIER_CONFIDENCE.global === 0.5` and `reason === 'global'` to catch
silent drift if the tier table is ever refactored.
Finding #6 — preComputedArgTypes worker path
- String overload matched via preComputedArgTypes=['String']
- Int overload matched via preComputedArgTypes=['int'] (lowercase,
mirroring the parse-worker's inferred-literal shape; stored 'Int' is
normalized via normalizeJvmTypeName at comparison time)
Finding #7 — Enum null-route documentation
- Enum-only free call asserts `toBeNull()` with an explanatory comment
linking to the INSTANTIABLE_CLASS_TYPES rationale. NOT marked skipped
— current behavior is intentional, not broken.
Finding #4 — Swift extension dedup guard
- Two same-name Class entries at different path lengths; exercises the
full dispatch chain:
1. filterCallableCandidates with 'free' strips Class → length 0
2. hasClassTarget triggers resolveStaticCall
3. Homonym ambiguity null-routes per SM-12 round-1 contract
4. Constructor-form retry repopulates with both Classes
5. Dedup block sorts by filePath.length → shortest path wins
Verification
- `tsc --noEmit` clean
- 3064 unit tests pass (+12)
- 1766 integration tests pass
- Zero regressions
Plan: docs/plans/2026-04-09-003-fix-sm13-resolve-free-call-review-findings-plan.md
Review: https://github.com/abhigyanpatwari/GitNexus/pull/756#issuecomment-4213879002
* refactor(SM-13): extract dedupSwiftExtensionCandidates shared helper
Follow-up to the PR #756 review fix. SM-13 duplicated the Swift
extension same-name collision dedup block between `resolveCallTarget`
and `resolveFreeCall` — two copies of identical 15-line logic with the
same heuristic (`filePath.length` sort, Class/Struct-only, `length > 1`
guard). Extract a single shared helper so the two sites cannot drift.
Changes
- New `dedupSwiftExtensionCandidates(candidates, tier)` helper defined
alongside `tryOverloadDisambiguation`, with JSDoc documenting:
- The Swift extension scenario it addresses
- Why it is intentionally narrower than INSTANTIABLE_CLASS_TYPES
(Class/Struct only, not Record — C#/Kotlin records don't exhibit
the multi-file definition pattern, widening risks accidental
dedup of legitimately distinct record types)
- The return-null-on-no-match contract so callers can fall through
- `resolveCallTarget` tail dedup (was lines 1593-1610): replaced with
a single `dedupSwiftExtensionCandidates` call
- `resolveFreeCall` tail dedup (was lines 1994-2012): same replacement
- Net line count: -32 insertions, -9 deletions in the consumer sites,
+36 for the shared helper + JSDoc
Verification
- `tsc --noEmit` clean
- 3064 unit tests pass (including the R7 Swift dedup guard test added
in the previous commit that exercises the full free-form retry
chain through this helper)
- 1766 integration tests pass
- Zero regressions
Follows-up on: https://github.com/abhigyanpatwari/GitNexus/pull/756
* docs(SM-13): address PR #756 final review — comment cleanup only
Three documentation-only findings from the approval review. No
behavior change, no new tests, no code path modifications.
Finding #1 — stale line-number comment
- The comment inside `resolveFreeCall` at the `hasClassTarget` site
referenced "lines ~1994-2008" for the Swift extension dedup block.
Those lines were the inlined pre-SM-13 version; the block has since
been extracted to `dedupSwiftExtensionCandidates`. Replaced the line
reference with the helper name so future readers don't chase dead
line numbers.
Finding #2 — fuzzy-widening asymmetry undocumented
- `resolveFreeCall` intentionally has no `widenCache` parameter and no
D2 fuzzy-widening pass (unlike `resolveCallTarget`'s member-call
path). Added an explicit "Asymmetry vs `resolveCallTarget`" paragraph
to the JSDoc so a caller comparing the two signatures knows the
skipped pass is deliberate and tied to Phase 5.
Finding #3 — constructor-form retry reasons undocumented
- `resolveStaticCall` can return null for three distinct reasons
(empty instantiable pool, homonym ambiguity, ownerless Constructor
nodes). The retry below it unconditionally re-filters with
`'constructor'` form, which is correct for all three but not
obvious. Added a structured three-case comment enumerating each
reason and linking (a) to the SM-12 null-route contract, (b) to
the R7 dedup test, and (c) to the currently-uncovered ownerless-
Constructor path (noted as a future test candidate).
Verification
- `tsc --noEmit` clean
- 175 `resolveFreeCall` + `resolveStaticCall` + sibling tests pass
(sanity check — no behavior change expected)
- No regressions
Follows-up on: https://github.com/abhigyanpatwari/GitNexus/pull/756#issuecomment-4215739052
* test(SM-13): cover ownerless-Constructor retry + PHP free function
Two low-severity test gaps from PR #756 review comment 4215739052 —
previously addressed doc-only, now have concrete test coverage.
Finding #3 low — ownerless-Constructor retry path (previously comment-only)
- The retry after resolveStaticCall returns null handles three distinct
null-return reasons. Cases (a) and (b) were already tested (Interface/
Trait null-route from SM-12, Swift shadowing dedup from R7). Case (c) —
resolveStaticCall step-4 bailout when the tiered pool contains
ownerless Constructor nodes — was only covered by a comment.
- New test: Class + ownerless Constructor in tiered pool, callForm='free'.
Exercises the full chain:
1. resolveStaticCall step 3 walks classCandidates via
lookupMethodByOwner — ownerless Constructor not in methodByOwner,
nothing found.
2. Step 4 detects Constructor in tiered pool, bails with null.
3. resolveFreeCall retry re-runs filterCallableCandidates with
'constructor' form, which prefers Constructor over Class per
CONSTRUCTOR_TARGET_TYPES ordering.
4. Single survivor returned.
- Asserts the Constructor node (not the Class) is the resolved target.
Low — PHP free function coverage gap
- The language coverage table in the same review flagged PHP free
functions (top-level `function helper()` outside any class) as
uncovered. Added a test mirroring the existing Go/Python/Rust/Java/
JS language tests — exercises the `.php` dispatch path for free
calls. Ruby and C/C++ remain uncovered; deferred to a future round
since those languages also have other gaps in the broader test file.
Verification
- `tsc --noEmit` clean
- 3066 unit tests pass (+2 new regression tests)
- 1766 integration tests pass
- Zero regressions
Follows-up on: https://github.com/abhigyanpatwari/GitNexus/pull/756#issuecomment-4215739052
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* feat(SM-12): extract resolveStaticCall from resolveCallTarget
- Add resolveStaticCall(className, methodName, currentFile, ctx, argCount?) using
lookupClassByName + lookupMethodByOwner for O(1) constructor/static resolution
- Add S0 fast path in resolveCallTarget for constructor/free-form class calls
- Export resolveStaticCall from call-processor.ts
- Add 11 unit tests covering constructor resolution, confidence tiers,
arity disambiguation, and resolveCallTarget delegation
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c9471ca9-57ff-4dae-956e-e7ffdc326bc4
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* chore: revert unrelated package-lock.json change
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c9471ca9-57ff-4dae-956e-e7ffdc326bc4
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* refactor: shorten verbose test name per code review feedback
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c9471ca9-57ff-4dae-956e-e7ffdc326bc4
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix(SM-12): address PR #754 review findings
Addresses Claude's review comments on PR #754:
Performance
- Pass pre-computed `tiered` result into `resolveStaticCall` as optional
`tieredOverride` parameter, eliminating the duplicate `ctx.resolve(className,
currentFile)` on every constructor call path.
- Cache `freeFormHasClassTarget` in `resolveCallTarget` so the S0 fast path
and the free-form constructor retry share a single `.some()` scan.
Architecture
- Reconcile `CLASS_LIKE_TYPES` (call-processor) with `CLASS_TYPES`
(symbol-table): `CLASS_LIKE_TYPES = [...CLASS_TYPES, 'Impl']`. This makes
the relationship explicit — the call resolver's set is a strict superset
of the heritage-index set, guaranteeing anything reachable via
`lookupClassByName` also passes the resolver filter. Trait is now included
(harmless: traits have no Constructor nodes, so step-3 returns undefined
and step-5 still returns the class-like node when unique). Documented
the Interface inclusion rationale (static methods + MRO walker).
- Collapse `resolveStaticCall`'s `methodName` parameter into `className` —
all call sites passed identical values. Named constructors (Dart
`User.fromJson()`) arrive as member calls and go through
`resolveMemberCall`. Documented the reserved path for when a language
surfaces a static-method-shaped call with a distinct member name.
- Document the known gap: `callForm === 'member'` constructor patterns
(e.g. Python `models.User()`) are handled by the tail fallback, not S0.
Tests
- Add tiered-override test asserting `ctx.resolve` is not re-invoked when
a pre-computed result is passed in.
- Add language-specific `_resolveCallTargetForTesting` integration tests
for Java (`new User()`), Python (`User()`), and Kotlin (`User()`).
Verification: 3031 unit + 1766 integration tests pass, zero regressions.
* fix(SM-12): restrict resolveStaticCall fallback to instantiable kinds
Addresses the high-severity finding from the Codex adversarial review of
PR #754: `resolveStaticCall`'s step-5 "return the class itself when no
Constructor node is found" fallback reused `CLASS_LIKE_TYPES`, which —
after SM-11 and PR #754's reconciliation — now includes `Interface`,
`Trait`, and `Impl`. That is the method-dispatch set, not the
instantiable set, so constructor-shaped calls could resolve to
non-instantiable nodes and emit false `CALLS` edges.
Concrete failure: Rust same-file `impl User { ... }` alongside
`struct User { ... }` — both land at same-file tier, the Impl is not
filtered out, and the step-5 fallback produces a `CALLS` edge to the
`Impl` block instead of the `Struct`. The same widening exposed
Interface / Trait targets in Java / C# / PHP / Scala.
Fix
- Introduce `INSTANTIABLE_CLASS_TYPES = {'Class', 'Struct', 'Record'}`
as a sibling to `CLASS_LIKE_TYPES`, documenting the contract
explicitly and cross-referencing `CONSTRUCTOR_TARGET_TYPES`.
- Update `CLASS_LIKE_TYPES` JSDoc to clarify it is the method-dispatch
set and add an anti-pattern warning against reusing it for
constructor-fallback filtering.
- Tighten `resolveStaticCall` step 5: filter `classCandidates` through
`INSTANTIABLE_CLASS_TYPES` before the `length === 1` check. This
strips `Impl` from the Rust shadowing scenario (leaving `Struct` as
the sole instantiable target) and null-routes Interface / Trait /
`Impl`-alone scenarios, matching the SM-10 R3 null-route precedent.
- Step 3 (explicit Constructor lookup via `lookupMethodByOwner`) is
intentionally unchanged — its `def.type === 'Constructor'` check is
the correct contract, and legitimate Constructor nodes attached to
`Impl` owners still resolve correctly.
Tests (+10 regression scenarios)
- Positive guards: Struct, Record fallback paths.
- Null-route: Interface (Java/C#/TS), PHP Trait, Rust Trait.
- Rust same-file shadowing: Struct wins over Impl.
- Rust Impl-alone: null-routes (no Struct present).
- Step-3 preservation: Constructor owned by Impl still resolves to the
Constructor node, proving step-5 tightening doesn't leak into step 3.
- Full cascade via `_resolveCallTargetForTesting` for Interface and
Trait — confirms no downstream path silently re-introduces the edge.
Verification
- `tsc --noEmit` clean
- 3041 unit tests pass (+10)
- 1766 integration tests pass
- Zero regressions
Plan: docs/plans/2026-04-09-002-fix-sm12-constructor-fallback-instantiable-only-plan.md
Codex review job: review-mnrao7fr-nv9y0e
* fix(SM-12): address PR #754 second review round
Addresses the 9 findings from the follow-up review on PR #754.
Performance
- Align `freeFormHasClassTarget` with `INSTANTIABLE_CLASS_TYPES`: drop
`Enum` (S0 would always return null for it — wasted lookup work) and
add `Record` (C# records and Kotlin data classes were bypassing S0
entirely). The trigger set and the fallback filter set now agree by
construction, documented inline.
Documentation
- Remove stale single-line JSDoc on `CLASS_LIKE_TYPES` (line 57) that
duplicated the full multi-line block immediately below it — tooling
picks up the first block so the old one-liner was shadowing the
current explanation.
- Rewrite the `resolveStaticCall` JSDoc step list to match the actual
step boundaries in the implementation (steps 3, 4, 5 were blurred in
the old description).
- Add inline comment on step 3 documenting the same-name lookup
assumption (`${candidate.nodeId}\0${className}`) and the symmetric
miss case for Python `__init__`-style constructors.
- Add inline comment on step 4 documenting that it also catches the
ambiguous-step-3 case, and warning against removing the check
without handling that path explicitly.
- Add inline comment on step 5 enumerating the three length outcomes
(0 / 1 / >1) so future readers see the dominant null-route case.
- Document Ruby `User.new` as a known gap alongside Python
`models.User()` in the S0 header comment.
Tests (+2 scenarios)
- Record free-form constructor call via `_resolveCallTargetForTesting`
exercises the aligned `freeFormHasClassTarget` trigger end-to-end,
closing the gap where the direct `resolveStaticCall` test passed
but the integration path was silently bypassing S0.
- Arity threading via `_resolveCallTargetForTesting` asserts that
`call.argCount` flows through resolveCallTarget → S0 →
resolveStaticCall → lookupMethodByOwner, catching any future
regression where the argCount is dropped at the S0 call site.
Verification
- `tsc --noEmit` clean
- 3043 unit tests pass (+2)
- 1766 integration tests pass
- Zero regressions
Plan: docs/plans/2026-04-09-002-fix-sm12-constructor-fallback-instantiable-only-plan.md
Review: https://github.com/abhigyanpatwari/GitNexus/pull/754#issuecomment-4213536094
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* feat(SM-11): extract resolveMemberCall from resolveCallTarget
- Create resolveMemberCall(ownerType, methodName, currentFile, ctx, heritageMap?)
that uses owner-scoped + MRO resolution only (no fuzzy lookup)
- resolveCallTarget delegates member calls (D0 path) to resolveMemberCall
- walkMixedChain uses resolveMemberCall for owner-scoped member-call resolution
- Add 7 unit tests for resolveMemberCall covering direct, inherited, MRO,
null cases, and confidence tier assertions
- Export resolveMemberCall for external use
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/3b7889a9-5f2f-4572-8904-45084210f10d
* fix(SM-11): address PR #744 review
Blocking fixes:
- B1: Revert unrelated package-lock.json gitnexus-shared addition
- B2: Document confidence-tier semantic change on resolveMemberCall
Performance / coupling fixes:
- S1: walkMixedChain now calls resolveMethodByOwner directly (hot path) to avoid throwaway ResolveResult allocation per chain step
- S2: Thread tier from resolveMethodByOwner via { def, tier } tuple; eliminates double ctx.resolve
Alignment with semantic-model plan (Phase 3 target):
- resolveMethodByOwner now iterates ALL class-like candidates from ctx.resolve, deduplicating matches by nodeId. Absorbs D4's ownerId-filtering into the owner-scoped path.
- Handles homonym classes (two Users in different files) without falling through to D1-D4 fuzzy widening
- Shared-ancestor MRO walks automatically dedup (both homonyms walk to same base method)
- Unified direct-vs-MRO lookup under a single canWalkMRO check
Tests added:
- T1: Three D0 skip-condition tests via new _resolveCallTargetForTesting internal export (overloadHints, preComputedArgTypes, hasActiveModuleAlias)
- T2: Rust qualified-syntax null test (trait-inherited method) + direct impl control
- T3: C++ leftmost-base diamond inheritance test
- B2 lock-in: cross-file class tier assertion
- Homonym disambiguation: only-one-owns-method, both-own-method ambiguity, shared-ancestor MRO convergence
Verification:
- tsc --noEmit: clean
- vitest run test/unit/: 3014 passed
- vitest run test/integration/resolvers/: 1746 passed
* test(SM-11): address second PR #744 review round + per-language integration tests
Review fixes (https://github.com/abhigyanpatwari/GitNexus/pull/744#issuecomment-4211877593):
P1 (Performance): Replace Map allocation in resolveMethodByOwner with a firstDef+ambiguous flag pattern. Zero allocation for the common single-candidate case on the hot path — the previous Map approach allocated on every member call regardless of whether deduplication was needed.
P2 (Test gap): Strengthen the module-alias D0 skip test with a homonym fixture (two Users in different files). Previously the test passed whether or not D0 was actually bypassed; the new version proves D0 must be skipped by showing that resolveMemberCall directly returns null (ambiguous) but D1-D4 with alias narrowing picks the right one. Also fixes the underlying D2-vs-alias widening interaction: when filteredCandidates was narrowed by module-alias disambiguation, D2 no longer widens back to the full fuzzy pool (introduces aliasNarrowed boolean flag).
L1 (Language coverage): Add C# and Kotlin implements-split tests at the resolveMemberCall layer.
L2 (Maintainability): Export OverloadHints as @internal so the test can use a direct cast instead of fragile Parameters<...> type inference.
Per-language integration tests:
- rust-child-extends-parent: Direct impl method resolution via D0 (with honest documentation of the trait-method-as-Function gap that is Phase 5 / SM-16 scope)
- java-interface-default-method: User implements Validator with default method resolved via implements-split MRO
- csharp-interface-default-method: Same pattern for C# 8.0+ default interface methods
- kotlin-interface-default-method: Same pattern for Kotlin interfaces with default implementations
- python-multi-level-mro: 3-level C3 linearization (Grandparent ← Parent ← Child)
- cpp-diamond-inheritance: Classic diamond (Base ← A, B ← Derived) via leftmost-base MRO
Verification:
- tsc --noEmit: clean
- vitest run test/unit/: 3015 passed
- vitest run test/integration/resolvers/: 1763 passed (+17 new per-language tests)
* fix(SM-11): Codex adversarial review corrections + deeper D0 fixes
Addresses the three high-severity findings from the Codex adversarial review of PR #744 (https://github.com/abhigyanpatwari/GitNexus/pull/744#issuecomment-4212075120), plus four deeper fixes discovered during regression triage. All discovered issues are now addressed end-to-end rather than papered over with tail-return fallbacks.
Codex review findings:
R1 (C++ diamond): The cpp-diamond-inheritance fixture used non-virtual inheritance, which is genuinely ambiguous in real C++ (two Base subobjects). Changed A and B to use 'virtual public Base' so there's a single shared Base subobject and d.method() is an unambiguous call that the leftmost-base MRO walk correctly resolves.
R2 (C# default-interface): The csharp-interface-default-method fixture called user.Validate() via a User-typed variable, but C# does not inherit default interface methods as callable class members — the call is only valid through an interface-typed variable. Changed App.cs to 'IValidator user = new User(...)' which is the idiomatic dispatch pattern.
R3 (resolveCallTarget tail-return): When D1-D4 receiver filtering produced zero file-matched and zero owner-matched candidates for a member call, the function fell through to the permissive single-candidate tail return — silently emitting CALLS edges for methods that don't belong to the receiver. Added an explicit null-route inside the D1-D4 block that fires only when both filters yielded 0.
R4 (Rust negative assertion): Added the c.trait_only() negative integration test in rust.test.ts demonstrating that direct member calls on Rust structs do not walk trait ancestry. The test now passes because of R3 (previously fell through to the tail return).
Regression triage discoveries:
1. D0 was dead code on the sequential pipeline. The sequential path sets overloadHints for every call regardless of whether the method is overloaded, and the original D0 skip condition '!overloadHints && !preComputedArgTypes' was therefore always false. The Java/C#/C++ SM-9/SM-10 inheritance tests were passing ONLY via the tail-return fallback. Fix: narrow the skip to 'overloadHints && filteredCandidates.length > 1' — skip D0 only when there are actually multiple candidates that need overload disambiguation.
2. lookupMethodByOwner couldn't disambiguate arity-differing overloads (e.g. C++ greet() vs greet(string)). With D0 now firing on the sequential path, same-name/different-arity overloads would collapse to an arbitrary first pick. Fix: added an optional argCount parameter to lookupMethodByOwner + lookupMethodByOwnerWithMRO that filters the overload set by parameterCount/requiredParameterCount before the returnType dedup.
3. Python and Rust class methods are captured as Function nodes (not Method) with ownerId set to the class. The methodByOwner index only accepted 'Method' and 'Constructor' types, so Python class methods and Rust trait methods were invisible to D0. Fix: extended the methodByOwner indexing condition to include 'Function' when ownerId is set. This also unlocks the Rust trait-method negative assertion by ensuring the qualified-syntax MRO strategy has something to return null for.
4. D0 was being skipped when a local variable shadowed an imported module name (Python 'from models.c import C; c = C()' creates both a module alias 'c → models/c.py' AND a typed local 'c'). Fix: the D0 skip now gates on 'aliasNarrowed' (a new boolean tracking whether the alias block actually narrowed filteredCandidates) instead of 'hasActiveModuleAlias'. If the method isn't in the aliased module, the receiver is a typed local variable and D0 should run.
5. PHP trait walk missed the HasTimestamps trait because lookupClassByName did not include 'Trait' type. buildHeritageMap uses lookupClassByName to resolve parent names, so 'BaseModel use HasTimestamps' was failing to register an ancestor edge for BaseModel → HasTimestamps. Fix: added 'Trait' to CLASS_TYPES. The trait is now a valid class-like type for heritage resolution (PHP use, Rust impl Trait for Struct, Scala traits).
Test updates:
- Updated the 'no heritageMap' unit test in call-processor.test.ts to assert the correct null-route behavior instead of the old tail-return fallback.
- Added a new unit test asserting Trait inclusion in the class set.
- Updated the 'does NOT include other type-like labels' test to remove Trait from its rejection set.
Verification:
- tsc --noEmit: clean
- vitest run test/unit/: 3016 passed (+1 new Trait inclusion test)
- vitest run test/integration/resolvers/: 1764 passed (+1 new Rust negative assertion)
- Zero regressions
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Gergo Magyar <magyargergo@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* Add MRO fast path before D2 fuzzy widening in resolveCallTarget
When receiverTypeName is known, try resolveMethodByOwner (owner-scoped
+ MRO lookup) before falling back to the expensive lookupFuzzy in D2.
This short-circuits cross-file member call resolution for the common
non-overloaded case.
The fast path is skipped when overload disambiguation hints are
available (overloadHints or preComputedArgTypes) to avoid picking the
wrong overload for same-return-type overloaded methods.
Passes heritageMap to resolveCallTarget from all 4 call sites:
- Language seed path (processCalls)
- Sequential path (processCalls)
- walkMixedChain fallback
- Worker path (processCallsFromExtracted)
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9e49521f-2472-47bc-96e9-be4a46b073f0
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix(SM-10): address PR #741 review
Correctness:
- Module-alias guard for D0. When call.receiverName matches an active
entry in ctx.moduleAliasMap for the current file, D0 is now skipped
and resolution falls through to D1-D4 which respects the
alias-narrowed candidate pool. Prevents a homonymous class in a
different file from being picked by ctx.resolve(receiverTypeName)
inside resolveMethodByOwner. New unit test pins the contract.
Unit tests (call-processor.test.ts — 3 new):
- D0 hit: child.parentMethod() resolves via MRO walk when
heritageMap is provided.
- D0 skipped: same scenario still resolves via D1-D4 when heritageMap
is undefined (backward-compat guard).
- Module-alias guard: two files both define class User with a save()
method; 'import auth_mod as auth' in app.py must resolve
auth.user.save() to auth_mod.py, not user_mod.py.
Integration language coverage (+3 fixtures/tests):
- swift-child-extends-parent — first-wins, gated on swiftAvailable.
- ruby-child-extends-parent — first-wins.
- php-child-extends-parent — first-wins (uses ParentClass since
'Parent' is a PHP reserved word).
* test(SM-10): address second PR #741 review round
Unit tests (call-processor.test.ts, +2 new):
- overloadHints guard: Java source with two same-return-type overloads
method(int) and method(String), int added first so lookupMethodByOwner
would return it. processCalls auto-generates overloadHints for Java,
forcing D0 to be skipped. o.method("hello") must resolve to
method(String) via literal-inferred disambiguation.
- preComputedArgTypes guard: worker-path equivalent via
processCallsFromExtracted with ExtractedCall.argTypes=['String'].
Same two overloads, same correctness guarantee.
Integration tests (+2 fixtures + test blocks):
- go-child-extends-parent — struct embedding, first-wins
(Go structs are labeled 'Struct' not 'Class' in GitNexus).
- dart-child-extends-parent — extends, first-wins, gated on
dartAvailable like other Dart tests.
Documentation:
- Expanded the fallthrough comment in resolveMethodByOwner to clarify
that unknown-extension paths land on plain lookupMethodByOwner
without an ancestor walk, and that D1-D4 still runs on D0 miss.
* test(SM-10): D0 miss with heritageMap present falls through to D1-D4
Closes the last remaining gap from PR #741 review round 3. The existing
'D0 skipped' test only covered the heritageMap=undefined case, leaving
the miss-with-heritageMap path implicitly covered by integration tests
only. This adds a focused unit test where:
- Class Obj has a method doWork findable via tiered resolution
(import-scoped) but intentionally NOT registered in methodByOwner
(no ownerId), so lookupMethodByOwner misses.
- heritageMap is provided but built from an empty heritage array, so
getAncestors(class:Obj) returns []. The MRO walk yields no parents.
- lookupMethodByOwnerWithMRO therefore returns undefined → D0 miss.
- D1 resolves the receiver type; D2 widens via lookupFuzzy;
D3 file-filter picks the single matching candidate.
- A CALLS edge must still be emitted — D0 miss must not swallow
the call.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* feat(SM-9): add lookupMethodByOwnerWithMRO with HeritageMap parent chain walking
- Export c3Linearize from mro-processor.ts for reuse
- Add lookupMethodByOwnerWithMRO in call-processor.ts with MRO strategy support
- Update resolveMethodByOwner to fall back to MRO walk when HeritageMap available
- Thread heritageMap through walkMixedChain for chain resolution
- Add 10 unit tests covering all acceptance criteria
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/cc58249b-42f1-45a9-89fb-e3917e4d0171
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* feat(SM-9): add Java integration test with class Child extends Parent fixture
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/cc58249b-42f1-45a9-89fb-e3917e4d0171
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* docs: address code review comments on MRO strategy documentation
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/cc58249b-42f1-45a9-89fb-e3917e4d0171
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* perf(SM-9): address PR #740 review comments
- Eliminate double direct lookup in resolveMethodByOwner: delegate
straight to lookupMethodByOwnerWithMRO when a HeritageMap is
available (the MRO helper already does the direct lookup before
walking ancestors). Fallback path handles the no-HeritageMap case.
- Memoize C3 linearization per HeritageMap via a WeakMap keyed cache.
HeritageMap is immutable after build, so C3 results are stable for
its lifetime; WeakMap lets the cache auto-drain when the HeritageMap
is GC'd. Null sentinel caches linearization failures so cyclic
hierarchies are not reprocessed. Eliminates per-call buildParentMap +
c3Linearize on Python codebases.
- ancestors variable typed as readonly to accept the cached result
without copying.
- Add four missing MRO unit tests: Kotlin implements-split, C#
implements-split, JavaScript first-wins (separate provider from TS),
and C++ leftmost-base diamond (first diamond test for C++).
* fix(SM-9): CI prettier + address PR #740 follow-up review
- Fix CI prettier failure in test/integration/resolvers/java.test.ts
(auto-formatted — was introduced in 37563a31 before my first fix
commit but had not been caught locally).
- Pin caller on the SM-9 Java integration test (parentMethodCall.source
=== 'run') so a regression that misattributes the CALLS edge fails.
- Add two implements-split unit tests:
* Ambiguous default from two interfaces → BFS first-wins. Pins the
contract that lookupMethodByOwnerWithMRO returns a defined result
(full ambiguity detection is deferred to computeMRO graph pass).
* Class method precedence over interface default: Child extends Base
implements IFoo where both define handle() — documents that BFS
visits the extends edge first, matching Java's class-wins rule.
- Add @internal JSDoc on lookupMethodByOwnerWithMRO clarifying it is
exported only for testing; resolveMethodByOwner is the proper entry
point for callers.
* test(SM-9): per-language integration fixtures and tests for inherited method resolution
Extends the SM-9 integration coverage beyond Java with six new
child-extends-parent fixtures, one per MRO strategy:
- python-child-extends-parent → C3 strategy
- typescript-child-extends-parent → first-wins
- javascript-child-extends-parent → first-wins (separate provider)
- kotlin-child-extends-parent → implements-split
- csharp-child-extends-parent → implements-split
- cpp-child-extends-parent → leftmost-base
Each fixture follows the java-child-extends-parent pattern:
- Parent class with a single method
- Child class extending Parent, no override
- App class/function that instantiates Child and calls
the parent method — exercises the full ingestion pipeline,
HeritageMap construction, and lookupMethodByOwnerWithMRO walk.
For every fixture the matching integration test asserts:
- Parent and Child classes are detected
- Child → Parent EXTENDS edge is emitted
- The parent-method call resolves to the correct target file
- The caller is pinned (source === 'run' / 'Run') to catch
edge misattribution regressions
Rust is intentionally omitted — its qualified-syntax strategy
returns undefined from lookupMethodByOwnerWithMRO by design, so
there is no inherited-method resolution to assert against.
All 1739 integration resolver tests pass (+18 new SM-9 tests
across 6 languages).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* Initial plan
* feat(SM-8): add HeritageMap with MRO-aware parent/ancestor lookup
- New heritage-map.ts: HeritageMap interface with getParents() and getAncestors()
- buildHeritageMap() consumes ExtractedHeritage[], resolves names via lookupClassByName
- Cycle protection and bounded depth (MAX_ANCESTOR_DEPTH=32) in getAncestors
- Worker path: HeritageMap built from deferredWorkerHeritage, threaded into processCallsFromExtracted
- Sequential path: Heritage accumulated across chunks, HeritageMap built after all chunks, passed to processCalls
- 18 unit tests covering parent lookup, multi-level, diamond, cycles, missing parent, bounded depth
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c413e0a3-5d63-4ddb-8ece-02fe6ed99efd
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* test: rename cycle test for clarity per code review
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c413e0a3-5d63-4ddb-8ece-02fe6ed99efd
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* refactor(SM-8): merge implementor map into heritage map
- Add `getImplementorFiles(interfaceName)` to HeritageMap interface
- Build implementor index (interface name → file paths) alongside parent
lookup in `buildHeritageMap`, using same `resolveExtendsType` logic
- Remove `ImplementorMap` type, `buildImplementorMap`, `mergeImplementorMaps`
from call-processor.ts
- Update `findInterfaceDispatchTargets`, `processCalls`, and
`processCallsFromExtracted` to use HeritageMap for both parent
lookup and implementor dispatch
- Pipeline: single `buildHeritageMap` call replaces separate
buildImplementorMap + buildHeritageMap for both worker and
sequential paths
- Migrate implementor tests from call-processor.test.ts to
heritage-map.test.ts (4 new getImplementorFiles tests)
- Update interface dispatch test to use buildHeritageMap instead
of hand-constructed ImplementorMap
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/085dffb4-b31e-4aa5-9aa3-4314bc0010e7
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* test: rename implementor test for clarity per code review
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/085dffb4-b31e-4aa5-9aa3-4314bc0010e7
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix(SM-8): address PR #739 review comments
- pipeline.ts: cache chunk file contents from Pass 1 to eliminate
double-read of sequential chunks in Pass 2. Peak memory drains
incrementally as Pass 2 processes each chunk.
- heritage-map.ts: document Rust trait-impl omission from implementor
index and the interface-name collision limitation.
- heritage-map.test.ts: add six tests covering the extends->IMPLEMENTS
path across C# (interfaceNamePattern), Swift (heritageDefaultEdge),
Java (symbol-table Interface lookup), Kotlin, PHP, and the Rust
trait-impl omission.
- pipeline.ts: comment why the heritage accumulation uses a manual
push loop instead of spread (ref #650).
* test(SM-8): address second PR #739 review pass
- Add TypeScript implements test to getImplementorFiles (closes
the .ts coverage gap flagged by the bot reviewer).
- Tighten deep-chain boundary assertion from toBeLessThanOrEqual(32)
to toBe(32) so a future regression returning fewer ancestors
fails loudly. Added an ancestors[31] === 'class:Level32' check
to pin the upper boundary.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* refactor(call-processor): use class lookup index in phase p
* test(call-processor): cover class lookup fallback
---------
Co-authored-by: 许恩宁 <xuenning@qiyi.com>
* refactor(type-env): use class lookup index for type resolution
* test(type-env): add lookupClassByName regression coverage
* test(type-env): expand class lookup regression coverage
---------
Co-authored-by: 许恩宁 <xuenning@qiyi.com>
Add eagerly-populated methodByOwner index to SymbolTable, keyed by
ownerNodeId\0methodName. Used by walkMixedChain as a fast path for
resolving intermediate method calls in cross-class chains like
user.getAddress().getCity().getZipCode(), avoiding expensive fuzzy
lookups when the owner type is already known.
Handles overloaded methods: returns the first match when all overloads
share the same returnType, undefined when return types differ (ambiguous).
- Add lookupMethodByOwner to SymbolTable interface + implementation
- Add resolveMethodByOwner helper in call-processor.ts
- Add fast path in walkMixedChain before resolveCallTarget fallback
- Add Java cross-class chain fixture + 6 integration tests
- Add 148 unit tests for methodByOwner index behavior
* fix(ignore): respect negation patterns in .gitnexusignore childrenIgnored
childrenIgnored checked `ig.ignores(rel) || ig.ignores(rel + '/')` which
short-circuited on the bare path — directory-only negation patterns like
`!iOS/` were missed because `ig.ignores('iOS')` treats the path as a file.
Now only checks with trailing slash since childrenIgnored is only called
for directories. Bare-name patterns (e.g. `local`) still match per gitignore spec.
Fixes#596
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(ignore): add edge-case for bare `!dir` negation pattern
Verifies that `!iOS` (without trailing slash) also un-ignores the iOS/
directory — confirms the `ignore` package normalizes both `!dir` and
`!dir/` forms consistently when tested with a trailing-slash path.
Addresses non-blocking review suggestion on #654.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(ignore): link ignore package docs for bare-name normalization
Adds references to the `ignore` package documentation in both the
childrenIgnored comment and the bare-negation test, explaining why
`!iOS` (without trailing slash) also re-includes the iOS/ directory.
Addresses non-blocking review suggestion on #654.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: replace Array.push(...spread) with loop to prevent stack overflow
On large codebases (78K+ C files), deferred arrays in
runChunkedParseAndResolve accumulate 100K+ entries. The spread
operator in push(...array) puts every element on the call stack
as a function argument, exceeding the maximum call stack size.
Replace all 11 occurrences of `arr.push(...other)` with
`for (const _item of other) arr.push(_item)` which uses
constant stack space regardless of array size.
Fixes#649
* fix: also replace push(...spread) in parsing-processor.ts
* style: format pipeline.ts to match prettier config
---------
Co-authored-by: tian kian tan <tan@example.com>
* feat: same-arity overload disambiguation via type-hash suffix (#651)
Add ~type1,type2 suffix to Method/Constructor node IDs when same-arity
overloads with different parameter types exist in the same class. Also add
$const suffix for C++ const-qualified method overloads via new isConst field.
Key changes:
- typeTagForId() detects same-arity collisions and appends ~typeTag
- constTagForId() detects const/non-const collisions and appends $const
- TS/JS excluded from type-hashing (overload signatures collapse to impl body)
- Sequential findEnclosingFunction fixed: falls through on ambiguous same-class
candidates instead of picking first; fallback path includes typeTag + constTag
- Per-call-site integration tests across Java, C#, Kotlin, C++, TypeScript
- Cross-file + chain resolution tests for all 5 languages
- C++ isConst extraction via tree-sitter type_qualifier in function_declarator
1710 integration + 18 unit tests pass.
* fix: preserve generic/template args in type-hash, perf + type safety fixes
- Add rawType field to ParameterInfo preserving full type text (vector<int>)
while type stays simplified (vector). typeTagForId uses rawType for tags.
- Populate rawType in all 11 language method extractors
- Add buildCollisionGroups() to pre-group methods by name#arity (O(N) once
per class instead of O(N) per method call)
- Cache method extraction in call-processor findEnclosingFunction fallback
- Fix null guards on getLanguageFromFilename in all findEnclosing paths
- Tighten SKIP_TYPE_HASH_LANGUAGES to ReadonlySet<SupportedLanguages>
- Document ID stability invariant on first overload introduction
- C++ integration tests: template overloads (vector<int> vs vector<string>),
cross-file template + chain resolution, out-of-class method definitions
1718 integration + 20 unit tests pass.
* fix: add rawType to method-extraction unit test assertions
All 26 parameter .toEqual() assertions in method-extraction.test.ts
needed the new rawType field added to match ParameterInfo schema change.
* perf: cache tempMap/groups per class, consolidate extractFromNode
- Cache derived method map + collision groups per classNode.id in
parsing-processor (avoids rebuild per method in same class)
- Replace per-call extractFromNode with cached class extraction +
funcName:line lookup in call-processor fallback (avoids AST walk
per call site)
- Remove dead clearEnclosingFunctionCache export, fix JSDoc
* test: add sequential-path integration test for same-arity overloads
Add skipWorkers option to PipelineOptions to force sequential parsing.
New test suite verifies type-hash disambiguation produces identical
results through the sequential path (parsing-processor + call-processor
findEnclosingFunction) as the worker path.
* feat: MethodExtractor configs for Python, PHP, Swift, Dart, Rust, Ruby with exhaustive integration tests
Add per-language MethodExtractionConfig for all remaining tree-sitter languages
(RFC #568 PR 2). Each config follows the established createMethodExtractor()
factory pattern — no new types, no parse-worker changes.
Configs:
- Python: @abstractmethod, @staticmethod/@classmethod, *args/**kwargs, type hints, _/__ visibility
- PHP: abstract/final/static keywords, PHP 8 #[] attributes, __construct/__destruct
- Swift: 5-level visibility, protocol-as-abstract, static/class methods, @ attributes
- Dart: _ convention visibility, abstract (no body), method_signature unwrapping
- Rust: pub visibility, &self receiver, trait_item + impl_item, #[] attributes
- Ruby: positional visibility via sibling-walk, singleton_method as static
Integration fixtures (18 directories) covering 3 resolution patterns:
- Method enrichment: parameterTypes, isAbstract, isFinal, annotations on graph nodes
- Overload dispatch: arity-based CALLS resolution via parameterTypes
- Abstract dispatch: abstract/concrete method distinction (Python, PHP, Rust, Swift)
Go deferred — requires factory changes for receiver-based method extraction.
Closes#571
* fix: address code review findings across 6 MethodExtractor configs
Fix all actionable items from the PR #624 deep-dive review:
Dart (critical — fixes 6 CI failures):
- isDartStatic: check children first, siblings as fallback
- isDartAbstract: handle declaration nodes for abstract methods
- extractSingleParam: detect required keyword as sibling token
- Add declaration to methodNodeTypes, mixin_declaration to typeDeclarationNodes
- Add member call query for variable assignments in tree-sitter-queries
Python:
- hasDecorator now matches dotted paths (e.g. @abc.abstractmethod)
- Fix version comment from ^0.23.6 to 0.23.4
PHP:
- Add enum_declaration to typeDeclarationNodes (PHP 8.1+)
- Add version comment for 0.23.12
Swift:
- Add isOverride using hasKeyword/hasModifier pattern
Rust:
- Fix version comment from ^0.23.2 to 0.23.1
Also: identifier fallback in generic.ts for mixin owner names,
Dart integration test label fix (Method vs Function), version
comment for tree-sitter-dart 1.0.0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: Dart extension_declaration and Ruby module_function support
Dart:
- Add extension_declaration to typeDeclarationNodes and extension_body
to bodyNodeTypes — extension methods are now extracted into the graph
- Add extension_declaration and mixin_declaration to CLASS_CONTAINER_TYPES
for HAS_METHOD edge resolution
Ruby:
- module_function now maps to visibility 'private' in extractRubyVisibility
- module_function methods marked isStatic via backward-walk in isStatic
- Override semantics: private/public after module_function resets isStatic
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(go): Go MethodExtractor config with receiver-based extraction
Add Go as the 13th language with a per-language MethodExtractor config.
Go methods are top-level (not nested in struct bodies), so this adds
extractFromNode() to the MethodExtractor interface for direct method
node extraction without an enclosing class.
Config extracts:
- Name from field_identifier (methods) / identifier (functions)
- Return type including multi-return (first type from parameter_list)
- Parameters with variadic support
- Visibility via uppercase/lowercase convention
- Receiver type with pointer unwrapping (*User → User)
- isStatic for functions (no receiver)
Infrastructure:
- extractOwnerName optional hook on MethodExtractionConfig
- extractFromNode on MethodExtractor (factory auto-implements)
- Parse-worker uses extractFromNode when no enclosing class found
- method_declaration added to CLASS_CONTAINER_TYPES
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: method enrichment integration tests for 7 languages + TS abstract class fix
Add method-enrichment integration test fixtures and test blocks for
Go, C++, Java, Kotlin, TypeScript, JavaScript, and C#. Each fixture
tests: class detection, HAS_METHOD edges, EXTENDS edges, isAbstract,
isStatic, annotations, parameterTypes, and CALLS edge resolution.
Fixes found during testing:
- Remove method_declaration from CLASS_CONTAINER_TYPES (added for Go
but broke Java/C# HAS_METHOD edge resolution — method_declaration
is also Java's method node type)
- Add abstract_class_declaration query to TypeScript tree-sitter
queries (was missing, so abstract classes were invisible to pipeline)
1699 integration tests pass across 20 test files, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: format typeDeclarationNodes array for better readability in PHP config
* fix: Go interface methods + Rust impl-for-Struct owner resolution
Go:
- Add method_elem to methodNodeTypes so interface method signatures
are extractable as abstract methods
- Integration test: Animal interface detected, Speak isAbstract,
CALLS edges from app.go
Rust:
- Add extractOwnerName to resolve impl Trait for Struct to the
concrete Struct (not the Trait) — fixes method misattribution
- Fix findEnclosingClassId to generate Struct: label (not Impl:)
for impl blocks so HAS_METHOD edges resolve to struct nodes
- Tighten abstract-dispatch test: assert SqlRepo owns find/save
generic.ts:
- Fix extractOwnerName fallback: when hook returns a value, skip
both name-field and type_identifier scan (was overwriting result)
1703 integration tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: code review response — Rust impl label, Swift params, Dart async, sequential methodExtractor
Address code review findings from PR #624:
- ast-helpers: Rust `impl Trait for Struct` uses Struct label (matches existing
graph node), plain `impl Struct` uses Impl label (matches definition.impl)
- swift: fix parameter type extraction (user_type not type_annotation), detect
default values as function_declaration siblings, add version comment
- dart: isDartAsync now detects async*/sync* generators, add clarifying comment
for declaration nodes in extension bodies
- python: correct isFinal comment (PEP 591 @typing.final exists, just not modeled)
- parsing-processor: port methodExtractor enrichment to sequential path so
isAbstract/isStatic/visibility/annotations/isFinal populate on <15-file repos
- tests: remove silent `if (prop !== undefined)` guards, assert properties
directly, fix label queries (Dart Method vs Function, Swift Method for protocol
methods), add Rust HAS_METHOD sourceLabel tests, Swift parameterTypes tests,
and Dart async/sync* integration tests with fixture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: Rust grammar gap + qualified method IDs to resolve same-file collisions
Phase 1 — Rust grammar:
- Add function_signature_item query to RUST_QUERIES so abstract trait methods
(fn speak(&self) -> String;) become graph nodes with isAbstract=true
Phase 2 — Qualified method IDs:
- findEnclosingClassInfo returns {classId, className} for AST-based class lookup
- Both parsing paths (sequential + worker) qualify method/property IDs with
enclosing class: Method:file:ClassName.method instead of Method:file:method
- extractFuncNameFromSourceId handles ClassName.method format
- Fixes silent data loss when same-name methods in different classes shared a
file (e.g., Animal.speak and Dog.speak both now exist as distinct graph nodes)
Test updates:
- Rust: abstract+concrete trait methods both verified, function count adjusted
- Python: static method disambiguation now emits 2 CALLS edges (correct — no
more ID collision masking the second call)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: owner-aware resolution for qualified method IDs
Address Codex adversarial review findings after qualified ID change:
- findEnclosingFunction: disambiguate candidates by ownerId when multiple
same-name methods exist in file; qualify fallback-generated IDs
- findEnclosingFunctionId (worker): qualify sourceIds with enclosing class
name so CALLS source attribution matches definition-phase node IDs
- buildExportedTypeMapFromGraph: use lookupExactAll + nodeId match instead
of lookupExactFull which returns first definition for bare name
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: methodExtractor variadic arity, return type preservation, PHP abstract dispatch
Three bugs in the methodExtractor enrichment path broke 17 integration tests:
1. Variadic parameterCount: buildMethodProps and parse-worker set
parameterCount = info.parameters.length even for variadic functions,
causing arity filtering to reject valid calls. Now checks isVariadic
and sets parameterCount = undefined (matching extractMethodSignature).
2. C++ bare `...` token: extractCppParameters only iterated named
children, missing the unnamed `...` token in C-style variadics like
log_entry(const char* fmt, ...). Added fallback scan of all children.
3. Return type stripping: All 11 language extractReturnType functions
used extractSimpleTypeName() which strips generic parameters
(List<User> → "List", Task<User> → "Task"). Changed to .text?.trim()
to preserve full generic types needed for for-loop iterable resolution,
async-await binding, and return-type inference.
Also fixes PHP abstract dispatch test that matched SqlRepository instead
of the interface due to ambiguous filePath.includes('Repository') filter,
and adds parent-walk fallback in PHP isAbstract for extractFromNode path.
* chore: remove plan and review artifacts from PR
* fix: address Round 4 review findings + infrastructure improvements
- Ruby: add singleton_class support for class << self methods (4 new tests)
- PHP: add enum_declaration to CLASS_CONTAINER_TYPES
- Dart: add mixin/extension labels to CONTAINER_TYPE_TO_LABEL
- Swift: add TODO for unverifiable struct/enum node types on Node 22
- C#: add grammar version comment (0.23.1)
- Ruby: fix version comment range to pin (0.23.1)
- Rust/ast-helpers: add cross-reference comments for impl_item duplication
- ast-helpers: document CLASS_CONTAINER_TYPES ↔ typeDeclarationNodes invariant
- generic.ts: replace Array.includes with Set for O(1) dedup in addNestedBodies
- Go/Python/Ruby: align isAbstract signature with 2-param interface contract
- CLAUDE.md: fix malformed backtick around gitnexus:start HTML comment
- parsing-processor: add per-class method extraction cache (eliminates O(N*M))
- ast-helpers: add scoped_type_identifier to impl_item resolution
- call-processor: add dev-mode warnings at silent candidates[0] fallbacks
- MCP context(): surface methodMetadata for Method/Function/Constructor nodes
- resources.ts: update schema to list all stored Method properties
* fix: singleton_class HAS_METHOD edge regression in findEnclosingClassInfo
singleton_class (class << self) was added to CLASS_CONTAINER_TYPES but
has no name field — its receiver `self` has node type 'self', not
'identifier'. findEnclosingClassInfo now walks up to the enclosing
class/module to inherit its name, matching ruby.ts:extractOwnerName.
Also fixes findEnclosingClassNode in parse-worker.ts to skip
singleton_class and return the actual class/module node.
Adds integration test assertions for from_habitat (class << self method):
HAS_METHOD edge from Animal, isStatic=true, parameterCount=1.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(vue): add Vue SFC (.vue) support for indexing
Vue Single File Components are now fully supported in the indexing pipeline.
The implementation extracts <script> / <script setup> blocks from .vue files
and parses them using the existing TypeScript tree-sitter grammar — no new
npm dependencies required.
Key changes:
- SFC script extractor: regex-based extraction of <script setup lang="ts">
blocks with correct line offset mapping back to the .vue file
- Vue language provider: reuses TypeScript queries, type config, field
extractors, and named binding extraction
- Import resolution: .vue added to EXTENSIONS so `import Foo from './Foo'`
resolves to Foo.vue; Vue import resolver delegates to TS resolver for
tsconfig path alias support
- Export detection: <script setup> top-level bindings are implicitly exported
- Template component detection: PascalCase tags in <template> emit CALLS edges
- Line offsets applied to all emitted positions (startLine, endLine, route
lineNumbers, decorator positions) in both worker and sequential paths
Validated on a 3,553-file Vue project:
Before: 24,693 nodes | 73,614 edges | 0 symbols from .vue
After: 30,495 nodes | 112,324 edges | 5,213 symbols from .vue
18,682 imports from .vue | 5,826 vue-to-vue imports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(typescript): track destructured call results in TypeEnv
Extend `extractPendingAssignment` to handle object destructuring from
function calls and await expressions:
const { isMaker } = useUserRole()
const { data } = await fetchData()
const { name } = repo.getProfile()
Previously, only `const { x } = someVariable` (identifier RHS) produced
TypeEnv bindings. Call-expression RHS was silently skipped, leaving
destructured properties untracked.
The fix emits a synthetic `callResult` item plus N `fieldAccess` items
per destructured property, which the existing fixpoint resolver processes
in 2 iterations. No changes needed to type-env.ts, PendingAssignment
types, or call-processor — the existing infrastructure handles it.
Also extracts a `collectDestructuredFields` helper to share the
object_pattern property iteration logic between the identifier and
call-expression branches.
Note: Full property-type resolution requires the callee to have a
declared returnType in the SymbolTable. Arrow-function composables
without type annotations (common in Vue/React) won't resolve property
types until return-type inference is added in a future change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(vue): address PR review issues for Vue SFC support
- Extract duplicated isVueSetupTopLevel to vue-sfc-extractor.ts shared
utility, removing identical copies from parse-worker.ts and
parsing-processor.ts
- Fix VUE_BUILT_INS to be a superset of TS BUILT_INS by importing and
spreading the TypeScript set, preventing spurious unresolved calls for
standard built-ins (Symbol, BigInt, WeakMap, array methods, etc.)
- Add Vue template component CALLS edge resolution in both sequential
and worker paths (call-processor.ts), matching PascalCase template
tags against imported .vue file basenames via the import map
- Add integration test for template PascalCase CALLS edges
(App.vue → Button.vue)
- Add integration test for isExported: false on non-setup <script>
blocks (OldStyle.vue options API)
- Add comment explaining TEMPLATE_RE greedy regex behavior for nested
template tags
- Fix stale language count comment (14 → 15) and remove dead code
branch in test
Made-with: Cursor
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spec covers 4 HIGH-priority issues from review: path traversal via
group name, gRPC proto regex nested braces, service boundary detector
directory exclusions, double-close of LadybugDB pools.
Plan: 6 tasks with TDD, ordered by complexity.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire extractors into the sync pipeline with service boundary detection.
GroupService provides high-level API for all group operations.
- Sync pipeline: orchestrates extraction (HTTP, gRPC, topics) with
service boundary assignment and exact matching
- GroupService: groupList, groupSync, groupContracts, groupQuery,
groupStatus (groupImpact deferred to cross-repo follow-up PR)
- CLI: group create/add/remove/list/sync/contracts/query/status
- MCP tools: group_list, group_sync, group_contracts, group_query,
group_status
- Monorepo fixture: 3 services (auth/orders/gateway) connected via
gRPC + Kafka + HTTP — all intra-repo cross-links discovered
- Documentation: CLI commands and MCP tools added to both READMEs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Core foundation for repository group analysis:
- Type system: ContractType, ExtractedContract, StoredContract, CrossLink
with optional `service` field for intra-repo matching
- Config parser for group.yaml (repos, detection flags, matching thresholds)
- Contract registry storage with atomic writes
- Exact matching engine with per-type normalization (HTTP, gRPC, topic)
and intra-repo support (different services within same repo can match)
- Extract LadybugDB pool-adapter from MCP backend for reuse by sync pipeline
- Git staleness checker for group status reporting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>