mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-07 08:26:11 +00:00
131 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9ad1984b17
|
fix: resolve C/C++ cross-file calls through transitive #include chains (#816)
* fix: resolve C/C++ cross-file calls through transitive #include chains In C/C++, #include is transitive: if a.c includes b.h and b.h includes c.h, then a.c can call any function declared in c.h. The wildcard import synthesis only walked direct imports (1 hop), missing symbols reachable through transitive header chains. This is the dominant pattern in large C codebases — Redis's db.c includes server.h which includes dict.h, so db.c should resolve calls to dictFind() declared in dict.h and defined in dict.c. Before this fix, those cross-file call edges were missing entirely. The fix expands the import closure transitively for C/C++ files before synthesizing wildcard bindings. A BFS walks ctx.importMap and graphImports to collect all transitively reachable headers, then passes the full closure to synthesizeForFile. Tested on Redis (github.com/redis/redis): - Before: dictFetchValue had 0 cross-file callers, processCommand had 0 - After: dictFetchValue has 9 callers, processCommand has 1, +1946 edges total Fixes #813 * refactor(ingestion): dispatch wildcard synthesis by import-semantics strategy Generalize PR #816's C/C++ transitive #include fix into a language-agnostic strategy pattern. The `wildcard-synthesis.ts` pipeline phase no longer references `SupportedLanguages.C` / `SupportedLanguages.CPlusPlus` — it dispatches on `provider.importSemantics` via an exhaustive `switch`. Also fixes a correctness bug the original BFS introduced: `queue.pop()` (LIFO/DFS) reversed the iteration order of `#include` directives, which — combined with first-seen-wins dedup in `synthesizeForFile` — silently bound overloaded symbols to the wrong header. For the `cpp-calls` fixture, `write_audit("hello")` was being resolved to `zero.h`'s arity-0 overload instead of `one.h`'s arity-1 overload, breaking arity narrowing. Switched to FIFO (`queue.shift()`) with direct imports seeded in declaration order. Taxonomy (researched across 20+ languages + stack-graphs / SCIP prior art): | Tag | Traversal | Languages | |---------------------|-----------------|------------------------------------| | named | none | TS, JS, Java, C#, Rust, PHP, Kotlin| | wildcard-transitive | BFS closure | C, C++ | | wildcard-leaf | single hop | Go, Ruby, Swift, Dart | | namespace | none at import | Python | | explicit-reexport | topological DAG | (scaffold; TS `export *` future) | Changes: - Widen `ImportSemantics` union from 3 to 5 tags with full taxonomy JSDoc - Retag 5 providers: c-cpp (x2) → wildcard-transitive; dart, go, ruby, swift → wildcard-leaf - Move BFS closure into `wildcard-synthesis.ts` as `expandTransitiveIncludeClosure` (pipeline-owned; providers stay pure declarations) - Replace `if (lang === C || CPP)` with `dispatchSynthesis` helper called by both Loop 1 (ctx.importMap) and Loop 2 (graphImports) so a future transitive language whose edges arrive via graphImports gets closure expansion consistently - `never`-assertion default arm forces compile-time exhaustiveness - `explicit-reexport` arm falls through to leaf behavior (scaffold; TODO: implement re-export DAG walk for TS `export *` / Rust `pub use`) - New unit tests covering circular includes, deep chains, diamond dedup, graphImports-only paths, and order-preservation (the regression fix) Verification: - All existing C/C++ transitive tests pass unchanged - Previously failing `cpp.test.ts > resolves run → write_audit to one.h via arity narrowing` now passes - `tsc --noEmit` clean - 225/225 tests pass across wildcard-synthesis, cross-file-binding, cpp resolver, and new closure unit tests * fix(ingestion): bound closure size, O(1) dequeue, track Strategy 4 (#816 review) Address @xkonjin's review feedback on the import-resolution strategy refactor: 1. **DoS guard**: cap transitive closures at 5,000 files via `MAX_TRANSITIVE_CLOSURE_SIZE`. Pathological codebases (boost-style headers, monoheader kernels) could previously produce closures with tens of thousands of entries per translation unit. BFS now stops early and returns a partial closure rather than risking OOM. The closest-headers-first BFS ordering means the partial closure still contains the files overload resolution cares about. 2. **Perf**: replace `Array.prototype.shift()` (O(n)) with a head-index queue (O(1) dequeue). Deep chains previously had quadratic BFS behavior; now linear in closure size. 3. **Strategy 4 tracking**: change TODO in `dispatchSynthesis` to `TODO(#821)` referencing the filed issue for TS `export *` / Rust `pub use` DAG-walk implementation, and clarify that today's leaf fallthrough preserves correctness for direct imports — only the extra re-export traversal is missing. 4. **Test**: new unit test exercising the 5,000-file cap on a 10k-file synthetic chain, verifying partial-closure invariants (starts from importer side, bounded, deep nodes excluded). Not addressed in this commit (followups): - Review point 3 (graphImports-only deep-chain *integration* fixture): unit tests already exercise the `graphImports` traversal path directly in isolation and combined with `importMap`. A fixture that stresses graphImports-only transitive resolution is valuable but requires understanding when the pipeline populates graphImports distinctly from ctx.importMap — tracking as a followup rather than blocking this PR. --------- Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
759c983dce
|
fix(extractors): resolve 3 silent contract mis-resolution bugs (#793) (#817)
* fix(extractors): resolve 3 silent contract mis-resolution bugs (#793) Addresses Codex adversarial review findings for extractor contract resolution on the new group extractor surface. F1 (manifest-extractor): resolveSymbol passed the full "METHOD::path" contract string through normalizeRoutePath, producing "/GET::/api/orders" which never matches Route.name. Adds parseHttpContract() helper that strips the METHOD:: prefix before path normalization. Contract ID construction (buildContractId) is unchanged. F2 (http-route-extractor): graph-assisted backfill used path-only detections.find(), so multi-verb same-URL files attached the wrong verb/handler to provider rows and inferred the wrong verb on FETCHES consumer edges. Now requires path+method match when method is known, and skips backfill when method is unknown and multiple detections tie on path. F3 (grpc-extractor): resolveProtoConflict seeded bestScore=-1 and only replaced on strict >, so all-zero-score ties silently selected candidates[0]. Now computes all scores, counts ties at the top score, and returns null on ambiguity (caller skips contract emission and warns with service name + candidate paths). All three fixes are test-first; 73 tests pass across the three suites. No schema changes, no new dependencies, contract ID wire format (http::METHOD::path, grpc::pkg.Service/Method, http::*::path) preserved. * fix(extractors): address PR #817 review — ambiguous symbol pick + contract id casing Copilot + Claude review on PR #817 flagged two follow-up bugs on top of the F1/F2/F3 fixes: 1. http-route-extractor: ambiguous multi-verb case left handlerName null but still ran the CONTAINS DB query. pickSymbolUid(syms, null) then silently picked pool[0] — reintroducing handler mis-attribution via a different route than the .find() bug F2 fixed. Now gates symbol enrichment on an ambiguousCandidates flag so the file-basename fallback wins instead. 2. manifest-extractor: buildContractId passed raw user casing through for the explicit-method form, so get::/api/orders and GET::/api/orders produced different contract ids even though parseHttpContract upper-cases during lookup. Now reuses parseHttpContract + normalizeRoutePath to canonicalize both method and path, so logically equivalent manifest inputs share a contract id (and share a manifestSymbolUid fallback). Adds one regression test per bug: lowercase vs uppercase manifest contract ids must match, and ambiguous multi-verb with CONTAINS rows must not silently attach a real handler or call the CONTAINS query at all. 75 tests pass across the three extractor suites. * chore: prettier formatting |
||
|
|
26ff700e37
|
refactor(pipeline): DAG-based phase architecture + container-logic extraction to LanguageProvider (#809)
* Initial plan * refactor: move language-specific container node logic into LanguageProvider - Add resolveEnclosingOwner hook to LanguageProviderConfig - Add staticOwnerTypes to MethodExtractionConfig - Implement Ruby resolveEnclosingOwner (singleton_class → class/module) - Replace hardcoded STATIC_OWNER_TYPES with config.staticOwnerTypes - Move Ruby static types to rubyMethodConfig - Move Kotlin static types to kotlinMethodConfig - Remove Ruby singleton_class branch from findEnclosingClassInfo - Collapse seqFindEnclosingClassNode/seqFindRawEnclosingContainerNode into single provider-aware seqFindEnclosingOwnerNode - Update worker path to pass provider.resolveEnclosingOwner Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/bc9f9d4d-f749-4872-9ff2-17fc86e08787 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test: add regression tests for config-driven staticOwnerTypes and resolveEnclosingOwner hook Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/bc9f9d4d-f749-4872-9ff2-17fc86e08787 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * refactor: implement DAG-based pipeline architecture with phase extraction Restructure the ingestion pipeline from a ~1800-line monolithic orchestrator into a DAG (Directed Acyclic Graph) of named phases with explicit dependencies. New files under pipeline-phases/: - types.ts: PipelinePhase, PipelineContext, PhaseResult contracts - runner.ts: DAG runner with topological sort validation - scan.ts, structure.ts, markdown.ts, cobol.ts: early phases - parse.ts + parse-impl.ts: chunked parse + resolve (the core) - routes.ts, tools.ts, orm.ts: post-parse enrichment phases - cross-file.ts + cross-file-impl.ts: cross-file binding propagation - mro.ts, communities.ts, processes.ts: graph analysis phases - index.ts: barrel export pipeline.ts reduced from ~1960 lines to ~184 lines: - DAG phase array declaration - runPipelineFromRepo as thin orchestrator - topologicalLevelSort retained for backward compat Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/136bf9c3-2f4f-449b-9fff-001332c8371c * test: add DAG runner unit tests, update ARCHITECTURE.md with phase DAG docs Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/136bf9c3-2f4f-449b-9fff-001332c8371c * fix: address code review - pass resolutionContext through parse output, fix worker URL path Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/136bf9c3-2f4f-449b-9fff-001332c8371c * fix: declare transitive parse dependency explicitly in mro/communities/processes phases Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/136bf9c3-2f4f-449b-9fff-001332c8371c * refactor: improve pipeline-phases clean code and folder structure - Extract synthesizeWildcardImportBindings to wildcard-synthesis.ts - Extract extractORMQueriesInline to orm-extraction.ts - Create shared constants.ts for AST_CACHE_CAP - Fix inline type import in orm.ts (use proper top-level import) - Add comprehensive JSDoc to getPhaseOutput explaining type safety - Move isDev to module level in cross-file.ts (consistency) - Improve module-level documentation across files - Organize barrel exports in index.ts with section comments Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2bd6d4aa-6271-4009-8dd2-332ea8ec73ab Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * address review feedback: fix circular dep, allFetchCalls mutation, progress bugs, remove DAG naming, extract isDev, fix _item naming, fix O(n²) line calc Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/6cf53c9b-d55d-4c6f-bf3d-7bfb82d512b6 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * improve JSDoc on lineNumberAtOffset binary search Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/6cf53c9b-d55d-4c6f-bf3d-7bfb82d512b6 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * address review: filter deps in runner, move totalFiles to ctx, fix cycle JSDoc, centralize isDev, remove DAG naming Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b388424f-b939-4a94-97de-3855f9465564 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix doc consistency in graph-sort.ts module-level and function-level JSDoc Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b388424f-b939-4a94-97de-3855f9465564 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(pipeline): wrap phase errors with phase name and emit terminal error progress event Restores phase diagnostics at CLI/MCP boundary. runPipeline now wraps phase.execute() in try/catch and rethrows with 'Phase <name> failed: ...' preserving the original via { cause }. Also emits a terminal { phase: 'error' } progress event so subscribers see the failure before the rejection propagates. Handler errors during error reporting are swallowed to keep the original cause authoritative. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U1) * fix(pipeline): move bindingAccumulator dispose into crossFile try/finally; make single-use crossFile.execute() now wraps its body in try/finally so the accumulator is released on both the happy path and when runCrossFileBindingPropagation throws. Dev-mode telemetry stays inside the try block before dispose (all three counters return 0 after dispose clears internal maps). BindingAccumulator becomes single-use: appendFile after dispose now throws 'BindingAccumulator: use after dispose' instead of silently re-animating via the old _disposed auto-clear. Docs updated; the only production construction site (parse-impl) always creates a fresh instance per run, so no caller relied on the re-use contract. Residual risk documented in crossFile module JSDoc: a future phase inserted between parse and crossFile that throws would still leak the accumulator. Any such phase must manage accumulator lifetime explicitly. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U2) * docs(pipeline): explain why importCtx teardown is safe before crossFile Investigation (plan U3) confirms: `importCtx` (ImportResolutionContext) is a scratch workspace with no downstream consumer after parse. `resolutionContext` (returned to crossFile) is a distinct object that owns importMap / namedImportMap / packageMap / moduleAliasMap / model, and never closes over importCtx. cross-file-impl consumes only that ctx via processCalls. The two confusingly-similar "context" names were the root of the adversarial reviewer's concern — comment locks in the invariant so the next reader sees it. No behavioral change. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U3) * refactor(pipeline): remove ctx.totalFiles side-channel; promote to ParseOutput totalFiles was a hidden mutable field on PipelineContext written by parse and read by mro/communities/processes — five reviewers flagged this as a violation of the immutable-context invariant. Removed from PipelineContext, which is now fully readonly, and made the implicit temporal dep explicit: mro/communities/processes now declare 'parse' as a dep and read totalFiles via getPhaseOutput<ParseOutput>(...). No behavior change. Topo-sort unchanged because parse was already a transitive dep through crossFile. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U4) * feat(method-extractor): runtime staticOwnerTypes guard at factory chokepoint createMethodExtractor now rejects MethodExtractionConfigs that list companion_object / singleton_class / object_declaration in typeDeclarationNodes but omit the matching entry from staticOwnerTypes. Fails loudly at provider construction time instead of producing silent isStatic=false on the 50000th file analyzed. Opt-out convention preserved: an explicit `new Set()` (empty Set) signals intentional exclusion and passes the guard (memory obs #30588). All 13 existing language configs pass the guard; the new negative test fails without it. Test-first. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U5) * fix(pipeline): wrap sequential-fallback in try/finally so cleanup survives throws The sequential-fallback block in runChunkedParseAndResolve now runs inside a try/finally that guarantees astCache.clear(), accumulator finalize, and enrichExportedTypeMap execute even if readFileContents or processCalls throws mid-fallback. Cleanup failures are caught inside the finally so they can't mask the original error. Accumulator disposal ownership remains with crossFile (U2) — U6 only adds astCache cleanup and preserves finalize ordering on the error path. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U6) * test(pipeline): direct unit coverage for wildcard-synthesis and cross-file-impl Both modules previously had zero direct unit coverage — branches were exercised only through integration tests' happy paths. wildcard-synthesis.test.ts covers: Go graph-IMPORTS fallback, Python moduleAliasMap build, MAX_SYNTHETIC_BINDINGS_PER_FILE cap, dedup against existing namedImportMap entries, and empty-exportedSymbols early return. cross-file-impl.test.ts covers: gapRatio below threshold no-op, MAX_CROSS_FILE_REPROCESS cap, graph-only exportedTypeMap fallback, and empty namedImportMap short-circuit. Tests assert current behavior — any future regression flips them. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U7) * test(pipeline): golden-file graph-parity regression guard on mini-repo fixture Pins the current post-P1/P2 graph output (57 symbols, 92 relationships, 4 processes, deterministic edge digest) so future silent refactors cannot drift behavior unnoticed. If any count changes or any edge rewires, the test fails with a readable diff listing what changed and a copy-pasteable UPDATE_GOLDEN=1 regen command. Edge digest keyed by symbolic (label, name, filePath) triples rather than raw generateId output — stays meaningful across id-encoding refactors while still catching real semantic rewiring. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U8) * fix(pipeline): minimal cycle reporting + resolveEnclosingOwner loop safeguards U9: runner cycle detection now reports only the SCC members via DFS back-edge trace ('Cycle detected: A -> B -> C -> A') rather than everything with inDegree > 0 (which mixed cycle members with blocked dependents). Also emits the 'error' progress event for graph- validation failures, symmetric with U1's runtime-error path. U16: findEnclosingClassInfo now defends against language-provider hooks that return non-container nodes — visitedContainers Set breaks repeat-visit loops, MAX_ENCLOSING_WALK_ITERATIONS is belt-and-braces. Documented the hook contract invariant so future provider authors know the walk-continues-upward expectation. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U9, U16) * refactor(pipeline): type hygiene, dead code cleanup, shared allPathSet, graph-sort naming Bundles plan units U10, U11, U12, U14, U15: U10 — Type hygiene: readonly ParseOutput arrays (allExtractedRoutes, allDecoratorRoutes, allToolDefs, allORMQueries, allPaths); removed redundant 'as string[] | undefined' cast in routes.ts and 'as URL' in parse-impl.ts; WorkerPool is now 'import type'. Readonly contract propagated into processORMQueries (only iterates). U11 — Dead code & shims: deleted constants.ts shim (AST_CACHE_CAP inlined into its sole real consumer cross-file-impl.ts; isDev consumers now import directly from ../utils/env.js). Removed internal utility re-exports from pipeline-phases/index.ts (no external consumers). Removed topologicalLevelSort re-export from pipeline.ts; updated topological-sort.test.ts to import from the canonical utils/graph-sort.js. Stripped 'Phase 3+4:' stale JSDoc from parse-impl.ts. U12 — Perf: StructureOutput now carries allPathSet (ReadonlySet<string>) built once; cobol, markdown, and cross-file-impl consume the shared set instead of allocating their own. Parse forwards it via ParseOutput.allPathSet; processCobol/processMarkdown widened to ReadonlySet<string>. U14 — graph-sort.ts: renamed local 'inDegree' to 'pendingImportsPerFile' with expanded JSDoc explaining the reverse- graph Kahn's formulation and warning future maintainers not to 'correct' it to standard in-degree semantics. Added self-edge test. U15 — Unconditional worker-fallback logging: removed isDev guard on the worker-pool-creation-failure console.warn so operators can diagnose perf degradations in production. No behavior change. U8 golden-file test confirms pipeline output is byte-identical. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U10, U11, U12, U14, U15) * docs: fix ARCHITECTURE.md table integrity; bump AGENTS.md/CLAUDE.md to 1.3.0 U13 — documentation fixes: ARCHITECTURE.md: the prior insertion of the 'Pipeline Phase DAG' section orphaned 7 rows from the 'Where to change what' header. Moved those 7 rows back up under their header so the table reads contiguously; DAG section now follows the completed table. AGENTS.md + CLAUDE.md: bumped version 1.2.0 -> 1.3.0, updated Last reviewed to 2026-04-13, added matching Changelog row documenting the GitNexus index stats refresh after the DAG refactor. Stat bumps (symbols/relationships/execution flows) that were sitting uncommitted in the working tree are now landed under a proper changelog entry per each file's own documented schema. Plan: docs/plans/2026-04-13-001-fix-pipeline-dag-refactor-review-findings-plan.md (U13) * refactor(pipeline): drop spurious parse deps, true-readonly ParseOutput.exportedTypeMap, skip redundant wildcard synth - mro/communities/processes: switch redundant `parse` dep to `structure` — totalFiles originates in structure, so depending on parse for it was a spurious data dep that obscured the real DAG. - ParseOutput.exportedTypeMap: typed as truly ReadonlyMap<...,ReadonlyMap>>; graph→exports enrichment moved into parse-impl so the snapshot is fully populated at parse return. crossFile builds its own local mutable working copy for per-file re-resolution writes — no cast at the boundary. - parse-impl: hasSynthesized flag guards the unconditional final synthesizeWildcardImportBindings call when per-chunk/fallback synthesis already ran (graph-global + idempotent across chunks). - cross-file-impl: documented the intentional `phase: 'parsing'` progress label so telemetry bucketing stays consistent with the parse phase. - cross-file-impl test: replaced the now-moved fallback-enrichment assertion with a stronger one — crossFile must not mutate the parse-supplied map. Addresses PR #809 review pass 5 carry-overs. --------- 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> |
||
|
|
6388113e10
|
fix: prevent stack overflow and memory exhaustion on large repo analysis (#814)
* Initial plan * fix: prevent stack overflow and memory issues on large repo analysis - Convert c3Linearize from recursive to iterative (explicit work stack) to handle deep class hierarchies without stack overflow - Replace push(...arr) spread patterns with safe loops in parse-worker.ts and lbug-adapter.ts to prevent stack overflow on large arrays - Stream relationship CSV lines directly to per-pair temp files in lbug-adapter.ts instead of accumulating millions of lines in memory - Add test for deep 500-level inheritance chain Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9cb2eed2-adc7-4fa4-9216-e7ac3facb9b5 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: add stack size flag and enhanced error messages for large repos - Auto-set --stack-size=4096 alongside --max-old-space-size in analyze command to prevent stack overflow on deep class hierarchies - Add helpful error guidance for known large-repo failure modes (stack overflow, heap OOM, Map size limits) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9cb2eed2-adc7-4fa4-9216-e7ac3facb9b5 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address code review feedback - Add error handling for write stream close in lbug-adapter.ts - Handle backpressure when writing relationship CSV lines to disk - Clarify ENTER/MERGE phase transition comment in resolve.ts - Fix inconsistent stack size in error message (4096 not 8192) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9cb2eed2-adc7-4fa4-9216-e7ac3facb9b5 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address PR review — resource leak, redundant guard, Set, test depth - Fix write-stream resource leak on readline error by destroying all open WriteStreams before rejecting (lbug-adapter.ts) - Switch failedPairCsvPaths from array to Set for O(1) lookup - Remove redundant MERGE-phase empty-parents guard in resolve.ts (unreachable — ENTER phase already handles that case) - Increase deep inheritance test DEPTH from 500 to 2000 for reliable regression coverage across platforms Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/cf1f3e22-3864-454a-a3a5-2bded9ebfdba Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * style: fix prettier formatting in lbug-adapter.ts Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b5ca33c4-bb03-402f-a206-21ea7e1e310e Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore: revert unintended package.json/lock changes Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b5ca33c4-bb03-402f-a206-21ea7e1e310e Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: strip NODE_OPTIONS in skip-git-cli test child processes Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fc59cd11-348b-4e22-b9ea-98787300de48 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: don't put --stack-size in NODE_OPTIONS (rejected by Node 24) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fc59cd11-348b-4e22-b9ea-98787300de48 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: pass --stack-size as CLI arg only, not in NODE_OPTIONS (Node 24 compat) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fc59cd11-348b-4e22-b9ea-98787300de48 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> |
||
|
|
a6421b3b1b
|
[dart] Add call patterns for await, cascade, lambda, and widget-tree contexts (#801)
* feat(dart): add call patterns for await, cascade, lambda, and widget-tree contexts * fix(dart): address review feedback — await member-chain, cascade comment, static_final comment, add to query-compilation smoke test * test(dart): add integration tests for await and widget-tree call patterns * style: apply prettier formatting to dart integration tests --------- Co-authored-by: arkh <local@localhost> |
||
|
|
4d4756fe86
|
feat(group): extractor expansion + manifest extractor (2/4 of #606 split) (#796)
* feat(group): extractor expansion + manifest extractor Part 2 of 4 in the split of #606 (ticket: #792). Follows #795 (bridge.lbug storage foundation, already merged), but this PR has no code-level dependency on #795 — it only imports types and the ContractExtractor interface that existed on upstream main before either PR. It could have been reviewed in parallel with #795. ## What changed Expands the 3 existing contract extractors with substantially more language/framework coverage, and adds a new `manifest-extractor` that resolves `group.yaml`-declared cross-links against the per-repo graph via exact-name lookups. ### New file (228 LOC) - `gitnexus/src/core/group/extractors/manifest-extractor.ts` — exact graph lookup for `group.yaml`-declared cross-links. HTTP paths are canonicalized before Route.name matching; gRPC is resolved by service/method name (NO `.proto`-filename fallback); topic and lib use exact-name match. Falls back to a synthetic `manifest::<repo>::<contractId>` uid when the graph has no matching symbol, so cross-impact traversal still has a stable anchor for the contract. ### Modified extractors (+958 LOC prod) - `extractors/grpc-extractor.ts` (+522) — `.proto` parser with comment and string-literal sanitization (braces inside strings no longer truncate service bodies); package/service/method canonical IDs; server/client detection across Go (`grpc.NewServer`, `RegisterXxxServer`, `XxxGrpc.XxxImplBase`), Java (`@GrpcService`, `BlockingStub`), Python (`servicer_to_server`, `XxxStub`), and TypeScript/Node (`@GrpcMethod`, `ClientGrpc`, `loadPackageDefinition`). - `extractors/http-route-extractor.ts` (+174) — Go gin/echo/stdlib `HandleFunc`, NestJS `@Controller`+`@Get`/etc, Python FastAPI decorators, Java Spring `@RequestMapping`/`@GetMapping`, restTemplate / WebClient / OkHttp consumers. - `extractors/topic-extractor.ts` (+98) — sarama `ProducerMessage{}` struct literal detection (replaces a constructor-anchored regex that missed topics inside producer loops), kafka-go Writer/Reader, Python NATS (`await nc.subscribe`/`await nc.publish`), JetStream helpers. ### Modified and new tests (+1264 LOC) - `grpc-extractor.test.ts` (+539) — full coverage of the new proto parser (strings-with-braces regression, comments-with-braces regression), per-language server/client detection - `http-route-extractor.test.ts` (+240) — per-framework route extraction + normalization edge cases - `topic-extractor.test.ts` (+177) — the sarama in-loop regression, JetStream, Python NATS, kafka-go Writer/Reader - `manifest-extractor.test.ts` (+308 NEW) — HTTP path normalization, gRPC exact lookup with proto-fallback regression, lib and topic exact matching, synthetic-uid fallback behavior ### Self-review fixes folded in Carried forward from the #606 self-review (commit `d15b8cb`): - **HIGH #1** — `manifest-extractor.resolveSymbol` was too fuzzy. Previously used `CONTAINS` on route/name fields plus an unconditional `filePath ENDS WITH '.proto'` fallback for gRPC. Consequences: `/orders` matched `/suborders`, and any repo with any `.proto` file returned a random proto symbol for a gRPC manifest entry. Replaced with exact equality + deterministic `ORDER BY` + synthetic-uid fallback for unresolved manifests. Regression tests included. - **MED #3** — gRPC proto parser brace-depth counting now sanitizes strings and comments first (`stripProtoCommentsAndStrings`). A valid proto with `option deprecated_reason = "use NewService { instead"` used to have its service body closed early by the `"{"` inside the literal, silently dropping methods after the offending string. Regression tests for both string-with-brace and comment-with-brace cases. - **MED #4** — sarama Kafka regex changed from `sarama.NewSyncProducer[\s\S]{0,300}?Topic:` (anchored on constructor, caught only first topic in a loop) to `sarama.ProducerMessage{...Topic:}` (matches every struct literal directly). Regression test with a for-loop that constructs multiple `ProducerMessage`s. - **MED #7** — `manifest-extractor.resolveSymbol` no longer has a silent `catch { /* fall through */ }`. Errors from the graph executor are logged via `console.warn` with link type, contract name, repo key, and error message before falling through to the synthetic-uid path. ## Why Reviewer focus here is pure regex / parser correctness — no storage, no Cypher queries, no algorithmic changes to the cross-link algorithm. Separating this from the bridge foundation PR (#795) meant reviewers could stay in a single mental mode (parsing logic) instead of context-switching between DDL, Cypher, and regex. ## How to verify - `cd gitnexus && npx tsc --noEmit` - `cd gitnexus && npx vitest run test/unit/group/grpc-extractor.test.ts --pool=forks` - `cd gitnexus && npx vitest run test/unit/group/http-route-extractor.test.ts --pool=forks` - `cd gitnexus && npx vitest run test/unit/group/topic-extractor.test.ts --pool=forks` - `cd gitnexus && npx vitest run test/unit/group/manifest-extractor.test.ts --pool=forks` Local pre-push: typecheck clean, all 99 extractor unit tests pass (grpc 43, http 18, topic 30, manifest 8). ## Risk / rollback **Low.** Extractors have no user-facing surface in this PR — they produce `ExtractedContract[]` that is consumed by `sync.ts` in the next split (#793). No existing behavior changes for users who don't run a `group sync`. Rollback = `git revert` of the merge commit; the modifications to `grpc-extractor.ts` / `http-route-extractor.ts` / `topic-extractor.ts` revert to the pre-PR versions that still work (they're subsets of the new functionality). ## Scope discipline (per GUARDRAILS.md) - Only the 8 files above are touched; no drive-by refactors - No CI/release/security config changes - No secrets or machine-specific paths - Content lifted from #606 (CI 11/11 green on `d15b8cb`) ## Dependencies - **Base:** `main` (upstream already includes #795 as `1ff324c`) - **Blocks:** sync pipeline (#793) and the cross-impact feature (#794) - **Tracker issue:** #792 - **Parent PR:** #606 Co-authored-by: Claude <noreply@anthropic.com> * refactor(group): migrate topic-extractor from regex to tree-sitter queries Addresses @magyargergo's feedback on #796 that regex-based lookups should use tree-sitter nodes instead, and that the top-level extractors must NOT carry language dependencies. This is phase 1 of a multi-step migration — topic-extractor first because its patterns are the most uniform (16 "call/annotation with first-arg string literal" variants), which makes it a clean proof of the approach before grpc-extractor and http-route-extractor get the same treatment. ## Architecture: language-agnostic orchestrator + per-language plugins The top-level extractor is a thin orchestrator that never imports a tree-sitter grammar or a query string. Per-language knowledge lives in a new `topic-patterns/` folder with one file per language plus a registry that maps file extensions to compiled plugins: ``` src/core/group/extractors/ ├── tree-sitter-scanner.ts # shared, language-agnostic scanning utilities ├── topic-extractor.ts # thin orchestrator (no grammar imports) └── topic-patterns/ ├── types.ts # TopicMeta, Broker ├── index.ts # registry: extension → compiled provider ├── java.ts # tree-sitter-java + JAVA_TOPIC_PROVIDER ├── go.ts # tree-sitter-go + GO_TOPIC_PROVIDER ├── python.ts # tree-sitter-python + PYTHON_TOPIC_PROVIDER └── node.ts # tree-sitter-javascript + tree-sitter-typescript # → JAVASCRIPT_/TYPESCRIPT_/TSX_TOPIC_PROVIDER ``` **Shared scanner (`tree-sitter-scanner.ts`)** — defines `PatternSpec<TMeta>`, `LanguagePatterns<TMeta>`, `CompiledPatterns<TMeta>` and the `scanFile(parser, plugin, content)` helper. Plugins compile their queries eagerly at module load via `compilePatterns()`, so a broken pattern fails loudly at import time instead of silently at scan time. `unquoteLiteral()` handles single/double/template quotes, Python triple-quoted strings, and Go raw backtick strings. **Per-language plugins** own: - the tree-sitter grammar import (this is the ONLY place in `src/core/group/` where tree-sitter grammars are imported), - the query S-expressions, - the `TopicMeta` payload (role, broker, confidence, symbolName) that the orchestrator receives back on every match. Each plugin uses a `@value` capture name to bind the topic literal node. The JavaScript and TypeScript grammars share AST node names for every construct we query, so `node.ts` defines the pattern sources once and compiles them against `JavaScript`, `TypeScript.typescript`, and `TypeScript.tsx` — exporting three providers because `Parser.Query` objects are NOT portable across grammar instances. **Registry (`topic-patterns/index.ts`)** — maps `.java` → Java provider, `.go` → Go, `.py` → Python, `.js`/`.jsx` → JS, `.ts` → TS, `.tsx` → TSX. Also exports `TOPIC_SCAN_GLOB` so adding a new language is a single file-level edit (drop `topic-patterns/<lang>.ts`, import + register it here — zero edits required in `topic-extractor.ts`). **Orchestrator (`topic-extractor.ts`)** — ~110 lines, no grammar or query imports. Per file: `getProviderForFile(rel)` → `scanFile(parser, provider, content)` → `unquoteLiteral(valueText)` → `makeContract(...)`. Reuses one `Parser` instance across files; the scanner calls `setLanguage` per plugin. ## Why this is better than regex 1. **Comments and strings are respected for free.** The old regex would match `// kafkaTemplate.send("fake.topic")` as a real producer; tree-sitter never visits comments or string literals as code nodes, so false positives from commented-out code are eliminated. 2. **Struct/object literal patterns are structural, not textual.** `sarama.ProducerMessage{Topic: "..."}` no longer needs a 300-char lookahead (which was a known cross-match bug partly mitigated by a loop regression test in the self-review). The new query matches a specific `composite_literal` with a specific `qualified_type` and `keyed_element` — exactly one struct literal per match. 3. **No order-of-operations fragility.** Regex for `channel.publish` vs `channel.consume` was independent and file-wide; the AST scopes matches to the specific `call_expression`. 4. **Language-agnostic extension.** Adding Ruby, Rust, or C# topic detection later means dropping one file in `topic-patterns/` — no changes to shared scanner or orchestrator, and no tree-sitter imports leak into top-level code. ## Per-file fault tolerance - Malformed files that tree-sitter can't parse are silently skipped (`parser.parse` is wrapped by `scanFile`). The ingestion pipeline already logs unparseable files at index time. - A syntactically invalid query is caught at `compilePatterns` time, not scan time — broken plugins fail loudly at import. - Per-pattern `matches()` failures are swallowed so one broken query in a plugin doesn't block the rest. ## Tests All 30 existing `topic-extractor.test.ts` tests pass **without any changes to the test file** — they were written as input/output contract tests (given this source file, expect these `ExtractedContract` objects) and that contract is unchanged. Regression coverage includes: - Kafka: Java `@KafkaListener` + `kafkaTemplate.send`; Node `producer.send` + `consumer.subscribe`; Go sarama producer/consumer (sync and async); kafka-go Writer/Reader; Python `KafkaConsumer` + `producer.send/produce` - RabbitMQ: Java `@RabbitListener` + `rabbitTemplate.convertAndSend`; Node `channel.consume/publish/sendToQueue`; Python `basic_consume/ basic_publish` with keyword args - NATS: Go and Node `nc.Subscribe/Publish`; Go and Node JetStream `js.Subscribe/Publish`; Python `await nc.subscribe/publish` Including the regression test for the sarama `ProducerMessage` in-loop case — the AST-based query captures every literal in the file independently, not just the first one after `NewSyncProducer`. ## Neighbor regression check - `topic-extractor.test.ts` — 30/30 pass (rewritten extractor) - `http-route-extractor.test.ts` — 18/18 pass (untouched) - `grpc-extractor.test.ts` — 43/43 pass (untouched) - `manifest-extractor.test.ts` — 8/8 pass (untouched) - Full `npx tsc --noEmit` clean ## Scope discipline (per GUARDRAILS.md) - Only files under `src/core/group/extractors/` are touched; no changes to other extractors, tests, MCP surface, or pipeline.ts. - No CI/release/security config changes, no secrets. - New tree-sitter imports all reference grammars that are already installed as dependencies (`tree-sitter`, `tree-sitter-javascript`, `tree-sitter-typescript`, `tree-sitter-python`, `tree-sitter-java`, `tree-sitter-go` — all in `package.json` for the existing pipeline). ## Phase 2 / phase 3 plan - **Phase 2 (next commit):** rewrite `http-route-extractor.ts` Strategy B (regex fallback) on the same plugin pattern. Graph-assisted Strategy A stays as-is (already uses pipeline-built tree-sitter data via `HANDLES_ROUTE` Cypher queries). - **Phase 3 (commit after):** rewrite `grpc-extractor.ts` for Java / Go / Python / TypeScript detection. `.proto` files are the one outstanding question — there is no `tree-sitter-proto` grammar installed; the in-tree string-sanitizing parser stays as a pragmatic exception with a comment, alternative being to add `tree-sitter-proto` as a dep (open for the maintainer). Co-authored-by: Claude <noreply@anthropic.com> * refactor(group): migrate http-route-extractor Strategy B to tree-sitter plugins Phase 2 of the extractor refactor requested by @magyargergo on #796. Same architecture as the phase 1 topic-extractor rewrite: a thin, language-agnostic orchestrator plus per-language plugins that own tree-sitter grammars and query sources. The top-level extractor file no longer imports any tree-sitter grammar or query string. ## Architecture ``` src/core/group/extractors/ ├── tree-sitter-scanner.ts # shared, language-agnostic primitives ├── http-route-extractor.ts # thin orchestrator (no grammar imports) └── http-patterns/ ├── types.ts # HttpDetection, HttpLanguagePlugin, HttpRole ├── index.ts # registry: ext → plugin + HTTP_SCAN_GLOB ├── java.ts # tree-sitter-java: Spring + RestTemplate/WebClient/OkHttp ├── go.ts # tree-sitter-go: gin/echo/HandleFunc + http/resty consumers ├── python.ts # tree-sitter-python: FastAPI + requests ├── php.ts # tree-sitter-php: Laravel Route::get/... └── node.ts # tree-sitter-javascript + tree-sitter-typescript: # NestJS controllers, Express, fetch, axios ``` **Shared scanner (`tree-sitter-scanner.ts`)** — generalised from phase 1: - `ScanMatch<TMeta>.captures` is now a full `CaptureMap` (every named capture the query binds, not just a single `@value`). Topic extractor updated to read `match.captures.value` accordingly. - New `runCompiledPatterns(plugin, tree)` helper lets plugins run multiple query bundles against the same pre-parsed tree. This is needed for HTTP plugins that combine a class-prefix query with a method-route query (Spring, NestJS). - `scanFile` becomes a thin wrapper over `parser.parse + runCompiledPatterns`. **HTTP plugin shape** — unlike topic plugins, HTTP plugins expose a `scan(tree)` function rather than a flat pattern list. This reflects HTTP's more complex extraction: each detection needs method + path + handler name, and framework patterns like Spring `@RequestMapping` / NestJS `@Controller` require cross-referencing a class-level prefix with method-level annotations. Plugins internally use `compilePatterns` + `runCompiledPatterns` and walk the AST to resolve the class/method relationships. **Per-framework coverage:** - **Java (`java.ts`)** - Spring: `@RequestMapping("/api/v2")` class prefix + `@(Get|Post|Put| Delete|Patch)Mapping("/sub")` method routes, joined via the enclosing `class_declaration` node id. - `RestTemplate.getForObject/postForEntity/put/delete/patchForObject` → method derived from API name. - `WebClient.method(HttpMethod.X, "/path")` → method from `HttpMethod.X` capture. - `new Request.Builder().url("/path")` → OkHttp consumer. - **Go (`go.ts`)** - gin / echo / chi frameworks: `\w+.GET("/path", handler)` captures upper-case verb + handler identifier. - `net/http.HandleFunc("/path", handler)` → provider (default GET). - `http.Get/Post/Head` consumer, `http.NewRequest("METHOD", ...)`, resty `client.R().Get/Post/...`. - **Python (`python.ts`)** - `@app.get("/path")` FastAPI decorators. - `requests.get/post/...` and `requests.request("METHOD", "url")`. - **PHP (`php.ts`)** - Laravel `Route::get/post/.../patch('/path', ...)` via `scoped_call_expression`. Uses `PHP.php_only` to match the existing ingestion pipeline's grammar selection. - **Node (`node.ts`) — JS + TS + TSX** - Pattern sources defined once, compiled against three grammar variants (`JavaScript`, `TypeScript.typescript`, `TypeScript.tsx`) because `Parser.Query` objects are not portable across grammars. Exports three plugins sharing the same `scan` logic. - NestJS: `@Controller('prefix')` decorators are siblings of the class in `export_statement` / `program`; `@Get(':id')` decorators are siblings of the method in `class_body`. The plugin walks decorator → next named sibling to find the decorated class / method, then combines the class prefix with the method path. Only emits NestJS detections when the enclosing class has a real `@Controller` decorator — prevents false positives from generic classes that happen to use `@Get` from another library. - Express: `(router|app).<verb>('/path', ...)`. - `fetch(url)` (default GET) + `fetch(url, { method: 'X' })` (uses two queries + a SyntaxNode-id dedupe set so URL literals aren't double-emitted by the options variant). - `axios.get/post/...`. ## Orchestrator changes `http-route-extractor.ts` drops every `scanXxxProviders` / `scanXxxConsumers` regex method and replaces them with a single source-scan loop that delegates to `getPluginForFile(rel).scan(tree)`. The orchestrator still owns: - **Path normalization** (`normalizeHttpPath`, `normalizeConsumerPath`) — language-agnostic string processing shared by both strategies. - **Graph-assisted Strategy A** (`HANDLES_ROUTE` / `FETCHES` / `CONTAINS` Cypher queries) — unchanged in spirit. The only regex helpers it used (`inferMethodFromFileScan`, `pickJavaHandlerName`) are now replaced by a lookup against the plugin's detections for the same file: for each route row, find the detection whose normalized path matches, and pull the HTTP method + handler name from it. - **Per-file parse cache** — the orchestrator parses each relevant file at most once per `extract()` call. Both the graph-assisted enrichment loop and the source-scan fallback share the same `cachedDetections` map, so we never run the plugin twice for the same file. ## Why this is better than the regex version 1. **Comments and strings for free.** The old regex would match `// router.get('/fake')` as a real Express route; tree-sitter never visits string/comment nodes. 2. **Structural controller-prefix.** Spring and NestJS class-prefix joining is now scoped to the enclosing class via `class_declaration` node ids, eliminating file-wide state that broke when a file had multiple controllers. 3. **Precise NestJS disambiguation.** The plugin only emits a NestJS detection when the enclosing class has a real `@Controller` decorator — the old regex would fire on any `@Get(...)` in the file regardless of surrounding context. 4. **Language-agnostic extension.** Adding Ruby / Rust / Kotlin HTTP detection later means dropping one file in `http-patterns/` — no changes to the shared scanner, the orchestrator, or the Strategy A Cypher queries. ## Tests - `http-route-extractor.test.ts` — **18/18 pass** (tests unchanged; they're contract-style input/output tests and the contract shape is unchanged). Covers Spring class prefix, Express, gin/echo, stdlib HandleFunc, NestJS, Laravel, FastAPI for providers and fetch/axios/python-requests/rest-template/webClient/okhttp/go-stdlib/ resty for consumers, plus graph-first Strategy A for both. - `topic-extractor.test.ts` — **30/30 pass** after the `captures.value` API migration. - `grpc-extractor.test.ts` — 43/43 pass (untouched; phase 3). - `manifest-extractor.test.ts` — 8/8 pass (untouched). - `service.test.ts`, `sync.test.ts`, `storage.test.ts` — 41/41 pass. - `npx tsc -p tsconfig.json --noEmit` clean. ## Scope discipline (per GUARDRAILS.md) - Only files under `src/core/group/extractors/` are touched. - No changes to pipeline.ts, MCP surface, ingestion, or tests. - No CI / release / security / secrets changes. - Tree-sitter grammars imported by plugins (`tree-sitter-java`, `tree-sitter-go`, `tree-sitter-python`, `tree-sitter-php`, `tree-sitter-javascript`, `tree-sitter-typescript`) are all already in `package.json` for the existing ingestion pipeline. ## Phase 3 plan - **grpc-extractor** gets the same treatment: plugin-per-language under `grpc-patterns/` for Java / Go / Python / TS detection. `.proto` files remain an open question — no `tree-sitter-proto` grammar is installed, so the in-tree string-sanitizing parser from PR #796's self-review stays as a pragmatic exception unless the maintainer wants us to add `tree-sitter-proto` as a new dep. Co-authored-by: Claude <noreply@anthropic.com> * refactor(group): migrate grpc-extractor source scans to tree-sitter plugins Phase 3 (final) of the extractor refactor requested by @magyargergo on #796. Same architecture as phase 1 (topic) and phase 2 (http): thin language-agnostic orchestrator + per-language plugins that own tree-sitter grammars and query sources. With this commit the top-level extractors under `src/core/group/extractors/` import ZERO tree-sitter grammars and ZERO query strings — every grammar import lives in a `*-patterns/<lang>.ts` plugin file, and the orchestrators go through the registry indirection. ## Architecture ``` src/core/group/extractors/ ├── tree-sitter-scanner.ts # shared primitives (unchanged) ├── grpc-extractor.ts # orchestrator (only `.proto` parser left) └── grpc-patterns/ ├── types.ts # GrpcDetection, GrpcLanguagePlugin, GrpcRole ├── index.ts # registry: ext → plugin + GRPC_SCAN_GLOB ├── go.ts # tree-sitter-go: RegisterXxxServer, Unimplemented, NewXxxClient ├── java.ts # tree-sitter-java: @GrpcService + XxxImplBase + newBlockingStub ├── python.ts # tree-sitter-python: add_XxxServicer_to_server + XxxStub └── node.ts # tree-sitter-javascript + tree-sitter-typescript: # @GrpcMethod, @GrpcClient field type, # .getService<X>('Svc'), new XxxServiceClient, # loadPackageDefinition dynamic constructors ``` ## Per-language coverage **Go (`go.ts`)** - Provider: `\w+.RegisterXxxServer(...)` via `call_expression → selector_expression → field_identifier` + JS regex filter `^Register(\w+)Server$`. - Provider: `pb.UnimplementedXxxServer` embedded in a struct via `struct_type → field_declaration_list → field_declaration → qualified_type → type_identifier` + JS filter. - Consumer: `\w+.NewXxxClient(...)` via the same call_expression query + JS filter `^New(\w+)Client$`. **Java (`java.ts`)** - Provider: `class X extends YyyGrpc.YyyImplBase` — two queries handle the scoped and plain forms. `scoped_type_identifier`'s children are positional (no `scope:`/`name:` fields), so the query matches the two `type_identifier` children by position. - `#match? @inner "ImplBase$"` restricts matches at query time. - Whether the class has `@GrpcService` or not controls only the `source` metadata label — the plugin walks the class_declaration's `modifiers` child in JS to detect the marker_annotation. - Consumer: `YyyGrpc.newStub(ch)` / `newBlockingStub(ch)` via a `method_invocation` query with `#match? @method "^new(Blocking)?Stub$"`, service name extracted via `^(\w+)Grpc$` on the object identifier. **Python (`python.ts`)** - Single call-expression query covers both bare identifier and `obj.method` attribute forms: `(call function: [(identifier) @fn (attribute attribute: (identifier) @fn)])`. - Plugin filters `@fn.text` against two JS regexes: `^add_(\w+)Servicer_to_server$` (provider) and `^(\w+)Stub$` (consumer), with a reserved-names ignore list for the Stub case (Mock / Test / Fake / Stub). **Node — JavaScript + TypeScript + TSX (`node.ts`)** - Pattern sources defined once, compiled three times (one per grammar) because `Parser.Query` objects are not portable across grammars. Exports three `GrpcLanguagePlugin`s sharing the same `scan`. - `@GrpcMethod('Service', 'Method')`: decorator query captures the two string literals. Confidence is hard-coded 0.8 regardless of proto map resolution (matches the original regex version's behaviour). - `@GrpcClient(...) field: XxxServiceClient`: decorator query captures the decorator node, plugin walks up to find the enclosing `public_field_definition` (decorators on fields are CHILDREN of the field definition in tree-sitter-typescript, not siblings) and reads its first `type_annotation → type_identifier`, then runs the `^(\w+Service)Client$` JS filter. - `client.getService<X>('AuthService')`: call-expression query on `member_expression.property = "getService"` + string literal arg. - `new XxxServiceClient(...)`: `new_expression` with a bare identifier constructor, filtered by `^(\w+Service)Client$` so generic `new AuthClient(...)` (missing the `Service` infix) does NOT falsely register as a consumer. Preserves the regression test `test_extract_ts_non_service_client_constructor_is_ignored`. - `loadPackageDefinition` dynamic loader: gated on `tree.rootNode.text.includes('loadPackageDefinition')`. When set, `new foo.bar.Xxx(...)` qualified constructors with a capitalised property name register as consumers. ## Orchestrator changes `grpc-extractor.ts` loses every `scanGoProviders` / `scanJavaProviders` / ... helper and replaces them with a single source-scan loop that: 1. Parses each file with the plugin's grammar (one shared `Parser` instance across all files, `setLanguage` called per plugin). 2. Calls `plugin.scan(tree)` to get `GrpcDetection[]`. 3. Converts each detection to an `ExtractedContract` via the private `detectionToContract` helper, which: - Looks the short service name up in the proto map (filled by the `.proto` parser). - Picks confidence = `confidenceWithProto` if resolved, else `confidenceWithoutProto`. - Builds a method-level contract id (`grpc::pkg.Svc/Method`) when the detection carries a `methodName` (TS `@GrpcMethod` only), otherwise a service-level id (`grpc::pkg.Svc/*`). Everything else — the `.proto` parser, `buildProtoContext`, `buildProtoMap`, `resolveProtoConflict`, `serviceContractId`, `stripProtoCommentsAndStrings`, `extractServiceBlocks`, the dedupe function — stays exactly as before. The `.proto` parser is kept as a pragmatic exception to the "no regex in extractors" rule because no `tree-sitter-proto` grammar is installed in the repo; a comment at the top of the file explains this and flags the maintainer option of adding `tree-sitter-proto` as a dependency. ## Why this is better than the regex version 1. **Comments and strings are respected for free.** Matched node types are only code constructs, never text inside comments or string literals. 2. **No false positives on partial names.** The old `(\w+?)Grpc`-style regexes would cross-match unrelated identifiers; structural queries restrict matches to the exact AST shape (`scoped_type_identifier → type_identifier` pairs, `method_invocation → identifier` etc.). 3. **NestJS `@GrpcClient` is structural, not regex-based.** The old regex required a specific textual layout (`@GrpcClient(...) private readonly foo!: XxxServiceClient`); the plugin now walks the AST, so modifier order / optional modifiers / multi-line formatting don't break it. 4. **Language-agnostic extension.** Adding Kotlin / Rust / C# gRPC detection later is a one-file edit in `grpc-patterns/index.ts` — no touches to the shared scanner, the orchestrator, or the proto parser. ## Tests - `grpc-extractor.test.ts` — **43/43 pass** (tests unchanged; the contract shape is identical). Covers .proto parsing (including the brace-inside-string regression), Go provider/consumer, Java @GrpcService / plain ImplBase provider + newBlockingStub consumer, Python servicer + stub, TS @GrpcMethod + @GrpcClient + .getService + new XxxServiceClient + loadPackageDefinition + the `AuthClient` vs `AuthServiceClient` discrimination, dedupe across multiple patterns in one file, proto-aware confidence, and the inherited-package resolution for split proto definitions. - `topic-extractor.test.ts` — 30/30 pass. - `http-route-extractor.test.ts` — 18/18 pass. - `manifest-extractor.test.ts` — 8/8 pass. - `service.test.ts`, `sync.test.ts`, `storage.test.ts` — 41/41 pass. - `npx tsc -p tsconfig.json --noEmit` clean. ## Scope discipline (per GUARDRAILS.md) - Only files under `src/core/group/extractors/` are touched. - No pipeline.ts, MCP surface, ingestion, CI / release / security, or test changes. - New tree-sitter grammar imports (`tree-sitter-go`, `tree-sitter-java`, `tree-sitter-python`, `tree-sitter-javascript`, `tree-sitter-typescript`) are all already installed for the ingestion pipeline. ## End of phase series This commit completes the three-phase extractor refactor: - **Phase 1** (`ea06d11`): topic-extractor → `topic-patterns/` - **Phase 2** (`b6015f6`): http-route-extractor → `http-patterns/` - **Phase 3** (this commit): grpc-extractor → `grpc-patterns/` Every remaining regex-based extractor helper under the `src/core/group/ extractors/` directory is either (a) language-agnostic string processing (path normalization, dedupe keys) or (b) the `.proto` parser, which is documented as an explicit exception. Co-authored-by: Claude <noreply@anthropic.com> * feat(group): add tree-sitter-proto for .proto file parsing Addresses @magyargergo's suggestion on #796 to replace the manual string-sanitizing .proto parser with a tree-sitter grammar. - **Vendored `tree-sitter-proto`** in `vendor/tree-sitter-proto/`. Grammar source from [coder3101/tree-sitter-proto](https://github.com/coder3101/tree-sitter-proto) (latest `grammar.js`), parser.c regenerated with `tree-sitter-cli 0.24` to produce ABI version 14 — compatible with the project's `tree-sitter 0.25` runtime (which supports ABI ≤ 14). Added as `optionalDependency` with `file:./vendor/tree-sitter-proto`. - **New `grpc-patterns/proto.ts` plugin** — uses the same `compilePatterns` + `runCompiledPatterns` infrastructure as every other plugin. Two queries: - `(package (full_ident) @pkg)` — package declaration - `(service (service_name) @service_name (rpc (rpc_name) @rpc_name))` — one match per (service, rpc) pair - **Graceful fallback** — `tree-sitter-proto` is an optional dependency. If it fails to install (platform incompatibility) or fails the runtime smoke-test (`setLanguage` + `parse` on a trivial proto), `PROTO_GRPC_PLUGIN` stays `null` and the orchestrator uses the existing manual parser. The smoke-test catches the `SyntaxNode` TDZ error that occurs in vitest's fork-based test runner. - **Orchestrator updated** — when `hasProtoPlugin` is true, `.proto` files are handled by the plugin loop (they're included in `GRPC_SCAN_GLOB`), and the manual `parseProtoFile` loop is skipped. `buildProtoContext` still runs to build the proto map for cross-referencing source-file detections. 1. **No manual comment/string stripping.** The old parser needed `stripProtoCommentsAndStrings` (110 lines) to avoid counting braces inside comments and string literals. tree-sitter handles this natively. 2. **No brace-depth tracking.** `extractServiceBlocks` used a manual depth counter to find service boundaries. tree-sitter's AST gives us `service` → `service_name` + `rpc` → `rpc_name` directly. 3. **Performance.** tree-sitter's C-based parser is faster than character-by-character JS scanning + regex on large proto files. - `grpc-extractor.test.ts` — **43/43 pass** (unchanged) - All other extractor tests — 99/99 pass - `npx tsc -p tsconfig.json --noEmit` clean Co-authored-by: Claude <noreply@anthropic.com> * chore: add .gitignore for vendored tree-sitter-proto build artifacts https://claude.ai/code/session_01SFUCxgKMMQ8EgRHYw91xPU * fix: correct .gitignore paths for vendored tree-sitter-proto Patterns should be relative to the .gitignore file's directory. https://claude.ai/code/session_01SFUCxgKMMQ8EgRHYw91xPU * refactor(group): address Copilot review feedback on #796 Six fixes suggested by the Copilot AI review: 1. **`normalizeHttpPath` root-path edge case** — stripping trailing slashes on the input `/` produced an empty string, yielding malformed contract ids like `http::GET::`. Now preserves `/` for the root handler/fetch case. 2. **Dedupe `scanFiles` call** — `extract()` was globbing the source-scan file list twice (once for the provider fallback, once for the consumer fallback). Moved to a single lazy call that memoizes the result for the rest of the method. 3. **HTTP `scanFiles` now ignores `**/vendor/**`** — every other extractor's glob already ignored vendored sources; the HTTP one didn't. Fixed for consistency. 4. **`loadPackageDefinition` check is now structural** — was calling `tree.rootNode.text.includes('loadPackageDefinition')` which forces materialization of the entire file text from the parse tree (expensive on large files). Replaced with a dedicated compiled query on `(call_expression function: [(identifier) | (member_expression)])` so the check stays in the AST domain. 5. **`grpc-extractor.ts` header docstring updated** — still claimed ".proto parsing is not tree-sitter-based because no grammar is installed". Now describes the actual behaviour: tree-sitter when `tree-sitter-proto` is available (optionalDependency), manual fallback otherwise. 6. **Eliminated the double proto file parse on the fallback path** — `buildProtoContext` already globs + parses every `.proto` file to build `servicesByName`. On the `!hasProtoPlugin` branch the extractor was globbing + parsing again via the now-removed `parseProtoFile` helper. The fallback branch now iterates the map that `buildProtoContext` already produced to emit provider contracts directly — single pass per proto file. ## Tests - `topic-extractor.test.ts` — 30/30 pass - `http-route-extractor.test.ts` — 18/18 pass - `grpc-extractor.test.ts` — 43/43 pass - `manifest-extractor.test.ts` — 8/8 pass - `npx tsc -p tsconfig.json --noEmit` clean Co-authored-by: Claude <noreply@anthropic.com> * refactor(group): address Claude review feedback (bugs + dedup + hygiene) on #796 Follows up `2f28bfc` with the remaining items from the Claude AI review: ## Bugs **Bug 2 — Label-unaware Cypher queries in `resolveSymbol`.** The manifest-extractor's lookup queries were `MATCH (n) WHERE n.name = $x` with no label filter, so a topic/service/package name could silently match any node type (File, Variable, Import, Folder, …). Added label filters: - `topic` → `(n:Function|Method|Class|Interface)` (topics are best-effort symbol-name matches against listener/publisher symbols) - `grpc` method → `(n:Function|Method)` - `grpc` service → `(n:Class|Interface)` - `lib` → `(n:Package|Module)` All 8 manifest-extractor tests still pass (mock executor is label-agnostic, but the production LadybugDB graph now gets correctly scoped queries). **Bug 8 — Tautological `!handlerName` condition.** `http-route-extractor.ts:extractProvidersGraph` had `let handlerName = null; if (!method || !handlerName) { ... }` — the `!handlerName` clause was always true since there was no intervening assignment. Simplified to always run the plugin-scan lookup (we need the handler name even when `methodFromRouteReason` already resolved the method). ## Clean code / dedup **Design 7 — `readSafe` was copy-pasted in all three orchestrators.** Extracted to `extractors/fs-utils.ts` as the single source of truth for the path-traversal guard. Dropped the three local copies and the now-unused `fs`/`path` imports from topic-extractor. **Style 10 — Language-specific `_test.go` skip in the topic orchestrator.** Was `if (rel.endsWith('_test.go')) continue;` inside the language- agnostic extraction loop. Pushed into the glob's ignore list (`'**/*_test.go'`) alongside the existing `node_modules`, `vendor`, `dist`, `build` entries, with a comment explaining that other languages' test file conventions either live in separate directories (Python `tests/`, Java `src/test/`) or are already covered by the existing ignores. ## Already addressed in `2f28bfc` (mentioned again in Claude review) - Bug 3: `normalizeHttpPath('/')` returns `''` — fixed - Bug 4: double glob + double parse of `.proto` — fixed - Bug 5: `scanFiles` called twice in HTTP — fixed - Bug 6: missing `**/vendor/**` in HTTP glob — fixed - Design 9 partially: `tree.rootNode.text.includes('loadPackageDefinition')` replaced with a dedicated structural query ## Deferred - Bug 1 (`http::*::path` vs `http::GET::path` matching) — out of scope; sync.ts matching logic lands in #793, manifest extractor already emits correct synthetic uids for unresolved HTTP contracts. - Design 9 full (change plugin `scan(tree)` → `scan(tree, source)`) — the only real use case (`loadPackageDefinition` gate) is already fixed via a structural query, so the interface change would be cosmetic churn without a concrete consumer. ## Tests - `topic-extractor.test.ts` — 30/30 pass - `http-route-extractor.test.ts` — 18/18 pass - `grpc-extractor.test.ts` — 43/43 pass - `manifest-extractor.test.ts` — 8/8 pass - `npx tsc -p tsconfig.json --noEmit` clean Co-authored-by: Claude <noreply@anthropic.com> * docs+fix(group): address remaining Claude review items + add pipeline flow chart ## Fixes **Remaining 🔴 — HTTP contract id wildcard format.** Documented the `http::*::<path>` format as an intentional wildcard for manifest links that omit the HTTP method, alongside the explicit-method form (`GET::/path` → `http::GET::/path`). The docblock on `buildContractId` now states both forms, notes that wildcard-aware matching is the responsibility of the sync / cross-impact layer (#793), and recommends the explicit-method form whenever the author knows the method (it round-trips through exact equality without needing wildcard logic downstream). Tests unchanged — the wildcard format is what they've always asserted. **Minor 1 — stale comment at `manifest-extractor.ts:124-126`.** The comment claimed "creates a contract with an empty symbolUid/ref" but the code switched to `manifestSymbolUid(repo, contractId)` a few commits back. Updated to describe the actual synthetic-uid fallback semantics and the cross-impact path that relies on both sides of the join deriving the same uid. **Minor 2 — exhaustiveness guard on `buildContractId`.** The `switch(type)` covered all five current `ContractType` variants but silently returned `undefined` if a new variant was added. Added a `default: const _exhaustive: never = type; throw new Error(...)` clause so the build fails loudly on an unhandled variant. **Minor 3 — `tree.rootNode.text` in `grpc-patterns/node.ts`.** Already fixed in `2f28bfc` via a dedicated structural query (`LOAD_PACKAGE_DEFINITION_SPEC`). No action needed. ## New: pipeline flow chart (per @magyargergo's request) Added `src/core/group/PIPELINE.md` with four Mermaid diagrams: 1. **High-level overview** — `group.yaml` → extractors + manifest → contract matching → `bridge.lbug` → `runGroupImpact`. 2. **Per-repo extractor two-strategy shape** — graph-assisted Strategy A vs. source-scan Strategy B. 3. **Plugin architecture** — orchestrator → registry → per-language `*-patterns/<lang>.ts` → `tree-sitter-scanner.ts` → `ExtractedContract`. 4. **Manifest extraction** — label-scoped `resolveSymbol` with the synthetic-uid fallback. 5. **Cross-impact query (#606)** — local impact → bridge join → cross-repo fan-out. Each diagram is annotated with which PRs own which stage (this PR: extractors + manifest; #795: bridge storage; #606: cross-impact runtime) and points at the concrete files/functions involved. ## Tests - 99/99 extractor tests pass - `npx tsc -p tsconfig.json --noEmit` clean Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
a94d6ef80b
|
Extract registries into model/ module with SemanticModel interface (#786)
* Initial plan * feat(SM-20): extract registries into model/ module with SemanticModel interface - Create model/type-registry.ts — TypeRegistry interface + factory - Create model/method-registry.ts — MethodRegistry interface + factory - Create model/field-registry.ts — FieldRegistry interface + factory - Create model/semantic-model.ts — SemanticModel interface + factory - Create model/heritage-map.ts — re-export HeritageMap types - Create model/binding-accumulator.ts — re-export BindingAccumulator types - Create model/resolve.ts — move lookupMethodByOwnerWithMRO from call-processor - Update symbol-table.ts — delegate to SemanticModel for registry ops - Update call-processor.ts — re-export lookupMethodByOwnerWithMRO from model/resolve No circular dependencies: model/resolve.ts does NOT import resolution-context.ts. All 775 related unit tests pass with no regressions. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/27ad2975-1a31-4f50-815b-178ee8a95277 * fix: clarify re-export comment per code review feedback Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/27ad2975-1a31-4f50-815b-178ee8a95277 * refactor(SM-20): wire up SemanticModel as first-class resolution input PR #786 extracted TypeRegistry/MethodRegistry/FieldRegistry into model/ behind SemanticModel, but consumers still routed through SymbolTable delegates. This change completes Phase 6 of the fuzzy-lookup elimination roadmap by making call-processor, resolution-context, type-env, and heritage-map query the model directly via `table.model.{types,methods,fields}`. Also absorbs the open PR #786 review findings so the branch lands clean: - Removed duplicate JSDoc block on lookupMethodByOwner (symbol-table.ts) - Added model/index.ts barrel for the public model/ surface - Fixed O(n) buildParentMapFromHeritage BFS via head-pointer queue - Clarified re-export facade framing on binding-accumulator.ts and heritage-map.ts inside model/ - Refined @internal JSDoc on lookupMethodByOwnerWithMRO Changes: - symbol-table.ts: expose `readonly model: SemanticModel` on the SymbolTable interface. SymbolTable delegate wrappers (lookupClassByName etc.) stay as thin pass-throughs for backward compat; deletion is a follow-up once all internal callers are migrated. - model/resolve.ts: lookupMethodByOwnerWithMRO now takes SemanticModel instead of SymbolTable, removing the last SymbolTable import from the model/ module. Preserves circular-dependency firewall. - call-processor.ts: 6 call sites in D0 member resolution, field resolution, ctor override, and ctor disambiguation migrated to model.types/methods/fields. - resolution-context.ts: tier 3 class+impl lookup migrated. - type-env.ts: 5 sites across lookupClassDefsByName, resolveFieldType, and resolveMethodReturnType migrated. - heritage-map.ts: parent/child class-name resolution migrated. Tests: - symbol-table.test.ts: +10 parity and feeding-audit tests covering every model.{types,methods,fields} path (Class, Method, Property, Impl, Function-with-ownerId, Property-without-ownerId skip, arity filtering, clear cascade). - call-processor.test.ts: classLookupSpy now targets ctx.symbols.model.types since the wrapper is bypassed. - type-env.test.ts: createMockSymbolTable and the destructured-call makeSymbolTable helpers gained a model shim that forwards to the (possibly overridden) top-level lookup stubs. Validation: full suite 5603 passed / 159 skipped, resolver integration suite (19 files, 1766 tests) clean, tsc --noEmit clean. * refactor(SM-21): invert ownership — SemanticModel contains SymbolTable Follow-up to SM-20. Previously SymbolTable owned a `model` subfield; this commit turns the ownership direction around so the SemanticModel is the top-level container and SymbolTable is nested as `.symbols`: SemanticModel (top-level, passed everywhere) ├── types (TypeRegistry) ├── methods (MethodRegistry) ├── fields (FieldRegistry) └── symbols (SymbolTable — file-indexed + callable-name index) The owner-scoped registries live directly on the model; file and callable-name lookups go through `.symbols`. Consumers receive a `SemanticModel` and reach into the appropriate field — no more `table.model.types.X` double-hop. Core changes: - symbol-table.ts: createSymbolTable now takes injected TypeRegistry/MethodRegistry/FieldRegistry via a SymbolTableDeps argument. When omitted (test fallback), it creates standalone registries locally and clears them in clear() — production callers always inject. The five registry convenience delegates (lookupClassByName, lookupMethodByOwner, lookupFieldByOwner, lookupClassByQualifiedName, lookupImplByName) remain as thin forwards to the injected registries so standalone SymbolTable use (chiefly tests) stays ergonomic. - model/semantic-model.ts: createSemanticModel() now creates the three registries AND a SymbolTable wired to them, exposing the SymbolTable as `.symbols`. clear() cascades through all four. - resolution-context.ts: `readonly symbols: SymbolTable` field is replaced with `readonly model: SemanticModel`. Internal factory builds a SemanticModel and keeps a local `symbols` alias for backward-compatible inner body. Consumer migrations (src/): - call-processor.ts: ctx.symbols.add/.lookupExactAll/ .lookupCallableByName → ctx.model.symbols.*; ctx.symbols.model.X → ctx.model.X. buildTypeEnv option key renamed symbolTable → model. - type-env.ts: symbolTable parameter renamed model (type SemanticModel), all internal call sites rewritten to use model.types.*, model.methods.*, model.fields.*, model.symbols.lookupExactAll / .lookupCallableByName. - heritage-map.ts: 2 class-lookup sites migrated. - pipeline.ts: ctx.symbols → ctx.model.symbols throughout. Test migrations: - symbol-table.test.ts: parity tests (which validated the old table.model.X hop) replaced with direct SemanticModel coverage via createSemanticModel(). New tests exercise types/methods/fields/ symbols feeding end-to-end. - type-env.test.ts: createMockSymbolTable rebuilt as a SemanticModel-shaped mock that still accepts the legacy flat override bag for backward compat; inline `makeSymbolTable` helpers for destructured-call and importedReturnTypes suites rewritten to match the new shape; buildTypeEnv options `symbolTable: X` and `{ symbolTable }` shorthand renamed to `model:`; one real createSymbolTable-based test rewritten to use createSemanticModel. - call-processor.test.ts, heritage-map.test.ts, heritage-processor.test.ts, symbol-resolver.test.ts: bulk sed `ctx.symbols.` → `ctx.model.symbols.`. call-processor.test.ts spy updated to target `ctx.model.types.lookupClassByName`. Validation: full test suite 5589 passed / 169 skipped / 0 failed; tsc --noEmit clean; pre-commit eslint + prettier + typecheck all green. CLAUDE.md / AGENTS.md stats bumped from an earlier `npx gitnexus analyze` refresh (3965 symbols / 10012 edges / 243 flows). * refactor(SM-22/SM-23): dispatch table + DAG rearchitecture SM-22: Extract registration dispatch table into model/registration-table.ts. Replaces the if/else ladder inside SymbolTable.add() with an O(1) Map<NodeLabel, RoutingDecision> fan-out. SemanticModel wires the table per-instance so hooks close over the correct registries. SM-23: DAG rearchitecture. symbol-table.ts is now a pure 2-index leaf (fileIndex + callableByName) with zero imports from model/. All type/method/field routing lives in the model/ layer. Tests migrated to createSemanticModel() + model.symbols access pattern. Tests: 5632 passed, 0 failures. * refactor: delete dead code (skipCallableIndex + model/ facades) Removes the unused skipCallableIndex flag from the registration dispatch table and deletes two facade files that had zero consumers. skipCallableIndex was declared on RoutingDecision and populated for all 10 entries but never read at runtime — semantic-model.ts explicitly documented that the flag was NOT consulted. The callable-index gate lives inside SymbolTable.add() via CALLABLE_TYPES.has(type), which is the single source of truth. Deleting the flag keeps SymbolTable as the sole decision point and removes documentation-as-data. model/binding-accumulator.ts and model/heritage-map.ts were facade pass-throughs of their parent-directory counterparts. Grep confirms no consumer imports either from the model/ path — all usage goes through ../binding-accumulator.js and ../heritage-map.js directly. model/index.ts was the only "user" and re-exported them with a note about unifying the import boundary, but that boundary has no actual consumers today. Resolves review findings M-01 and M-03 from .context/compound-engineering/ce-review/20260411-144641-59605d93/maintainability.json Tests: 5631 passed, 0 failures (1 less than pre-Unit-1: the skipCallableIndex-specific assertion was removed). * refactor: remove lookupMethodByOwnerWithMRO backward-compat shim call-processor.ts re-exported lookupMethodByOwnerWithMRO from ./model/resolve.js as a backward-compat shim for symbol-table.test.ts. The function already lives in model/resolve.ts and is re-exported properly from model/index.ts (the barrel) — the call-processor shim was a duplicate export path with no durable reason to exist. Migrated the test import from call-processor.js to model/index.js (the canonical barrel). Deleted the re-export statement and the stale "re-exported for backward compatibility" comment block. Hoisted the remaining import to the top of the file with the other imports; the bottom-of-file position was a relic of the shim pattern. Resolves review finding M-02 from .context/compound-engineering/ce-review/20260411-144641-59605d93/maintainability.json Tests: 5631 passed, 0 failures. * refactor: harden registration dispatch runtime safety Two hardening changes in semantic-model.ts, both closing silent-failure paths in the SM-series dispatcher-bypass failure mode. 1. model.symbols.clear() now cascades to the owner-scoped registries. Previously, the SymbolTable facade exposed rawSymbols.clear directly, which only emptied fileIndex + callableByName — the types/methods/ fields registries stayed populated. Any caller holding a SymbolTable reference that invoked .clear() left the model in a split state where subsequent .add() calls double-registered in the registries. No current caller exercises this path, but it was a latent phantom- resolution risk that didn't belong in a public API. Extracted the cascade into a single cascadeClear closure wired into both model.clear() and the facade's clear field. 2. runExhaustivenessGuard now throws instead of console.warn on drift. The production short-circuit via NODE_ENV === 'production' is preserved, so real users never see the throw — but CI and dev runs now fail loudly if a NodeLabel is added to gitnexus-shared without being placed in one of the three registration-table allowlists. The previous warn-only behavior was silent in test output volume; SM-19 already documented dispatcher-bypass as the dominant silent-failure mode in this codebase. Test-first: added test/unit/model/semantic-model.test.ts covering model.symbols.clear() cascade (4 registries × clear = 4 tests), the existing model.clear() cascade (regression guard), and a happy-path construction test that verifies the current allowlists have zero drift. Resolves correctness P2 finding (symbols.clear() partial clear), correctness P3 (exhaustiveness warn-only), and kieran-typescript KT-03 (same exhaustiveness finding, agreement boost). Tests: 5638 passed (+7 new), 0 failures. * docs: fix stale JSDoc references in resolveStaticCall call-processor.ts:2215-2216 referenced SymbolTable.lookupClassByName and SymbolTable.lookupMethodByOwner via {@link}. Both methods were removed from SymbolTable during SM-20 — they now live on TypeRegistry and MethodRegistry respectively, accessible via model.types and model.methods. Other SymbolTable.* references in the codebase (lookupExactFull, add, lookupCallableByName in call-processor.ts:593, symbol-table.ts:86, type-extractors/types.ts:57) target methods that are still on SymbolTable and remain valid. Resolves correctness P3 and kieran-typescript KT-02 (same finding, agreement boost). * refactor: deduplicate ALL_NODE_LABELS constant ALL_NODE_LABELS was private in semantic-model.ts and duplicated verbatim in registration-table.test.ts. Two hardcoded lists meant a new NodeLabel added to gitnexus-shared could land in one copy but not the other, silently drifting the exhaustiveness invariant. Exported ALL_NODE_LABELS from semantic-model.ts, re-exported through model/index.ts for barrel consistency, and switched the test to import it instead of redeclaring. The explanatory comment now describes the single-source-of-truth contract. Resolves maintainability M-04. Tests: 5638 passed, 0 failures. * refactor: add compile-time NodeLabel exhaustiveness check The runtime exhaustiveness guard in semantic-model.ts caught drift at test time. Added a type-level check in registration-table.ts that catches drift at BUILD time — if a new NodeLabel is added to gitnexus-shared without being classified into one of the three allowlists, TypeScript fails the _exhaustiveCheck assignment and names the missing label. The runtime guard stays as belt-and-suspenders: if a future contributor bypasses the type check with @ts-ignore, the runtime guard still fires in dev/test. Implementation: converted the three allowlist Set<NodeLabel> initializers to use `as const` tuples, then derived a union type from the tuples and asserted `Exclude<NodeLabel, union> extends never`. Zero runtime impact — the exported Sets are unchanged, Map.get hot-path performance is unchanged, the test API is unchanged. Resolves kieran-typescript KT-04. Tests: 21/21 registration-table tests pass with zero modifications. * refactor(test): restore type safety to createMockSymbolTable createMockSymbolTable was widened to (overrides: any = {}): any with an eslint-disable-next-line, and every buildTypeEnv call site passed the mock as `model: mockSymbolTable as any`. The widening masked silent false-green tests: buildTypeEnv accesses model.types/methods/fields, and a flat any-typed override could silently return undefined from a path that TypeScript should have caught at compile time. Defined LegacyMockOverrides interface with typed stubs for each method the mock can override (SymbolTable reads + TypeRegistry/MethodRegistry/ FieldRegistry lookups). Return type is now SemanticModel, so the mock object is compile-checked against the real interface — a missing registry method is a type error, not a silent runtime undefined. Removed the eslint-disable and all 9 `as any` casts at call sites (lines 1287, 1300, 1307, 2124, 2138, 5823, 5835, 5850, 5870). The mock's return value now flows through buildTypeEnv's typed `model` option without coercion. Resolves kieran-typescript KT-01 and testing gap TG-02. This was the highest-value cleanup in the plan — the only finding representing real hidden test weakness. Tests: 360 passed | 7 skipped (type-env.test.ts), typecheck clean. * test: close coverage gaps in model/ registries Added direct unit tests for the three owner-scoped registries that previously had only transitive coverage via symbol-table.test.ts and registration-table.test.ts. These new tests pin behaviors that were flagged by the testing reviewer as untested or undertested. method-registry.test.ts (14 tests): - T-01: arity-fallback branch — when argCount matches no overload, fall back to the full pool so fuzzy resolution still has candidates. Previously untested and would have returned undefined instead of a valid candidate if the branch regressed. - T-02: requiredParameterCount range filtering — methods with default parameters accept any argCount in [requiredParameterCount, parameterCount]. Previously untested at the registry level. - Variadic fallback (parameterCount=undefined is retained during arity narrowing, bypassing range check). - Return-type dedup paths: shared returnType → first wins, differing returnTypes → undefined, firstReturnType=undefined → undefined, single-overload skips dedup entirely. type-registry.test.ts (9 tests): - classByName homonym accumulation (two User classes in different packages both returned). - classByQualifiedName disambiguation — same simple name, different FQNs resolve independently. - Partial classes with identical simple + qualified name accumulate in both indexes. - registerImpl stores Rust impl blocks separately from classes. - Multiple impl blocks per type accumulate. field-registry.test.ts (6 tests): - register/lookup round-trip, owner-scope isolation, last-wins on duplicate key (flat map, not overload list). - clear + re-register round-trip. Extended symbol-table.test.ts cascade test (renamed from "both registries" to "all three registries and the nested symbol table") to also assert model.methods and model.fields are cleared — the test name previously implied full coverage but only asserted types + symbols. Resolves testing findings T-01, T-02, T-03, T-05. Tests: 5667 passed (+29 new), 0 failures. * refactor(test): replace brittle reference-equality tests + add intent comments Two cleanups flagged as low-severity P3 by the testing reviewer: 1. registration-table.test.ts: Replaced three reference-equality tests (hook identity via toBe) with behavioral tests that survive a future refactor to per-label closures. The new "class-like behavior group" describe iterates Class/Struct/Interface/Enum/Record/Trait and verifies each one writes to types.registerClass. Same pattern for Method/Constructor. A separate "behavior group isolation" describe verifies class-like hooks don't leak into methods/fields and Impl never pollutes registerClass. Strictly more coverage than the reference-equality tests provided and implementation-independent. 2. symbol-resolver.test.ts: Added a comment above the lookupExactFull and SM-16: getFiles() describes explaining why they intentionally use createSymbolTable() directly instead of createSemanticModel(). The DAG leaf-only behaviors they test do not involve registries, so testing the bare SymbolTable keeps the unit isolated. Prevents a future reader from "fixing" the inconsistency. 3. qualified-class-lookups.test.ts: Added a comment above `const symbolTable = model.symbols` explaining that processParsing writes still reach the owner-scoped registries via SemanticModel's fan-out — the alias is convenience, not a leaf in isolation. Resolves testing T-04, kieran-typescript KT-05, kieran-typescript KT-06. Tests: affected files all green (112 passed in registration-table + symbol-resolver + qualified-class-lookups). * refactor(model): collapse RoutingDecision wrapper and trim barrel surface Two cleanups against the advanced-review findings on post-Unit-9 state: S2 (cross-reviewer agreement — architecture-strategist + code-simplicity): Delete the RoutingDecision single-field wrapper interface. Post-Unit-1 it held exactly one field (hook: RegistrationHook) and added pure ceremony at every call site — `dispatchTable.get(key)!.hook(name, def)` vs the now-direct `dispatchTable.get(key)!(name, def)`. Change the Map type from Map<NodeLabel, RoutingDecision> to Map<NodeLabel, RegistrationHook>, drop the interface, and update 17 test call sites. A3 (architecture-strategist): Trim model/index.ts barrel surface. createRegistrationTable, RegistrationHook, and RegistrationTableDeps were re-exported from the barrel despite having zero legitimate consumers outside model/ itself. The only callers (semantic-model.ts and registration-table.test.ts) import directly from ./registration-table.js. Barrel exposure invited external callers to construct orphan dispatch tables with independent registries, weakening the SM-21 ownership inversion where SemanticModel is the composition root. Kept CALLABLE_ONLY_LABELS, INERT_LABELS, DISPATCH_LABELS exported since those remain useful for downstream resolution logic and have no construction risk. Resolves review findings: - S2 (code-simplicity P3, 0.85) + architecture-strategist residual - A3 (architecture-strategist P3, 0.82) Tests: 5674 passed, 0 failures. Typecheck clean. * refactor(model): replace runtime exhaustiveness guard with compile-time bijection Replace the three-layer drift protection (hardcoded ALL_NODE_LABELS array + 3 tuple consts + _ExhaustiveLabelCheck type + runExhaustivenessGuard runtime + CI taxonomy test) with a single Record<NodeLabel, LabelBehavior> map that structurally proves every invariant at compile time. ## Before - ALL_NODE_LABELS hardcoded in semantic-model.ts (36 entries, could drift) - DISPATCH_LABELS_TUPLE / CALLABLE_ONLY_LABELS_TUPLE / INERT_LABELS_TUPLE private tuples (36 more entries total, could overlap or miss) - _ClassifiedLabel / _UncoveredLabel type-level check (caught missing labels but NOT duplicates across tuples) - runExhaustivenessGuard runtime throw (only defense against duplicates) - NodeLabel taxonomy coverage test in CI (same check as runtime guard) Four defenses for invariants that the type system can express directly. ## After ```ts type LabelBehavior = 'dispatch' | 'callable-only' | 'inert'; const LABEL_BEHAVIOR = { Class: 'dispatch', // ...36 entries... Tool: 'inert', } as const satisfies Record<NodeLabel, LabelBehavior>; ``` The `as const satisfies Record<NodeLabel, LabelBehavior>` combo enforces: 1. **Every NodeLabel must be a key** — Record requires all K keys. Adding a NodeLabel to gitnexus-shared without classifying it here fails with "Property 'X' is missing in type ..." naming the drifted label. 2. **No non-NodeLabel keys allowed** — `satisfies` with object literals triggers excess-property checking. A typo'd key fails to compile. 3. **No duplicate classification** — impossible by construction; object keys are unique at the source level. 4. **Valid category** — LabelBehavior is a narrow union, typos caught. `ALL_NODE_LABELS`, `DISPATCH_LABELS`, `CALLABLE_ONLY_LABELS`, and `INERT_LABELS` are now derived via `Object.keys(LABEL_BEHAVIOR)` and `filter(l => LABEL_BEHAVIOR[l] === ...)` — single source of truth, structurally impossible to drift. ## Deleted - runExhaustivenessGuard() function in semantic-model.ts (~18 lines) - ALL_NODE_LABELS hardcoded array in semantic-model.ts (~38 lines) - DISPATCH_LABELS_TUPLE / CALLABLE_ONLY_LABELS_TUPLE / INERT_LABELS_TUPLE private consts in registration-table.ts (~30 lines) - _ClassifiedLabel / _UncoveredLabel / _exhaustiveCheck type machinery (~20 lines) ## Kept named proofs: none The `as const satisfies` on the object literal already catches all four drift modes. Named type-level proofs (_MissingFromMap / _ExtraKeysInMap) are pure duplication and were removed per review. ## Also in this commit - S6: trim wrappedAdd narration comments in semantic-model.ts (Step 1/2/3 block comments removed; kept the Function+ownerId WHY note) - A3: tighten model/index.ts barrel — createRegistrationTable, RegistrationHook, RegistrationTableDeps remain direct-imports only; ALL_NODE_LABELS and LabelBehavior re-exported from the new home in registration-table.ts ## Resolves - Advanced-review S4 (runtime guard per-call cost) — guard no longer exists - Advanced-review S1 (tuple three-defenses indirection) — single Record replaces all tuples - Correctness P3 (exhaustiveness warns-only) — structurally impossible to drift - Unit 6 type-level check — subsumed by the Record type - Unit 3 runtime throw — no longer needed Tests: 5674 passed, 0 failures. Typecheck clean. * test(model): delete duplicate closure-isolation spy tests S5 (code-simplicity P3): The 'closure isolation — each hook can only write to its registry' describe block duplicated the 'behavior group isolation' block's coverage via a different mechanism. Behavioral tests (lines 151-174, kept): table.get('Class')!('User', def); expect(deps.methods.lookupMethodByOwner('unrelated', 'User')).toBeUndefined(); expect(deps.fields.lookupFieldByOwner('unrelated', 'User')).toBeUndefined(); Spy tests (deleted, ~55 lines): vi.spyOn(deps.methods, 'register') table.get('Class')!('User', def); expect(methodsSpy).not.toHaveBeenCalled(); Both assert the same invariant — classHook does not touch the methods or fields registries. The behavioral form observes the END STATE of the registry (lookup returns undefined), which is the actual contract. The spy form asserts the IMPLEMENTATION (a specific method was not called), which couples to internal wiring — a refactor to a different register function name would break the spy test while the behavioral test would still pass. Also dropped the now-unused `vi` import from vitest. Tests: 24/24 registration-table.test.ts pass (-4 from spy deletion). * refactor(model): compile-time cross-invariant between CLASS_TYPES and dispatch classHook A1 (architecture-strategist P2, 0.90): CLASS_TYPES in symbol-table.ts and the class-like entries of the dispatch table were two independent hardcoded sets. Adding a new class-like label (e.g. Swift 'Extension') to one but not the other would silently degrade qualifiedName population — the symptom is subtle (partial qualified-name lookups) and no test asserted the co-extensive invariant. Fixed with a single source of truth and a two-layer compile-time enforcement: ## symbol-table.ts - Add `CLASS_TYPES_TUPLE` as `readonly [...] as const satisfies readonly NodeLabel[]`. The `satisfies` forces every tuple entry to be a valid NodeLabel at compile time. - Export derived type `ClassLikeLabel = typeof CLASS_TYPES_TUPLE[number]`. - Derive `CLASS_TYPES` Set from the tuple — same runtime shape as before, now typed `ReadonlySet<NodeLabel>`. ## registration-table.ts - Import `CLASS_TYPES_TUPLE` and `ClassLikeLabel` from symbol-table.ts. - Narrow the `satisfies` on `LABEL_BEHAVIOR` via intersection: Record<NodeLabel, LabelBehavior> & Record<ClassLikeLabel, 'dispatch'> This forces every class-like label to have value 'dispatch' at compile time. Adding a label to CLASS_TYPES_TUPLE without classifying it as dispatch in LABEL_BEHAVIOR fails to compile with a type error naming the drifted label. - Build the class-like entries of the dispatch Map by iterating `CLASS_TYPES_TUPLE` at factory time. Adding a label to the tuple automatically wires it to classHook — no second place to update. ## What the design prevents 1. Drift scenario A (A1 original): 'Extension' added to CLASS_TYPES_TUPLE but not to LABEL_BEHAVIOR → compile error on LABEL_BEHAVIOR's satisfies. 2. Drift scenario B: 'Extension' added to CLASS_TYPES_TUPLE but not wired to classHook → impossible because the Map is derived from the tuple. 3. Drift scenario C: class-like label classified as something other than 'dispatch' in LABEL_BEHAVIOR → compile error on the narrowed intersection. Runtime behavior unchanged: same 6 labels in CLASS_TYPES, same 6 class-like entries in the dispatch Map. Tests pin the behavior via the existing behavior-group tests in registration-table.test.ts. DAG unchanged: registration-table.ts already imported from symbol-table.ts (the allowed upward direction). symbol-table.ts still imports nothing from model/. Tests: 5670 passed, 0 failures. Typecheck clean. * test(field-extraction): use SemanticModel facade instead of raw SymbolTable A6 (architecture-strategist P3, 0.85): field-extraction.test.ts created its FieldExtractorContext fixture with `symbolTable: createSymbolTable()` — a raw SymbolTable leaf, not the facade. In production, the context's symbolTable field is always `model.symbols` (the SemanticModel-wrapped facade where .add() dispatches through the owner-scoped registries). The current field extractors don't call symbolTable.add() at all, so this change is behavior-neutral today. The value is architectural consistency — matching the test fixture to the production shape prevents silent drift if a future field extractor starts registering dynamically-discovered properties via the context. Without the fix, such writes would hit the raw leaf and skip the fan-out, and tests would pass even though the symptom (empty FieldRegistry) would manifest in production. Tests: 50/50 field-extraction.test.ts pass. Production tsc --noEmit clean. Test-tsconfig error count unchanged (634 pre-existing errors in unrelated test files, out of scope). * refactor(A5): decouple model/resolve.ts from language registry Move the MroStrategy type into gitnexus-shared and replace the language: SupportedLanguages parameter on lookupMethodByOwnerWithMRO with a direct mroStrategy: MroStrategy literal. Callers derive the strategy from their language provider before invoking the resolver. model/resolve.ts no longer imports from ../languages/index.js, so the model/ layer is free of cross-layer coupling with the language registry — this closes finding A5 from the SM-20/21/22/23 advanced review (plan 006). * feat(A4): add MethodRegistry.lookupMethodByName flat-by-name index Add a secondary `methodsByName: Map<string, SymbolDefinition[]>` index on MethodRegistry that returns every method with a given unqualified name, accumulated across owners and overloads. The new index shares SymbolDefinition references with methodByOwner — no duplication. This is step 1 of the A4 double-index removal (plan 006). Tier 3 global resolution will switch to this index in Unit 3 so Method and Constructor can be removed from CALLABLE_TYPES in Unit 4. * refactor(A4): extend Tier 3 + memberCallByFile to consult method registry Add model.methods.lookupMethodByName to Tier 3 global resolution in resolution-context.ts and to the callable-pool build in call-processor.ts (resolveMemberCallByFile + D2 widen path). Intentionally behavior-preserving: Method and Constructor are still in CALLABLE_TYPES so the new lookup returns identical candidates that already reach Tier 3 through callableByName. Both paths dedup by nodeId during this intermediate state — Unit 4 shrinks CALLABLE_TYPES and the dedup is removed. Part of plan 006 A4 step 2. * refactor(A4): shrink CALLABLE_TYPES to free callables only CALLABLE_TYPES = {Function, Macro, Delegate}. Method and Constructor are no longer double-indexed in callableByName — they reach resolvers through model.methods.lookupMethodByName instead. Companion changes: - Introduce CALL_TARGET_TYPES = CALLABLE_TYPES ∪ {Method, Constructor} for the resolver's kind filter (filterCallableCandidates, countCallableCandidates). Separates registration semantics (narrow) from the resolver's acceptable-target set (wide). - type-env.ts for-loop return-type inference consults both indexes, treating the union as the authoritative call pool. - resolveMemberCallByFile + D2 widen path keep the nodeId dedup in place: Python/Rust/Kotlin class methods emitted as Function+ownerId still land in both indexes until Unit 5 unblocks the normalization. - Tier 3 global resolution (resolution-context.ts) keeps the same dedup for the same reason. Test updates reflect the new contract: Method/Constructor live in methodsByName, not callableByName. Orphan Method-without-ownerId now lives only in the file index (no registry coverage). Part of plan 006 — closes A4 for strictly-labeled methods. Python/ Rust/Kotlin Function+ownerId normalization is tracked as Unit 5 (blocked). * refactor: rename CALLABLE_TYPES → FREE_CALLABLE_TYPES Pure rename. The constant's meaning changed in Unit 4 (free callables only — no methods, no constructors) so the name now reflects that scope: "callables that have no owner scope". Updates the constant declaration and every consumer in src/ and test/. Closes plan 006 Unit 6. * refactor(A2): strict SymbolTableReader (pure reads) + SymbolTableWriter (+add) Split the SymbolTable interface into three strictly layered surfaces: - SymbolTableReader: lookups + iteration. NO add, NO clear. Holders cannot mutate the table in any way. - SymbolTableWriter extends Reader: + add. NO clear. Holders can register new symbols but cannot trigger a leaf-index reset. - InternalSymbolTable (private, not exported): + clear. The cascading reset capability is reachable only through createSymbolTable's return type, held exclusively by SemanticModel.rawSymbols. SemanticModel.symbols is now typed as SymbolTableWriter — external consumers (workers, processors, pipelines) can register symbols and query them, but cannot reach .clear(). The A2 LSP fix holds: callers holding any public reference cannot desync the leaf indexes from the owner-scoped registries. Delete the transitional `type SymbolTable = SymbolTableReader` alias and migrate every consumer (src + test) to the explicit names: - Field and parameter annotations use SymbolTableReader by default; only code that calls .add() uses SymbolTableWriter. - parsing-processor (workers + sequential paths) takes SymbolTableWriter so it can register extracted symbols. - field-types, call-processor, named-binding-processor, workers/parse-worker: use SymbolTableReader (query-only). - Tests: drop the stale `clear` fields from mock factories and migrate the semantic-model cascade tests from the removed model.symbols.clear() path to model.clear(). Closes plan 006 Unit 7. Industry sources: TypeScript compiler API builder pattern, Salsa ParallelDatabase, .NET IReadOnlyList. See the a2-lsp-clear-contract-research artifact for full citations. * feat(A2): add SemanticModel.resetFileIndex() partial-reset entry point Add a named method that clears only the leaf file and callable indexes without cascading to the three owner-scoped registries (types, methods, fields). Replaces the rare partial-reset use case that was previously reachable via the now-removed symbols.clear() path from A2 (plan 006 Unit 7). JSDoc makes the semantic difference with model.clear() explicit so future readers don't have to guess which method to call for a given reingestion scenario. Test-first: three scenarios cover the partial-vs-full semantics, re-add after reset, and idempotency. Closes plan 006 Unit 8. * docs(S7): trim registration-table module JSDoc Remove the ~24 lines of design-provenance citations from the module JSDoc. The rust-analyzer, TypeScript-compiler, and Fowler references are preserved in git history via the original SM-22 commits and in plan 006 Unit 9. Keep the ownership diagram, behavior-group table, and the 'How to add a new NodeLabel' checklist — those are load-bearing for future contributors. Closes plan 006 Unit 9 (S7 advanced-review finding). * test(S3): migrate type-env.test.ts off LegacyMockOverrides Replace the createMockSymbolTable bridge and LegacyMockOverrides interface with real createSemanticModel() + add() calls across all 14 call sites. Where a test needs a specific registry lookup that can't be pre-populated cleanly, use vi.spyOn on the real registry instead. Pattern breakdown: - Pattern A (pre-populate via model.symbols.add): 13 sites - Pattern B (vi.spyOn on registry lookup): 1 site Deletes LegacyMockOverrides + createMockSymbolTable entirely. The real MethodRegistry arity/returnType semantics match the hand-rolled mock behavior in every migrated case, and no 'as any' casts remain in the file. Closes plan 006 Unit 10 (S3 advanced-review finding). * refactor: remove unused MroStrategy type exports from language-provider and resolve modules * refactor: relocate symbol-table, heritage-map, resolution-context into model/ Use git mv so blame and history follow each file: - gitnexus/src/core/ingestion/symbol-table.ts → model/symbol-table.ts - gitnexus/src/core/ingestion/heritage-map.ts → model/heritage-map.ts - gitnexus/src/core/ingestion/resolution-context.ts → model/resolution-context.ts These three files are part of the SemanticModel layer (file/callable indexes, heritage parent map, tiered resolver) and now sit alongside the registries they collaborate with. Updates every consumer import path across src/ and test/ to the new locations. * refactor(model): enforce pure-leaf DAG + delete legacy re-exports model/ is now a pure leaf: zero upward imports and zero compat shims in its parent processors. Completes the DAG cleanup started in the previous commit. 1. walkBindingChain — moved into model/resolution-context.ts; named-binding-processor.ts deleted. 2. NamedImportMap + NamedImportBinding + isFileInPackageDir — moved into model/resolution-context.ts. Every consumer now imports from the canonical location directly. Legacy re-exports in import-processor.ts deleted. 3. c3Linearize + gatherAncestors — moved into model/resolve.ts. mro-processor.ts imports them back for computeMRO. Legacy c3Linearize re-export from mro-processor.ts deleted. 4. ExtractedHeritage type — moved into model/heritage-map.ts. call-processor.ts, parsing-processor.ts, pipeline.ts, heritage-processor.ts, and the test files now import it from the canonical location. Legacy re-exports in parse-worker.ts and heritage-processor.ts deleted. 5. resolveExtendsType — rewritten in model/heritage-map.ts to take an explicit HeritageResolutionStrategy (A5-style DI). buildHeritageMap accepts an optional getHeritageStrategy callback; production uses getHeritageStrategyForLanguage from heritage-processor.ts. Legacy resolveExtendsType re-export from heritage-processor.ts deleted. Verified: - grep 'from "..' gitnexus/src/core/ingestion/model → empty - grep 'Re-export for legacy' gitnexus/src/core/ingestion → empty - npx tsc --noEmit → clean - npx vitest run → 5686 passing * docs(model): strip phase/plan references from module comments Remove SM-20/21/22/23, A2/A4/A5, plan 006, Unit N labels and historical phrasing ("previously", "legacy", "model-leaf DAG cleanup") from all 10 files in src/core/ingestion/model/. Preserve domain vocabulary (Tier 1/2/3), invariants, and caveats — only the plan archaeology is gone. * refactor(model): tighten interface segregation + compile-time invariants Apply four gated findings from branch-wide code review: - SemanticModel.symbols now typed as SymbolTableReader; MutableSemanticModel widens it back to SymbolTableWriter. ResolutionContext.model is typed as MutableSemanticModel since it owns the lifecycle. Resolvers that only query symbols can annotate their own fields as SemanticModel to drop write access at the type level. - Lookup methods (lookupExactAll, lookupCallableByName, lookupClassByName, lookupClassByQualifiedName, lookupImplByName) now return readonly SymbolDefinition[]. The returned arrays are live views into the internal indexes; the readonly marker prevents accidental caller mutation. walkBindingChain return type narrowed to match. - FREE_CALLABLE_TUPLE + FreeCallableLabel exported from symbol-table.ts as the single source of truth for free-callable labels. LABEL_BEHAVIOR now satisfies Record<FreeCallableLabel, 'callable-only'> as a second cross-invariant alongside Record<ClassLikeLabel, 'dispatch'>. Adding a label to the tuple without classifying it as 'callable-only' fails at build time. CALLABLE_ONLY_LABELS is now a re-export alias of FREE_CALLABLE_TYPES so the two sets cannot drift. - walkBindingChain fast-exits before allocating its cycle-detection Set when the caller's file has no named bindings. Skips ~200k transient Set allocations per large-repo resolution pass. Also fixes five stale comments flagged by the review: duplicate JSDoc block on RegistrationHook merged; resolve.ts "delegates to mro-processor" direction corrected; RegistrationTableDeps JSDoc names createRegistrationTable (not createSymbolTable); mro-processor.ts "re-exported at top" stale comment removed; gatherAncestors export comment matches reality. tsc --noEmit clean, full test suite green (5786 tests). * refactor(model): resolve four deferred P2 review findings Address the four gated items from the branch-wide review that needed design decisions before applying: F#3 — Method/Constructor without ownerId fallback to callable index. The dispatch hook silently skips owner-scoped labels that lack an owner (an extractor contract violation — AST-degraded parse, or a buggy language extractor). Pre-dispatch-table code let such defs fall through to callableByName and stay reachable at Tier 3 global resolution. This restores that fallback in SymbolTable.add so orphaned Methods and Constructors don't silently vanish. Property deliberately does NOT participate in the fallback to avoid polluting common names like id / name / type. F#4 — Delete MutableSemanticModel.resetFileIndex. The method had zero production callers (only three tests), documented a "rare partial- reingestion flow" that was never implemented, and contained the adversarial-reviewer's double-populate trap: calling resetFileIndex followed by re-adding the same class symbol would push a duplicate SymbolDefinition into TypeRegistry.classByName without ever clearing the first one. If incremental reingestion is ever needed, it can be designed properly with per-file TypeRegistry invalidation. For now, deleting the footgun is safer than documenting it. F#5 — Compile-time dispatch-table completeness check. `LABEL_BEHAVIOR` already enforces "every NodeLabel is classified" via `Record<NodeLabel, LabelBehavior>`, but the dispatch-table factory populated its Map with manual `table.set(...)` calls that TypeScript could not correlate back to the `'dispatch'` classification. Add a type-level `DispatchLabel` extracted from `LABEL_BEHAVIOR` via a conditional mapped type, and build the table from an object literal that satisfies `Record<DispatchLabel, RegistrationHook>`. Adding a new dispatch-classified label without wiring it to a hook now fails the build with a named-key error — no more silent no-op hooks. F#7 — Tier 3 dedup fast-path via MethodRegistry.hasFunctionMethods. The Set-based dedup between callableDefs and methodDefs is only needed when a Python/Rust/Kotlin class method (emitted as Function+ownerId by the worker) lands in both indexes. For TS/Java/C#/C++/Ruby-only repos — where the two indexes are disjoint by construction — the dedup was pure overhead on every global-tier hit. MethodRegistry now tracks whether any Function-typed def was ever registered, and resolution- context branches Tier 3 into a concat-only fast path when that flag is false. Slow path with dedup survives unchanged for mixed-language repos. New tests pin the invariants: hasFunctionMethods flag transitions, Method/Constructor orphan fallback, Property non-fallback, and the MethodRegistry clear() reset. Full test suite green (5756 tests). * refactor(model): close remaining P3 review findings + coverage gaps Address the remaining review items in one batch. Production refactors: - Rename classHook → classLikeHook (M05). The hook handles Class / Struct / Interface / Enum / Record / Trait; the vocabulary used in surrounding docs and the behavior-group table is "class-like". The rename makes the code match the taxonomy without forcing readers through a mental glossary. - Extract MAX_BINDING_CHAIN_DEPTH constant in resolution-context.ts and document it as a known silent false-negative source (ADV-003). Five hops cover the common TypeScript monorepo pattern; raising the cap is a one-line change if a real repo exceeds it. walkBindingChain consumes the constant so the 5 magic number no longer floats free. - Replace defs.filter() allocation in MethodRegistry.lookupMethodByOwner with a two-pass streaming count + conditional materialization (PERF-04). Pure-match and pure-reject arity paths now skip the filtered-array allocation entirely; only the discriminating case (at least one match AND at least one rejection) pays it. - Rewrite NOOP_SYMBOL_TABLE in parse-worker.ts and NOOP_SYMBOL_TABLE_SEQ in parsing-processor.ts to implement all six SymbolTableReader methods (ADV-005). The `as unknown as SymbolTableReader` cast is removed in favor of a direct SymbolTableReader annotation, so future additions to the interface surface as compile errors on the stubs instead of silently falling through. - type-env.ts getCallableUnionCount and getFirstCallable now take `model: SemanticModel` as an explicit argument instead of reaching into the enclosing `model!` non-null assertion (KT-003). Callers enter via an `if (model)` guard and pass the narrowed reference, so the non-null precondition is visible at the type level and the closures cannot be accidentally extracted into a context without the guard. - Tier 3 dedup in resolution-context.ts now covers all four index reads (classDefs, implDefs, callableDefs, methodDefs) via a pushUnique helper (C-03). Previously classDefs and implDefs were spread directly without dedup; any theoretical nodeId collision would have produced duplicates in globalDefs. Test infrastructure: - Extract makeDef / makeMethod factory helpers into test/unit/model/helpers.ts (T-07). The four registry/table test files now import the shared helper and specialize with overrides, removing ~25 lines of duplicated boilerplate and creating a single point of maintenance. New test coverage: - T-01: c3 BFS fallback — cyclic Python hierarchy that fails c3 linearization and must fall back to heritageMap.getAncestors() BFS order. Added to the lookupMethodByOwnerWithMRO describe block. - T-02: Tier 2a-named precedence — verifies the binding chain walker fires before Tier 2a import-scoped when an aliased import `import { User as U } from B` competes with a raw same-name Tier 2a hit. Also pins Tier 1 same-file precedence over Tier 2a-named. - T-03: Tier 3 Function+ownerId dedup — end-to-end test that a Python class method emitted as `Function + ownerId` yields exactly ONE Tier 3 candidate (not two). Companion test pins the fast-path branch for hasFunctionMethods === false repos. - T-06: walkBindingChain guards — circular re-export detection, depth-cap exceeded drop, and boundary case at exactly MAX_BINDING_CHAIN_DEPTH hops resolving successfully. All tests added to a new test/unit/model/resolution-context.test.ts dedicated to ResolutionContext.resolve() tier-precedence invariants. Full suite: 5708 passing (minus the known Windows LBUG lock flake that passes in isolation). --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
1ff324ca16
|
feat(group): bridge.lbug storage + contract matching expansion (1/4 of #606 split) (#795)
* feat(group): bridge.lbug storage + contract matching expansion Part 1 of 4 in the split of #606 (ticket: #791, closes #790 with a revised plan per @magyargergo's request). ## What changed Adds the LadybugDB-backed bridge storage infrastructure and extends the contract matching algorithm with wildcard support. All changes are additive: storage.ts, sync.ts, service.ts, cli/group.ts, mcp/tools.ts are left on their upstream main versions and will migrate to the new bridge in follow-up PRs (#792, #793, #794). ### Files **New (844 LOC prod):** - `gitnexus/src/core/group/bridge-db.ts` — atomic write-to-temp with `retryRename` for Windows EBUSY/EPERM, per-item write tolerance via `WriteBridgeReport`, `findContractNode` with three-tier symbol lookup (uid → filePath+name → filePath) - `gitnexus/src/core/group/bridge-schema.ts` — schema DDL - `gitnexus/src/core/group/normalization.ts` — contract ID canonicalization + `dedupeContracts` / `dedupeCrossLinks` helpers used by both matching and bridge write **Modified (+137 LOC prod):** - `gitnexus/src/core/group/matching.ts` — adds `runWildcardMatch` for `grpc::Service/*` wildcard consumers, `buildProviderIndex` helper, and canonical gRPC ID handling in `normalizeContractId` - `gitnexus/src/core/group/types.ts` — `MatchType` gains `'wildcard'`; new `BridgeHandle` and `BridgeMeta` interfaces **New tests (658 LOC):** - `gitnexus/test/unit/group/bridge-db.test.ts` — core write/read round trip, `WriteBridgeReport` shape, dropped-links counter, retryRename behavior on EBUSY/ENOENT/EPERM/EACCES - `gitnexus/test/unit/group/bridge-db-edge.test.ts` — edge cases (malformed meta, missing contract nodes, concurrent access) **Modified tests (+225 LOC):** - `gitnexus/test/unit/group/matching.test.ts` — wildcard consumer matching, gRPC canonical ID handling, same-service guard ### Self-review fixes folded in Carried forward from the original #606 self-review: - `writeBridge` try/finally handle lifecycle + `handleClosed` sentinel - `openBridgeDbReadOnly` partial-handle cleanup - `writeBridgeMeta` uses `retryRename` for Windows consistency - `retryRename` unit tests (was zero coverage) - Per-item try/catch around every CREATE loop so one malformed contract doesn't abort the whole write - Dropped cross-link counter (`linksDroppedMissingNode`) ### Why now magyargergo asked for the #606 PR to be split so we can iterate with confidence (https://github.com/abhigyanpatwari/GitNexus/pull/606#issuecomment-4229612271). This is the foundational layer — pure infra, no user-facing surface, no callers of the new APIs in this PR. Later PRs wire it in. ### How to verify - `cd gitnexus && npx tsc --noEmit` - `cd gitnexus && npx vitest run test/unit/group/bridge-db.test.ts --pool=forks` - `cd gitnexus && npx vitest run test/unit/group/bridge-db-edge.test.ts --pool=forks` - `cd gitnexus && npx vitest run test/unit/group/matching.test.ts --pool=forks` - Pre-commit hook runs clean ### Risk / rollback **Low.** All new code sits under `src/core/group/` in new files plus a minimal `+16/-1` diff to `types.ts` and a `+136/-0` diff to `matching.ts` (both purely additive). No existing callers reference the new APIs (bridge-db, openBridgeOrFallback, runWildcardMatch) — the PRs that wire them in come later in the split chain. Rollback = `git revert` of the merge commit; no state introduced, no schema migration triggered. ### Scope discipline (per GUARDRAILS.md) - Only the 8 files listed above are touched; no drive-by refactors - No CI/release/security config changes - No secrets, tokens, or machine-specific paths - Content is lifted from the #606 branch which already passed CI 11/11 green on `d15b8cb` (before the split) ### Dependencies - **Base:** `main` (no dependencies on other split PRs) - **Blocks:** extractor expansion (#792), sync pipeline (#793), cross-impact feature (#794) - **Related ticket:** #791 Co-authored-by: Claude <noreply@anthropic.com> * fix(group): address @claude review on #795 Addresses the findings from the automated review on PR #795 (https://github.com/abhigyanpatwari/GitNexus/pull/795#issuecomment-4229770000 — posted by @magyargergo / claude-code Action run). ### Medium severity (reviewer flagged as blockers) - **bridge-db.ts `openBridgeDbReadOnly` bak recovery** — the `.bak` recovery path used bare `fsp.rename(bakPath, dbPath)`, which is exactly the scenario most likely to hit Windows EBUSY/EPERM (an interrupted writer still holding the handle for a few ms). Switched to `retryRename` for consistency with the rest of the file's Windows-safe rename path. - **bridge-db.ts `ensureBridgeSchema` error detection** — the inline `msg.includes('already exists')` substring match has been lifted into a named constant `LBUG_ALREADY_EXISTS_MSG` with a comment documenting the coupling to LadybugDB's error message wording and why we can't use `IF NOT EXISTS` (LadybugDB DDL doesn't support it) or typed errors (LadybugDB's JS driver doesn't expose error codes). Also tightened the `catch (err: any)` to `catch (err: unknown)`. - **bridge-db.ts `findContractNode` — extracted out of writeBridge** — the 35-line async closure living inside `writeBridge` has been lifted to three module-level functions: `createContractLookupIndex`, `indexContract`, and `findContractNode`. `findContractNode` is now a pure synchronous function taking a prebuilt index instead of doing its own DB queries. The `writeBridge` cross-link loop is now ~25 lines instead of ~100. - **bridge-db.ts `findContractNode` — N+1 query elimination** — the old inner-closure version issued up to 6 DB round-trips per cross-link (2 endpoints × up to 3 tiers of fallback queries). For a group with 1000 cross-links, that's up to 6000 DB queries just to resolve endpoints. The new version consults an in-memory `ContractLookupIndex` built incrementally as contracts are inserted (`indexContract` called AFTER each successful insert so failed inserts don't poison the index). Cross-link resolution is now O(1) per link instead of O(3) DB queries per link, with zero DB round-trips during the cross-link loop. ### Minor severity - **bridge-db.ts `queryBridge` empty-array guard** — if LadybugDB ever returns an empty `QueryResult[]` at the top level (shouldn't happen with single-statement calls, but driver contract isn't explicit), the old code would call `.getAll()` on `undefined` and crash with a confusing stack. Added an `unwrapQueryResult` helper that throws an explicit `'empty QueryResult array'` error instead, making a potential driver regression visible immediately. - **normalization.ts `contractRichness` weights** — added a block-level comment documenting the weight ordering (+3 for symbolUid, +2 for each symbol-identifying field, +1 for service tag or non-manifest origin) and explicitly noting that the absolute numbers don't matter, only the relative ordering. Matches the "comment for contributors" suggestion in the review. - **bridge-schema.ts `BRIDGE_SCHEMA_VERSION` migration comment** — added a 4-point contract explaining what bumping the constant means ("discard and re-sync" strategy for V1, no in-place migration yet, new migration logic should live in a separate `bridge-migrations.ts` module when it becomes necessary). - **test/unit/group/fixtures.ts** — extracted the `makeContract` helper previously copy-pasted between `bridge-db.test.ts` and `bridge-db-edge.test.ts` into a shared fixtures module. Both test files now import from `./fixtures.js`. Kept the scope minimal: fixtures is NOT a general-purpose factory module, just the shared baseline contract builder. ### New tests Added 9 pure-function unit tests for the now-extracted `findContractNode` in `bridge-db.test.ts`: - returns null on empty index - tier 1 (symbolUid) match, including repo-scope and role-scope isolation - tier 2 (filePath + symbolName) fallback when symbolUid is empty or mismatches - tier 3 (filePath only) when exactly one contract lives in the file, and refusal when multiple do - priority ordering when multiple tiers could resolve These are fully isolated — no DB, no temp directories, no native LadybugDB binding — so they run in <10ms total and are immediately trustworthy as a regression safety net. ### Deliberately deferred (reviewer marked as "fine for now") - `BridgeHandle._db` / `._conn` typing to `unknown` with casts in `bridge-db.ts` — reviewer's note: "The typing is fine for now." - Batch inserts via `UNWIND` — needs LadybugDB support confirmation, tracked as a follow-up; the per-item pattern remains. - `queryBridge` prepared-statement lifecycle — the current pattern (prepare → execute → GC) relies on LadybugDB's internals, worth verifying against their docs in a separate audit. ### Scope discipline (per `GUARDRAILS.md`) - Only files touched by this PR (`bridge-db.ts`, `bridge-schema.ts`, `normalization.ts`, both bridge test files, new `fixtures.ts`) — no drive-by refactors - No CI/release/security config changes - No secrets ### Test + typecheck status - `npx tsc --noEmit` clean - `bridge-db.test.ts`: added 9 `findContractNode` tests, all pass in isolation. The full-file run still hits the pre-existing native LadybugDB cleanup segfault that flakes the reported count — same as every prior commit on this branch, not a regression. - `bridge-db-edge.test.ts`: 4/4 pass - `matching.test.ts`: 28/28 pass - `types.test.ts`: 5/5 pass - `retryRename` tests (4/4) and `findContractNode` tests (9/9) verified in isolation via `-t` filter Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
4911201664
|
fix: map diff hunks to symbol line ranges in detect_changes (#779)
* fix: map diff hunks to symbol line ranges in detect_changes The detect_changes tool previously used `git diff --name-only` and picked the first 20 arbitrary symbols from each changed file. This produced false positives (unchanged symbols reported as modified) and false negatives (actually changed symbols dropped by the LIMIT). Now uses `git diff -U0` to get unified diff with hunk headers, parses the @@ line ranges, and queries for symbols whose [startLine, endLine] range overlaps the diff hunks. Only truly touched symbols are reported. Also fixed the CONTAINS path match to ENDS WITH to prevent cross-file false positives from substring matching. Fixes #758 * fix: address review feedback - variable shadowing, batch queries, tests - Rename `params` to `queryParams` in detectChanges hunk-mapping loop to avoid shadowing the outer method parameter - Replace N+1 per-symbol process lookup with a single batched query using WHERE n.id IN $ids (same pattern as impact BFS traversal) - Add unit tests for parseDiffHunks covering single/multi file, single/multi hunk, omitted count, pure-deletion, and empty input * style: fix prettier formatting in parse-diff-hunks test |
||
|
|
08541e2857
|
Fix HTTP client vs Express route detection and Spring interface attribution (#780)
* fix: correctly identify HTTP client calls vs Express routes in receiver extraction * fix: skip Spring route extraction for Feign client interfaces * fix: address review feedback - receiver walk edge case, regex anchoring, add tests * style: fix prettier formatting in route extractor and test files |
||
|
|
d9960c62bf
|
SM-19: Delete resolveCallTarget — replace with thin dispatcher (#770)
* Initial plan
* SM-19: Replace resolveCallTarget with thin dispatcher
Delete the monolithic resolveCallTarget function (~200 lines) and replace it
with a 15-line thin dispatcher that routes to resolveMemberCall,
resolveStaticCall, or resolveFreeCall. Extract module-alias resolution and
file-based member-call fallback into dedicated helper functions.
- resolveCallTarget body reduced from ~200 lines to ~15 lines
- Extract resolveModuleAliasedCall helper (Python/Ruby module imports)
- Extract resolveMemberCallByFile helper (trait dispatch, overload disambiguation)
- Extract singleCandidate helper (constructor alias fallback, name-based fallback)
- Update unit tests for new dispatcher semantics
- Update doc comments referencing deleted D0-D4 paths
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/469eac38-b0c0-4a26-a2ff-3eb06299730b
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* SM-19: Add singleCandidate tail fallback for member calls with unresolvable receiver type
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/469eac38-b0c0-4a26-a2ff-3eb06299730b
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix(SM-19): address all PR #770 review findings + fix CI
Fixes all 5 test failures (2 unit + 3 integration) and addresses 10
review findings from comment 4225312416.
Critical fix — singleCandidate null-route guard
The SM-19 dispatcher chained singleCandidate as an unconditional tail
fallback for member calls with receiverTypeName. This bypassed the
SM-10 R3 null-route contract: when the receiver type IS in the index
but file/owner filtering produced zero matches, the old code returned
null (genuine miss), but the new code fell through to singleCandidate
(false-positive CALLS edge).
Root cause: resolveMemberCallByFile returns null for two semantically
different reasons — (1) type not found in the index at all, and
(2) type found but no candidate matched after narrowing. The dispatcher
treated both as "try the next fallback." The old resolveCallTarget
exited the entire function on case 2.
Fix: after the scoped resolvers both return null, check whether the
receiver type resolves in the index. If it does (case 2), null-route
— the scoped resolvers made the right decision. If it doesn't (case 1,
e.g. PHP 'mixed', dynamic types), singleCandidate is the correct last
resort. ctx.resolve is cached so the check is free.
This fixes:
- Unit: no heritageMap null-route test (was getting 1 edge, expects 0)
- Integration: Rust c.trait_only() negative test
- Integration: 3 PHP heritage + alias tests (singleCandidate correctly
fires when the receiver type is not in the index)
Performance (findings #1, #2, #3)
- Thread pre-computed tiered result into resolveModuleAliasedCall via
new tieredOverride parameter — eliminates the duplicate ctx.resolve
call on every module-alias path.
- Add countCallableCandidates helper that short-circuits at threshold
without allocating an intermediate array — replaces the
filterCallableCandidates(...).length > 1 allocation in skipMember.
- resolveMemberCallByFile lookupCallableByName caching deferred to a
follow-up (finding #2) — the fix requires threading widenCache
through the file-scoped resolver which is a larger change.
Code quality (findings #4, #5)
- Remove dead code: redundant conditional in resolveMemberCallByFile
where both branches returned null.
- Move WidenCache type declaration from mid-file (between JSDoc blocks)
to adjacent to CONSTRUCTOR_TARGET_TYPES with other type declarations.
Formatting
- Applied prettier to call-processor.ts (CI format check was failing).
Verification
- tsc --noEmit clean
- 3188 unit tests pass (0 skipped real tests)
- 1766 resolver integration tests pass
- Zero regressions — all PHP, Rust, and no-heritageMap tests green
Review: https://github.com/abhigyanpatwari/GitNexus/pull/770#issuecomment-4225312416
* fix(SM-19): restore module-alias narrowing and constructor disambiguation
Codex adversarial review on PR #770 surfaced two silent regressions in the
SM-19 thin dispatcher:
Finding 1 [high] — Typed member calls bypassed module-alias narrowing.
When two homonym receiver types are both imported by the caller, the
import-scoped tier no longer narrows and the owner/file resolvers see
genuine ambiguity. The dispatcher null-routed silently, dropping valid
CALLS edges. Fix: consult `resolveModuleAliasedCall` at the top of the
typed-member branch so an active alias on `call.receiverName` picks the
aliased file before the generic resolvers run.
Finding 2 [medium] — Constructor dispatch lost overload disambiguation.
When `resolveStaticCall` bails (ambiguous or ownerless Constructor pool)
and the caller supplied `overloadHints` / `preComputedArgTypes`, the
branch fell straight through to `singleCandidate` — which also bails on
multiple same-arity survivors. Fix: between `resolveStaticCall` and
`singleCandidate`, run constructor-filtered overload disambiguation on
the tiered pool. Only engages when a narrowing signal is present;
preserves SM-10 R3 null-route for genuinely ambiguous cases.
Tests:
- call-processor.test.ts: 3 new dispatcher-level regression tests
covering real-homonym alias narrowing, constructor overload
disambiguation with `argTypes`, and null-route control
- symbol-table.test.ts: update `module alias homonyms` test which
previously codified the Finding 1 regression; now asserts resolution
to the aliased file's method
Verification: 3191 unit + 2398 integration tests pass; tsc --noEmit
clean; prettier clean.
* refactor(SM-19): address code review findings with clean-code pass
Code review on commit
|
||
|
|
100858f8c8
|
feat(SM-18): Delete lookupFuzzy, lookupFuzzyCallable, globalIndex, callableIndex (#769)
* 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>
|
||
|
|
e5dafce9f2
|
feat(SM-16): Restructure resolveUncached — replace lookupFuzzy data source for all tiers (#764)
* 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> |
||
|
|
ab956f113c
|
feat(SM-15): Wire BindingAccumulator into processCallsFromExtracted for cross-file return type propagation (#763)
* 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> |
||
|
|
6147579e54
|
Fix security issues and critical bugs found in code review (#709) | ||
|
|
4a1f912aee
|
feat(sm-14): add BindingAccumulator — collect TypeEnv outputs across files (#743) | ||
|
|
d09078925e
|
Extract resolveFreeCall from resolveCallTarget (SM-13) (#756)
* 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> |
||
|
|
338cb01ee0
|
[codex] fix large repository graph loading (#732)
* fix(web): stream large graph responses * fix(server): harden graph streaming * fix(ci): stabilize graph loading coverage --------- Co-authored-by: gfwangjie <gfwangjie@gf.com.cn> |
||
|
|
4450a14b98
|
feat(SM-12): Extract resolveStaticCall from resolveCallTarget (#754)
* 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> |
||
|
|
bb68cc1eb0
|
Extract resolveMemberCall from resolveCallTarget (SM-11) (#744)
* 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> |
||
|
|
d9ba9aa998
|
SM-10: Add MRO fast path before D2 fuzzy widening in resolveCallTarget (#741)
* 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> |
||
|
|
c19e76a4a3
|
feat(SM-9): Add lookupMethodByOwnerWithMRO using HeritageMap (#740)
* 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
|
||
|
|
b75e76d44a
|
feat(SM-8): Build HeritageMap from accumulated ExtractedHeritage[] (#739)
* 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> |
||
|
|
83b5bec293
|
[cli] Replace owner-filtered method lookups in type-env (#736)
* refactor(type-env): use owner method lookup * test(type-env): cover owner lookup edge cases * test(type-env): cover inherited overload ambiguity --------- Co-authored-by: 许恩宁 <xuenning@qiyi.com> |
||
|
|
3388ae16d7
|
[cli] Replace Phase P class checks with class lookup index (#734)
* refactor(call-processor): use class lookup index in phase p * test(call-processor): cover class lookup fallback --------- Co-authored-by: 许恩宁 <xuenning@qiyi.com> |
||
|
|
d784f591b2
|
[cli] Replace class-type fuzzy lookups in type-env.ts (#733)
* 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> |
||
|
|
0f43190543
|
feat(symbol-table): add fuzzy lookup counters (#708) | ||
|
|
fe87ff8f74
|
fix(symbol-table): index constructors in methodByOwner (#694) | ||
|
|
be2401061e
|
[cli] Add qualified class lookups to SymbolTable (#716) | ||
|
|
b73233d232
|
feat(symbol-table): add class name lookup index (#707) | ||
|
|
cb772b9e29
|
feat: lookupMethodByOwner index for O(1) cross-class chain resolution (#665)
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 |
||
|
|
10f8815639
|
fix(ignore): respect negation patterns in .gitnexusignore (#654)
* 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>
|
||
|
|
6ead5e5986
|
fix(setup): prefer global gitnexus binary over npx for MCP config (#653) | ||
|
|
5a7c0fdbb1
|
feat: same-arity overload disambiguation via type-hash suffix (#651) (#658)
* 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. |
||
|
|
0561d24efd
|
feat: METHOD_IMPLEMENTS edges, overload disambiguation, MethodExtractor unification (#574) (#642) | ||
|
|
153262304c
|
fix(mcp): unify stdout silencing to prevent embedder/pool-adapter conflicts (#645) | ||
|
|
63fc4c795f
|
feat: MethodExtractor configs for Python, PHP, Swift, Dart, Rust, Ruby (#624)
* 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> |
||
|
|
5c4fca21c3
|
Merge pull request #626 from ivkond/feat/intra-repo-service-tracking-clean
[group] Intra-repo service communication tracking |
||
|
|
dd0f5eed7d
|
feat(vue): Vue SFC support + destructured call result tracking (#604)
* 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>
|
||
|
|
e3d73a7aed
|
Java method reference (#622) | ||
|
|
255e3e79eb |
fix(group): address 4 HIGH-priority issues from PR #626 review
1. Path traversal via group name — add validateGroupName() with regex [a-zA-Z0-9][a-zA-Z0-9_-]*, called in getGroupDir (defense in depth) 2. gRPC proto regex can't handle nested braces — replace serviceRe with extractServiceBlocks() brace-depth counter (init depth=1, skip malformed protos) 3. Service boundary detector directory exclusions — add EXCLUDED_DIRS set (vendor, target, build, dist, __pycache__, .venv, venv, .tox, .mypy_cache, .gradle, .mvn, out, bin) replacing inline node_modules 4. Double-close of LadybugDB pools — remove blanket closeLbug() from cli/group.ts; sync.ts per-id cleanup is sufficient Tests: 22 new tests across 5 files. Full suite: 4706 passed, 0 failed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
4fed097abb |
feat(group): add sync pipeline, CLI, MCP tools, and monorepo fixture
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> |
||
|
|
4fa395f4b6 |
feat(group): add service boundary detection and contract extractors
Service communication detection for microservice monorepos: - ServiceBoundaryDetector: auto-detects service boundaries via markers (package.json, go.mod, Dockerfile, pom.xml, Cargo.toml, build.gradle, pyproject.toml, etc.) - HttpRouteExtractor: graph-assisted (Strategy A) with source-scan fallback (Strategy B) for Spring, Express, Laravel, FastAPI providers and fetch/axios consumers - GrpcExtractor: parses .proto files, detects Go/Java/Python/TS gRPC servers (RegisterXxxServer, @GrpcService, add_XxxServicer_to_server, @GrpcMethod) and clients (NewXxxClient, newBlockingStub, XxxStub) - TopicExtractor: Kafka (@KafkaListener, producer.send), RabbitMQ (@RabbitListener, channel.publish/consume), NATS (nc.Subscribe/Publish) across Java, Node, Go, and Python Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
52277247fe |
feat(group): add group infrastructure and contract matching
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> |
||
|
|
ba5de0bde4
|
feat(cpp): C/C++ MethodExtractor config with pure virtual detection (#617)
* feat(cpp): C/C++ MethodExtractor config with pure virtual detection (#572) - Pure virtual (= 0) detected as isAbstract via token scanning - virtual/final/override via hasKeyword and virtual_specifier children - Access specifier visibility via backward sibling walk (public:/private:/protected:) - Pointer/reference parameter types extracted correctly - Constructor and destructor support via declaration node type - Static detection via storage_class_specifier - 16 new tests covering all acceptance criteria * fix(cpp): isVirtual infers from override/final + out-of-class resolution - isVirtual returns true for override/final methods (C++ mandates these are virtual) - Add findClassNodeByQualifiedName to parse-worker: resolves Foo::bar() back to the Foo class declaration for method extractor enrichment - Handles pointer/ref return types, constructors, destructors - Integration test for virtual/static/constructor inline methods - 233 unit+integration tests pass, 97 C++ resolver tests pass * fix(cpp): address review — deep pointers, templates, unions, trailing returns - Fix extractParamName: recursive unwrap for int** ptr → "ptr" (not "**ptr") - Fix findFunctionDeclarator: recursive unwrap for multi-level pointer chains - Template methods: generic extractor unwraps template_declaration to inner node - union_specifier: added to typeDeclarationNodes, visibility defaults to public - Trailing return type: auto foo() -> T now extracts T instead of "auto" - Fix version comment: ^0.22.4 → ^0.23.4 to match package.json - 4 new tests: double pointer params, template methods, union methods, trailing returns * fix(cpp): template method visibility + union isTypeDeclaration test extractCppVisibility now walks from the template_declaration parent when the node is wrapped by a template, restoring correct access- specifier resolution for templated class methods. Also adds missing isTypeDeclaration assertion for union_specifier and expands the template method test with explicit visibility checks. * fix(cpp): address deep gap analysis review findings - findClassNodeByQualifiedName: recursive pointer/reference declarator unwrap, fixing out-of-class linking for deep pointer return types (e.g. int** Foo::bar()) - findClassNodeByQualifiedName: recurse into namespace_definition blocks so namespace-wrapped classes resolve correctly - Suppress = delete / = default special members from extraction via delete_method_clause / default_method_clause node detection - Update known-gaps: namespace-wrapped classes, const-overload collapse - Add tree-sitter-c version comment for consistency - toBeFalsy() → toBe(undefined) for precise isVirtual assertion - Tests: = delete, = default, = 0 non-regression, operator overloads, deep pointer return types, default visibility (class vs struct), multiple access specifier sections |
||
|
|
80d363f145
|
fix(wiki): Azure OpenAI compat and HTML viewer script injection (#618)
* fix(wiki): Azure OpenAI compat and HTML viewer script injection - Use max_completion_tokens instead of deprecated max_tokens for all models - Skip sending temperature for Azure provider (some models reject non-default values) - Simplify Azure interactive setup: endpoint + deployment + key (3 prompts instead of 7) - Escape </script> in embedded JSON to prevent premature script tag closure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(test): align wiki-llm-client test with max_completion_tokens change The test expected max_tokens for non-reasoning models, but the source now uses max_completion_tokens for all models since max_tokens is deprecated by newer OpenAI models. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Abhigyan Patwari <abhigyan@Abhigyans-MacBook-Air.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
12be2025f1
|
feat(ts,js): TypeScript/JavaScript MethodExtractor config (#588)
* feat(ts,js): MethodExtractor config for TypeScript and JavaScript (#570) Add per-language method extraction config following the established JVM and C# patterns. Shared config base mirrors the field extractor's typescript-javascript.ts pattern — TS-only node types are harmless no-ops for JS. Key features: - isAbstract for abstract class methods and interface methods - Parameter extraction with isOptional (?:, defaults) and isVariadic (...) - Decorator extraction from preceding body-level siblings - isAsync and isOverride detection - Visibility via accessibility_modifier two-pass pattern - Return type extraction unwrapping type_annotation * test(ts,js): add override, getter/setter, destructured param tests Address code review findings: - Add override method detection test - Add getter/setter extraction test - Add destructured parameter with type annotation test - Tighten constructor and private method assertions * refactor(ts,js): address code review findings - Replace O(M*N) decorator index scan with previousNamedSibling walk - Remove dead findVisibility 'modifiers' fallback (TS uses accessibility_modifier, not a modifiers wrapper) - Document call_signature/construct_signature as known gaps - Document that TS constructors are method_definition nodes - Remove unused findVisibility import * fix(ts,js): type guard before cast, add generator/computed/overload tests - Use type guard pattern (Set.has check before as-cast) in visibility extraction to ensure string is validated before narrowing - Add generator method test (*items()) — confirms extraction works - Add computed property name test ([Symbol.iterator]) — documents bracket-in-name behavior as intentional - Add class-level method overload test — verifies overload signatures + implementation are all extracted * fix(ts,js): detect #private methods as visibility 'private' ES2022 private class methods (#name) use private_property_identifier as their name node type. Detect this and return 'private' visibility instead of the default 'public'. * fix(ts,js): address review findings + close ingestion gaps - hasKeyword/findVisibility: skip name field child to prevent false positives on soft-keyword method names (e.g. `abstract()`, `static()`) - extractTsJsParameters: filter TS `this` parameter (compile-time only) - extractMethodSignature: mirror `this`-param skip in fallback path - tree-sitter queries: capture abstract_method_signature, method_signature, and private_property_identifier for TS; add private_property_identifier for JS - Remove dead childForFieldName('name') fallbacks and typeFromAnnotation fallback - Add 10+ unit tests, 4 integration tests through query pipeline * test(ts): update HAS_METHOD count for interface method_signature capture The new method_signature query now captures ILogger.log() as a Method node with a HAS_METHOD edge, increasing the expected count from 4 to 5. * fix(ts,js): address second review — async generator test, declare module gap - Add async generator method test (async *values() → isAsync: true) - Document declare module/global augmentation as known gap |
||
|
|
c72890d59d
|
feat(csharp): C# MethodExtractor config (#582)
* feat(csharp): add C# MethodExtractor config (#573) Add C# method extraction config mirroring the JVM pattern from PR #576. Wire csharpMethodConfig into the C# language provider and add 18 tests covering classes, interfaces, abstract classes, structs, records, constructors, params/out/ref/optional parameters, sealed methods, attributes, and visibility modifiers. * fix(csharp): add destructor, operator, conversion operator, and in-param support - Add destructor_declaration, operator_declaration, and conversion_operator_declaration to methodNodeTypes - Custom extractName for operators (e.g., "operator +", "implicit operator double") - Fix extractReturnType for operator declarations (use type field, not returns) - Add in modifier to parameter extraction (alongside out/ref) - Add 4 new tests: destructor, operator+, implicit conversion, in parameter * fix(csharp): add ref param test and document compound visibility limitation - Add test for ref parameter modifier (was only testing out) - Document that protected internal / private protected resolve to first modifier * feat(csharp): support compound visibilities (protected internal, private protected) - Add 'protected internal' and 'private protected' to FieldVisibility union - Detect compound modifiers in both C# method and field extractors via collectModifierTexts helper scanning adjacent modifier nodes - Add 2 tests for compound visibility detection * feat(csharp): primary constructors, virtual/override/async, primary fields Address all known limitations from review: - Primary constructor support (C# 12): add extractPrimaryConstructor to MethodExtractionConfig and extractPrimaryFields to FieldExtractionConfig. Record params become public readonly properties; class params become private captured fields. - Add isVirtual, isOverride, isAsync optional fields to MethodInfo, MethodExtractionConfig, NodeProperties, and parse-worker propagation. - Detect virtual/override/async modifiers in C# method config. - Move collectModifierTexts to shared helpers.ts (deduplicate). - Fix destructor name to ~ClassName (disambiguates from constructor). - Add expression-bodied method test. - 118 tests total across method + field extraction suites, all passing. * fix(csharp): review round 2 — annotations, record_struct, grammar pin - Fix primary constructor annotations: use [] instead of extracting class-level attributes (C# has no syntax for ctor-specific attributes) - Add record_struct_declaration to typeDeclarationNodes in both method and field extractors, CLASS_CONTAINER_TYPES, and isRecord visibility check - Pin tree-sitter-c-sharp version (^0.23.1) in params comment * fix(csharp): complete record_struct query + label mapping, sealed override test - Add record_struct_declaration capture patterns to tree-sitter-queries.ts (type definition + primary constructor) - Add record_struct_declaration → 'Struct' in CONTAINER_TYPE_TO_LABEL - Assert isOverride: true alongside isFinal in sealed override test * fix(csharp): record_struct label mismatch, add record struct + documented limitation tests - Fix record_struct_declaration query tag: @definition.struct (not @definition.record) to match CONTAINER_TYPE_TO_LABEL and prevent broken HAS_METHOD edges - Add 3 record struct tests: isTypeDeclaration, method extraction, primary constructor - Add documented limitation tests: partial method (isAbstract: false), generic type parameter stripping (name excludes <T>) * fix(csharp): remove record_struct_declaration — not a real tree-sitter node type tree-sitter-c-sharp 0.23.1 parses 'record struct' as record_declaration (absorbs the 'struct' keyword as an unnamed child token). The non-existent record_struct_declaration in queries caused TSQueryErrorNodeType, breaking ALL C# file processing. Remove from: tree-sitter-queries.ts, typeDeclarationNodes in both extractors, CLASS_CONTAINER_TYPES, and CONTAINER_TYPE_TO_LABEL. Record struct types are already handled via record_declaration. * feat(csharp): add isPartial support, filter targeted attributes, static ctor test - Add isPartial optional field to MethodInfo, MethodExtractionConfig, NodeProperties, and parse-worker propagation pipeline - Detect partial modifier in C# config — marks both declaration-only and implemented partial methods - Filter targeted attribute lists (e.g. [return: MarshalAs(...)]) in extractCSharpAnnotations — only untargeted attributes collected - Add static constructor test (isStatic: true, same name as class) - Add 3 partial method tests: declaration-only, with body, coexisting pair - Document record_struct/record_class as defensive dead code in export-detection.ts (grammar absorbs keywords into record_declaration) * fix(csharp): this param for extension methods, dedup visibility, test fixes - Handle this modifier on extension method parameters (type prefixed as 'this string', consistent with out/ref/in handling) - Deduplicate visibility logic in extractPrimaryConstructor — reuse csharpMethodConfig.extractVisibility instead of inline compound check - Fix record struct test title to reflect actual grammar behavior - Add conversion operator returnType assertion - Add extension method this parameter test * fix(csharp): primary constructor line points to param list, empty name guard - Use paramList.startPosition instead of ownerNode.startPosition for primary constructor line number (avoids methodInfoCache key collision) - Guard against empty param names from tree-sitter error recovery nodes |
||
|
|
313b13fade
|
feat(java,kotlin): MethodExtractor abstraction with per-language configs (#576) | ||
|
|
b03413dcf9
|
feat: added skip-agents-md cli flag (#517)
* feat: added skip-agents-md cli flag * fix: apply prettier formatting * feat: added skip-agents-md cli flag * fix: apply prettier formatting * feat: add skipAgentsMd option to skip AGENTS.md and CLAUDE.md updates * fixed bad merge |