mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-09 03:17:54 +00:00
167 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7ca7166b8e
|
fix(fastapi): apply APIRouter constructor prefixes (#2312)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
|
||
|
|
8bef64baa1
|
feat(ingestion/routes): give Route nodes a (method, url) identity (#2289) (#2302)
* feat(ingestion/routes): give Route nodes a (method, url) identity (#2289) Route node identity was URL-only, so a same-URL multi-verb pair (GET /x + POST /x) collapsed into a single node and the second verb's handler and execution flow were silently lost. Route identity is now (method, url) via routeNodeKey(method, url): a known, specific verb keys as "METHOD url", while a method-less route (filesystem routes — Next.js / Expo / PHP — and Laravel resource/apiResource) or a wildcard "*" route (e.g. Django function views) falls back to URL-only. The fallback is byte-identical to the previous URL-only ids, so only genuine declaration-style multi-verb routes split into separate nodes. The identity key is shared across the three phases that must agree on the Route node id: - routes phase: registry key + node id + handler-symbol lookup; the Route node still carries the bare URL as its display name. - call-processor: resolveRouteHandlerSymbols re-keyed by identity so each verb resolves its own handler; a verb-less fetch() consumer matches by URL and connects to every Route node at that URL (one per verb). - processes phase: ENTRY_POINT_OF targets the identity-keyed node id. Bumps INCREMENTAL_SCHEMA_VERSION 4 -> 5: persisted pre-v5 Route nodes use the old url-only ids, so an incremental top-up would strand them alongside new composite-keyed nodes — force a full re-analyze instead. Part of #2280. * fix(ingestion/routes): address PR #2302 review (P1/P2/P3) P1 — Schema v5 fast-path bypass (run-analyze.ts): Adds a schemaVersion-mismatch guard above the alreadyUpToDate early-return, mirroring the pdgModeMismatch slot. Without it, a same-commit re-analyze on a pre-v5 stamp returned alreadyUpToDate without ever reaching the isIncremental gate, defeating the v5 schema bump's migration intent. Regression test covers: analyze (stamps v5) → meta downgrade to v4 → same commit re-analyze must NOT early-return and meta restamps to v5. P2 — ENTRY_POINT_OF handler-aware linking (processes.ts): Pre-fix routesByFile fanned every same-file Route to every same-file process, cross-wiring same-file GET/POST handlers. Now reads handlerSymbolId off the Route graph node (the source of truth routes.ts stamps) into routesByHandlerId, with a routesWithoutHandlerByFile fallback — mirrors the Tool linking precedent 10 lines below. Two regression tests: weak form (only one handler has a process; sibling verb does not get spuriously attached) and strong form (both handlers form distinct processes; each Route links to exactly its own entryPoint, 2 edges not pre-fix 4). P2 — Roundtrip composite-id (route-{method,handler-symbol}-roundtrip): Both tests now seed the Route node with generateId('Route', routeNodeKey('POST', '/api/orders')) and run the Cypher MATCH against the composite id, exercising the literal-space-in-id through CSV→COPY→HANDLES_ROUTE_QUERY. A space-in-id escape regression would surface here instead of being silently swallowed by the extractor's catch. P3 — doc-drift + test if: - route-path.ts:4 — header updated to "(method, url) via routeNodeKey" - java.ts:684 — drop "Route nodes are URL-keyed"; #2289 closes that gap - manifest-extractor.ts:196 — explicit that Route node *id* is composite while route.name remains the bare URL - multi-verb-route-identity.test.ts:88 — forEachRelationship+if rewritten as a .filter().map() chain (no test-level conditional). New route-process-linking tests are also if-free. Validation: tsc clean, prettier clean, 9 touched suites / 43 tests pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(ingestion/routes): drop routes.ts re-export, fix CI fast-path tests Two follow-ups on PR #2302's CHANGES_REQUESTED review: 1. Drop `routes.ts` re-export of `normalizeExtractedRoutePath` / `normalizeRouteMethod` / `routeNodeKey` (per @magyargergo's inline comment at routes.ts:153 — the symbols already live in `route-extractors/route-path.ts` and consumers should import them from the source, not via a routes-phase indirection that was kept only as a compat shim during the #2289 refactor). Updated the two remaining callers (blade-template-routes / spring-route-extractor- parity tests) to import directly from `route-extractors/route-path.js`. `call-processor.ts` and `processes.ts` already import from the source. 2. Fix two `run-analyze.test.ts` fast-path tests that started failing on CI after the schema-version mismatch guard landed ( "creates .gitnexus/.gitignore on the already-up-to-date fast path" and "reports isPrimaryBranch false for an up-to-date non-primary branch"). The test fixtures hand-built a RepoMeta with NO schemaVersion field; with the guard now checking `existingMeta.schemaVersion !== INCREMENTAL_SCHEMA_VERSION`, that pre-versioning shape was treated as a mismatch and forced a rebuild, short-circuiting the fast path the tests exercise. Stamp the current schemaVersion on those fixtures so they reflect the post-#2289 meta shape production actually writes (`runFullAnalysis` always stamps the field on git repos — see meta save site). Validation: tsc clean, prettier clean, 11 touched suites / 80 tests pass. --------- Co-authored-by: henry <zhangwei2017@unipus.cn> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
9aa65ae3f8
|
feat: ✨ resolve Nuxt/Nitro auto-imports in TypeScript scope resolver (#2026)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* feat: ✨ resolve Nuxt/Nitro auto-imports in TypeScript scope resolver * fix: 🐛 skip self-referential edges in Nuxt auto-import emission * fix: 🐛 address Sourcery review -- gate Nitro scan on imports.d.ts and pre-index explicit imports * fix: scope Nuxt auto-import resolution * fix: address Nuxt auto-import review follow-ups * fix(ingestion): capture only LHS binding names in Nitro server-util exports The Nuxt server-util export scanner ran a declarator regex over the whole `export const …` right-hand side, so it registered RHS tokens as auto-import names: arrow-function parameters (`export const f = (event) => …` → `event`), object-literal keys (`export const c = { onError } ` → `onError`), and bare operands. It also dropped generic-typed declarators (`export const x: Map<a, b> = …`) because the type-annotation skip broke at the comma inside the generic. Both produced wrong/missing auto-import CALLS edges. Capture only the leading binding name of each top-level declarator via a depth-aware comma splitter (tracks (), [], {}, <>), skipping destructuring patterns. Nitro auto-imports only surface top-level binding names, so the RHS is never parsed. Adds unit coverage for the param/object-key/operand/generic and multi-declarator forms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU * fix(ingestion): stop Nitro server callers resolving client composables `getNuxtAutoImportEntry` fell back to the client composable map when a `server/api|routes|middleware` caller's name had no `server/utils` entry. But Nitro only auto-imports `server/utils/**` into the server context — app `composables/` are Vue-app-only — so that fallback minted CALLS/IMPORTS edges Nitro never creates (e.g. a server route "calling" a composable it cannot see without an explicit import). Server callers now resolve the server map only. Restructure the barrel-directory integration test to use a client caller (which legitimately auto-imports the composable) so `index.*` resolution stays covered, and add a negative assertion that `server/api/route.ts` emits no edge to `composables/*` while its real `server/utils` call still resolves. Unit test locks that a server caller does not fall back to a client-only name. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU * fix(ingestion): let unresolved explicit imports shadow Nuxt auto-imports The explicit-import suppression index only recorded import local names whose edge resolved to a file (`edge.targetFile !== null`). An explicit import from an unresolved external package — `import { useAuto } from '@vueuse/core'; useAuto()` — therefore escaped suppression, and the post-resolution hook emitted a spurious Nuxt auto-import CALLS edge for a name the file already imports explicitly. Record the local name regardless of whether the import resolved: an explicit import is authoritative shadowing intent. Adds an integration fixture importing from an external package and a (non-vacuous) assertion that it emits no nuxt edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU * fix(ingestion): let type-annotated params shadow Nuxt auto-imports hasLocalBindingInScopeChain only consulted scope.bindings, but type-annotated function parameters live in scope.typeBindings (the TS scope query records them as `@type-binding.parameter`, not `@declaration`). A parameter named like a composable therefore failed to suppress the auto-import, leaking a spurious CALLS edge. Also check scope.typeBindings for the name (same-file scopes only). typeBindings holds value-space binders' type facts (parameter annotations, `self`, variable annotations) and never a pure type that belongs to callable space, so this cannot over-suppress a real auto-import. Documents the residual: function-typed params (`p: () => void`), untyped params, destructured locals, and catch-clause vars are captured by neither map and still leak — closing that needs shared scope-query changes beyond this feature, left as a follow-up. Also adds a no-vacuous-pass guard to the shadowing/noise test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU * fix(ingestion): treat server/plugins and server/tasks as Nitro runtime isNitroServerRuntimeFile only matched server/api, server/routes, and server/middleware. Nitro also auto-imports server/utils into server/plugins and (since Nitro 2.6) server/tasks, so callers there were misrouted to the client composable map. Extend the prefix set (now a named constant) to cover them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU * fix(ingestion): merge duplicate JSDoc on collectImportsDts Two consecutive JSDoc blocks preceded collectImportsDts; tooling (IDEs, TypeDoc) attaches only the last one, silently dropping the descriptive block. Fold the "returns true when read" line into the descriptive block as a `@returns` tag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU * fix(ingestion): contain .nuxt/imports.d.ts source resolution to the repo A crafted `.nuxt/imports.d.ts` source such as `from '../../../../etc/passwd'` passes the project-local relative-path check but resolves outside the analyzed repo, causing fs.stat probes against arbitrary host paths. Skip any source that resolves outside repoRoot before touching the filesystem. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d27fd11c4b
|
fix(lang-kotlin): support fun interface extraction via tree-sitter-kotlin re-vendor (#2271)
* fix(lang-kotlin): support `fun interface` extraction via tree-sitter-kotlin re-vendor Vendored tree-sitter-kotlin@0.3.8 (fwcd) parsed `fun interface Foo` as an ERROR node and dropped the declaration plus its abstract method, so functional (SAM) interfaces were never extracted. The fix landed upstream in fwcd/tree-sitter-kotlin#169 (closes #87), merged to main 2025-04-25, but is not in any npm release (latest tag 0.3.8; main is the unreleased 0.4.0). Re-vendor the grammar from the unreleased fwcd main commit c8ac3d26: - refresh src/{parser.c,scanner.c,node-types.json,tree_sitter/*.h} and bindings/node/index.js; bump the vendor version 0.3.8 -> 0.4.0; record the pinned SHA + rationale in _vendoredBy and the vendor README. - switch the prebuild workflow's kotlin registry kind 'npm' -> 'vendored' (the fix is unreleased on npm, so prebuilds must build from the vendored C source, like swift/dart/proto). - add a hold to .github/vendored-grammars.json so the weekly auto-update monitor does not strict-inequality-revert the pin to the broken npm 0.3.8 (isNewer compares 0.3.8 != 0.4.0). - add 3 regression tests + a fixture asserting fun interfaces extract as Interface nodes with their abstract methods, and that plain-interface heritage still resolves. Existing KOTLIN_QUERIES need no change: the new grammar models `fun interface` as a class_declaration with an "interface" keyword child (plus an extra "fun" modifier child), which the existing interface rule already matches. Full Kotlin suite green against the new grammar (300 unit/cfg/resolver + 233 integration). NOTE: prebuilds/ are intentionally not in this commit. The version bump auto-triggers .github/workflows/build-tree-sitter-prebuilds.yml, which regenerates all 6 platform binaries from the vendored source in a separate PR. Until that lands, CI loads the committed 0.3.8 prebuild, so the new kotlin tests are red and the grammar change is inert at runtime. Merge the prebuild PR first or together. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ci): count kotlin's vendored hold as a 0.25-readiness blocker The kotlin `hold` added in the previous commit makes the tree-sitter upgrade-readiness report count it as a blocker — the report treats every held vendored grammar as frozen below a runtime upgrade (same as the intentionally-pinned tree-sitter-cpp and the ABI-held tree-sitter-c), "in-range ABI or not". So the report's blocker count goes 2 -> 3. Update the hardcoded count in test_issue_update_summary_regex_matches_current_report (and the _render_report docstring) accordingly — exactly as that test instructs: "if a grammar is added/removed or a pin/hold changes, update the expected counts". kotlin's ABI (14) is in range; the hold is what flags it, with the reason recorded in .github/vendored-grammars.json. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ci): refresh kotlin baselines for the grammar bump Two committed baselines pinned the pre-bump kotlin state and broke when the grammar was re-vendored (0.3.8 -> 0.4.0): - cli-commands.test.ts pinned the vendored kotlin package version at 0.3.8 -> update to 0.4.0. - bench/scope-capture/baselines.json: the new kotlin-fun-interface fixture joins the lang-resolution/kotlin-* corpus AND the new grammar parses `fun interface` as a class_declaration (not an ERROR node), so the capture fingerprint drifts. Rebaselined to the NEW grammar's fingerprint (verified by building the vendored parser.c against tree-sitter@0.21.1 and running measure.mjs --check); scaling ~0.83 (linear). Like the fun-interface integration tests, the scope-capture --check passes only once the regenerated prebuilds land; until then CI loads the committed 0.3.8 binary, so it stays red. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(prebuilds): rebuild + commit grammar prebuilds into the PR on vendored-source change build-tree-sitter-prebuilds.yml previously rebuilt a grammar's native prebuilds only when its package.json VERSION bumped, and delivered them via a separate bot PR. Now any change to the vendored grammar source re-cuts the prebuilds and they ride into the same PR. - Trigger on any build-affecting change under gitnexus/vendor/tree-sitter-*/** (parser.c, grammar.js, binding.gyp, scanner, bindings), not just version bumps. The prebuilds/ subtree is negated in the paths filter AND excluded from the guard's source diff, so the bot's own prebuild commit can never retrigger the workflow (no build -> commit -> build loop). - The guard builds a grammar when its recorded version changed OR its vendored source changed vs the PR base. - Same-repo PRs get the rebuilt prebuilds committed straight onto their own head branch (included in the SAME PR) via a non-force push that only adds a commit on top of head. Manual dispatch still opens a fresh chore/ PR; fork PRs stay artifacts-only (a bot cannot push into a fork branch). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(prebuilds): deliver rebuilt prebuilds to fork PRs via a trusted workflow_run stage A fork PR's producer run has a read-only token and no secrets, so it can build and validate the prebuilds but can't commit them. Add the safe two-stage handoff that mirrors the pr-autofix producer/publish split. - build-tree-sitter-prebuilds.yml (untrusted producer): on a fork PR, upload a pr-meta artifact (schema, pr_number, head_sha, head_ref, head_repo, base_repo) alongside the prebuild artifacts. Values flow through env + jq, never interpolated into a shell. - commit-fork-prebuilds.yml (trusted, workflow_run): downloads ONLY the artifacts (never executes fork code — it checks out the pinned HEAD SHA solely to add files), allowlist-validates every metadata field, cross-checks identity against the workflow_run authority (head_sha / head_repo / pr_number, via commits/{sha}/pulls for forks), then pushes the prebuilds onto the fork head branch with --force-with-lease + http.extraheader auth. No PAT: this works when the contributor left "Allow edits by maintainers" on; on push failure it posts a sticky comment telling them to enable it or commit the downloaded artifacts. zizmor: allowlist commit-fork-prebuilds.yml's workflow_run dangerous-trigger with the documented mitigation, matching the existing ci-report / pr-autofix entries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(vendor): rebuild tree-sitter-kotlin prebuilds for the re-vendored fun-interface grammar The fun-interface re-vendor changed vendor/tree-sitter-kotlin source but left main's old (0.3.8) prebuilds in place, so all 6 platform binaries were stale relative to the new parser. Replace them with the freshly cross-built + ABI-validated binaries from build-tree-sitter-prebuilds run 28010841458 — each .node was require()-loaded and parsed a snippet on its target platform-arch before upload. This is the manual equivalent of the commit-fork-prebuilds.yml delivery, which can't run for this fork PR until it lands on main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(lang-kotlin): read extension-function receiverType from the re-vendored grammar's `receiver` field The fun-interface re-vendor changed the kotlin AST: an extension function's receiver is now a `receiver_type` exposed via a named `receiver` field, where the old grammar emitted a bare user_type before the name. extractReceiverType only matched the old shape, so receiverType came back null (method-extraction.test.ts > Kotlin MethodExtractor > extracts receiverType). Prefer the `receiver` field (unwrapping it), and keep the old child-scan — now also recognizing `receiver_type` — as a fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
dbd4e1c9fb
|
feat(group): Support Django route extraction for multi-repo (#1836)
* [+] Add django route discovery to create cross-link for multi-repo * [+] Update ingestion * [~] Fix bugs and abstraction violation * feat(python-http): add keyword url= and variable propagation for consumer detection - Add REQUESTS_KEYWORD_URL_PATTERNS for requests.get(url='...') keyword args - Add WRAPPER_URI_PATTERNS for generic wrapper.fetch(uri='...') calls - Add WRAPPER_URI_VAR_PATTERNS + buildLocalStringMap for uri=variable propagation - Add LOCAL_STRING_ASSIGNMENTS to track uri='...' assignments - Wire both direct-string and variable-propagation loops in scan() - Add normalizeConsumerPath() helper Note: Automatic cross-link detection remains limited for runtime-computed URLs (URLs built via .format(), string concat, or module constants). Manual manifest links needed for known cross-repo contracts. * [+] add extract uri and url keywork pattern for request http * feat(python-http): add variable propagation for uri=/url= consumer patterns Re-add LOCAL_STRING_ASSIGNMENTS, WRAPPER_URI_VAR_PATTERNS, buildLocalStringMap(), and normalizeConsumerPath() lost during cherry-pick merge of upstream keyword-URL commit. Together with the upstream WRAPPER_URI_PATTERNS and REQUESTS_KEYWORD_URL_PATTERNS, we now detect: - requests.get(url='literal') keyword args - wrapper.fetch(uri='literal') keyword args - wrapper.fetch(uri=variable) where variable was assigned a string literal * fix(group): discover Django roots relative to manage.py dir + multi-project (#1836 R1) A Django project not at the repo root (e.g. backend/manage.py) discovered zero routes: the settings module path was resolved repo-root-relative only, so backend/myproj/settings.py was never found and discovery returned null. Resolve settings, star-imported base settings, ROOT_URLCONF, and the root urls.py against the manage.py's own directory first, then the repo root (resolvedSettingsPath is now project-dir-aware so relative imports anchor correctly). Iterate every manage.py so a monorepo with several Django projects yields each project's root — the provider hook becomes plural (discoverRootRouteFiles → string[]) and the main-thread pass loops over all roots (inner-scoped continues, parser hoisted once per language). Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(group): remove dead code in Django root discovery (#1836 R9) - Collapse the identical if/else in extractStarImports to one push. - Drop the unreachable baseModule.startsWith('.') branch (baseModule is always a resolved slash-path or a bare absolute module — never dot-prefixed). - Import DjangoFileReader from django.ts instead of re-declaring the type. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): walk Django includes once per prefix, not per file (#1836 R2) The include() recursion guard was keyed on file path alone and shared across the whole walk, so a urlconf included under two prefixes (a "diamond" — the same app mounted at /v1/ and /v2/) emitted routes for only the first mount. Key the guard on (resolvedFilePath, accumulatedPrefix) at all three sites (function entry, path()-wrapped include, bare include) so a file reached under two distinct prefixes is walked once per prefix while a genuine cycle (same file + same prefix) still terminates — null/'' prefixes collapse to one key so a no-prefix re-entry is treated as a cycle. MAX_INCLUDE_DEPTH remains the backstop. Adds diamond + self-include-cycle tests. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): extract Django routes from non-list urlpatterns (#1836 R3) findUrlpatternsLists only accepted a list-literal RHS, so common shapes yielded zero routes: concatenation (urlpatterns = a + b), wrapper calls (format_suffix_patterns([...]), i18n_patterns, staticfiles_urlpatterns), and tuples. Add collectUrlpatternContainers to descend binary_operator operands, known wrapper-call list arguments, and tuples. Inherently-dynamic forms (DRF router.urls, comprehensions, bare names) still yield nothing but now emit a debug log so the silent-zero case is observable rather than mysterious. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(group): thread Django parser explicitly, drop module singleton (#1836 R4) extractDjangoRoutes relied on a module-level _djangoParser set via setDjangoParser before each call — hidden state that would break if a second language ever used the include re-parse path, and an easy-to-forget contract. Pass the tree-sitter parser as an explicit parameter of extractDjangoRoutes (the extractRoutes provider hook already receives it) and delete the global plus its setter. The Python provider wires it directly; tests pass the parser in place of the removed setDjangoParser() call. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): isolate a throwing extractRoutes in the cross-file route pass (#1836 R5) The main-thread cross-file route pass called provider.extractRoutes without a guard, so a throw (e.g. a future grammar edge case in the include() walk) would propagate out of the parse phase and abort the entire analyze — unlike the worker, which isolates per-file failures. Wrap the per-root extractRoutes call in try/catch that logs a warning and continues to the next root. Export extractCrossFileRoutes and add a unit test driving a stub provider whose extractRoutes throws, asserting the pass returns [] and does not propagate. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(ingestion): bucket only route-capable languages in cross-file pass (#1836 R6) extractCrossFileRoutes runs in the deferred band on every analyze (incl. warm all-cache-hit runs). It now derives the set of languages whose provider exposes the cross-file route hooks once, returns early if none do, and buckets only those languages' paths — so a non-framework repo no longer pays to bucket the languages it doesn't use here. Route results are intentionally not persisted across runs, so a Django repo still re-derives its routes each analyze; documented inline that cross-run route caching is a deliberate follow-up rather than implemented here. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(group): prettier-format http-patterns/python.ts (#1836 R7) The file was not formatted to the root .prettierrc (the consumer-path normalizer used single-line try/catch and method chains), so the CI quality/format check (`prettier --check .`) failed. Reflow only — no logic change (`git diff -w` confines the change to normalizeConsumerPath's layout). Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): dedup Python URI detections by byte offset, not line arithmetic (#1836 R8) The wrapper-URI dedup key was lineNum*1000+methodRow, which can collide for distinct calls in files over 1000 lines (carry into the row term) and can fail to dedup a genuine duplicate when a node straddles a line boundary. Key on node byte offsets (`${pathNode.startIndex}:${methodNode.startIndex}`), matching the sibling seenVarDetections dedup a few lines below. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ingestion): end-to-end Django cross-file route extraction (#1836 R10) Adds an integration test that runs runPipelineFromRepo against a Django fixture whose project lives under backend/, asserting the resulting Route graph nodes (/health, /api/items, /api/items/<int:pk>). This exercises the previously-untested main-thread orchestration glue (discovery → parse → extractRoutes → allExtractedRoutes → Route nodes) and, because the project is in a subdirectory, regresses the subdir-discovery fix (R1) — a repo-root-only resolver would discover nothing and emit zero Route nodes. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): anchor Django include() resolution at the project root (#1836 review F1) resolveIncludedFile tried the bare repo-root candidate (app/urls.py) before the project-relative one, so in a monorepo with both a repo-root app/ and a backend/ Django project that also has an app/, include('app.urls') from the backend project resolved to the WRONG service's routes. Probe up-tree from the root urls.py for the nearest manage.py (the Django project root / sys.path entry) and try that-anchored candidate first. Absolute module paths like `app.urls` now resolve to <projectRoot>/app/urls.py unambiguously. When no manage.py is reachable (e.g. unit tests with a urls-only reader) the prior strategy order is preserved. Adds a monorepo wrong-app test. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): drop bogus Django provider source-scan, use graph routes (#1836 review F2) The DJANGO_PATH_PATTERNS / DJANGO_URL_PATTERNS source scan emitted an HTTP provider contract for every path()/re_path()/url() string literal, without checking it was inside urlpatterns, without skipping include() mount points, and without composing the include() prefix across files. For `path('api/', include('app.urls'))` + child `path('items/', view)` it emitted providers for `/api` (a mount, not a route) and `/items` (un-prefixed) — which survived the exact-contract-ID dedup alongside the correct graph route `/api/items`, polluting cross-repo matching with false providers. Remove the Django provider patterns and their scan blocks. Django provider contracts come from the graph Route nodes, which the ingestion route extractor builds with includes already composed (and now correctly, per the other fixes). Python HTTP *consumer* patterns (requests/wrapper) are unaffected. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): match method-agnostic Django providers to any-method consumers (#1836 review F3) Django function views are method-agnostic, so extractDjangoRoutes emits httpMethod '*'. That '*' was dropped by normalizeRouteMethod and then defaulted to GET by the contract extractor, while the matcher only expanded wildcard *consumers* — so a `POST /api/items` consumer never matched the Django provider that was silently narrowed to GET. - routes.ts: preserve '*' as a method-agnostic marker on the Route node, so the contract layer emits a wildcard provider (http::*::path) instead of GET. - matching.ts: make findMatchingKeys symmetric — a specific-method consumer now matches an exact-method provider OR a wildcard (http::*::) provider on the same path, mirroring the existing wildcard-consumer expansion. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Dinh Huy <huynd86@fpt.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fff01189b1
|
fix(cpp-hooks): handle pack-base comments and missing hook overrides (#2247) | ||
|
|
72876ab69a
|
fix(cpp): rank homogeneous braced-init overloads (#2214)
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
|
||
|
|
e26002c37a
|
fix(cpp): suppress deleted overload winners (#2094)
* fix(cpp): suppress deleted overload winners * test(cpp): update scope capture fingerprint --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
ae5ec94fd9
|
fix: stop impact()/route_map under-reporting blast radius (#2129, #1858, #1589/#1852) (#2136)
* fix(query): stop impact()/context() under-reporting blast radius (#2129, #1858) Two read-side fixes to the "run impact before editing" safety workflow, both about the tools rendering "I could not give a single confident answer" as "no impact" — the most dangerous failure mode for a refactor-safety tool. #2129 — ambiguous resolution no longer hides a real caller behind a bare `impactedCount: 0`. When a bare name collides with several symbols, the resolver returns `ambiguous`; previously the payload carried a flat `impactedCount: 0`, so the real caller (which calls a *different* same-name node) was invisible unless the user already knew to disambiguate. The ambiguous branch now runs a bounded, summary-only BFS per candidate (capped at 6) and surfaces each candidate's true count plus the top-level `maxImpactedCount` / `maxRisk`, ranked most-impactful-first. `risk` stays `UNKNOWN` (ambiguity must not read as "safe"), `impactedCount` stays 0 (no single resolved symbol). The BFS and edge storage are unchanged — an empirical repro confirmed they are correct; the bug was purely in how the ambiguous case reported. Disambiguation by uid still returns the exact result. #1858 — impact()/context() now carry an additive `epistemic` field. When the queried symbol sits on an interface / indirection boundary (it implements or extends an interface, or is one) whose consumers bind via a DI container or dynamic dispatch, those callers are not traced to the concrete symbol, so the count is a lower bound. The result is annotated `epistemic: 'lower-bound'` with a human-readable `boundaries[]` note; a fully resolved leaf stays `epistemic: 'exact'`. Aligned to the surviving numeric confidence model (the 0.85 IMPACT_RELATION_CONFIDENCE heritage floor), not the long-deleted TIER_CONFIDENCE enum. Purely additive — no existing field or count changes. Tests: impact-ambiguous-blast-radius (per-candidate surfacing + uid disambiguation) and impact-epistemic-lower-bound (interface boundary → lower-bound, resolved leaf → exact, context parity). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(routes): configurable fetch wrappers + faster consumer scan (#1589/#1852) Closes the residual gap behind the now-merged #1852 (which fixed #1589): the fetch-wrapper consumer scan only traced wrappers the parse phase auto-detected as calling the bare global `fetch()`. A wrapper built on axios / a custom client, or one named outside the built-in convention, was invisible — route_map silently returned `consumers: []` (the exact "named outside convention → silent zero" hole #1858 calls out as needing a backstop). - Configurable wrappers: `.gitnexusrc` gains a `fetchWrappers: [...]` list (validated as identifier/member names, de-duped, capped, regex-safe), threaded AnalyzeOptions → PipelineOptions → routes phase. Configured names are unioned with the auto-detected ones; configured names alone now trigger the scan even when nothing was auto-detected. - Perf (F3 from #1852's review): the cross-file scan built one RegExp per (file × wrapper) — O(files × wrappers). It now builds a single alternation regex per file (O(files)) and reuses file contents already read for handler extraction instead of re-reading them. Tests: configurable-fetch-wrapper (axios-based `doRequest` wrapper — invisible without config, traced with it) + .gitnexusrc `fetchWrappers` validation cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(review): harden the under-reporting fixes after adversarial review Addresses findings from a reviewer-swarm pass over the two prior commits: - CLI text false-safe (major): `formatImpactResult` (eval-server.ts) had no ambiguous branch, so `gitnexus impact <colliding-name>` printed "No dependencies found. This symbol appears isolated." for an ambiguous target — the exact false-safe #2129 exists to kill, defeating the JSON-layer fix at the text surface. Added an ambiguous branch (per-candidate blast radius + maxImpactedCount/maxRisk) and a lower-bound branch for both the zero-count and non-zero paths, mirroring the context formatter. Covered by new unit tests. - Group fan-out dead work (major): impactByUid now passes skipEpistemic:true — the group cross-impact fan-out consumes only byDepth, so computing the #1858 boundary per neighbor was wasted round-trips on the highest-volume path. - Ambiguous all-UNKNOWN risk (minor): if every per-candidate probe fails, maxRisk now reports 'UNKNOWN' instead of falling to the 'LOW' seed (which would read as "safe"). - Candidate-probe cost (minor): the per-candidate summary BFS now sets skipEnrichment:true, bypassing the process/module aggregation passes it does not use. - Epistemic latency (minor): computeEpistemicBoundary now runs concurrently with the impact BFS instead of as a trailing serial round-trip. - Wrapper over-match (minor): the consumer-scan regex uses a `(?<![.\w$])` lookbehind instead of `\b`, so a bare configured name like `get` matches the free call `get('/x')` but not a member access `client.get(` (and `apiFetch` no longer matches `myApiFetch`). - Boundary wording (nit): correct article ("a class" vs "an interface") and singular/plural ("1 implementation"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(lint): drop unused describe import in new impact tests The withTestLbugDB harness wraps describe internally, so the explicit describe import was unused — unused-imports/no-unused-imports is an error (not a warning) in the root eslint config, failing quality/lint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(query): flag partialProbe when an ambiguous candidate probe fails (#2129 review F1) The ambiguous-impact branch hoists maxRisk/maxImpactedCount so a colliding name can't read as "isolated". But if a per-candidate BFS throws (e.g. DB pool contention during the ≤6-way fan-out), it was recorded as risk:'UNKNOWN', impactedCount:0 and silently masked by any benign sibling success — maxRisk reduced to the benign tier and maxImpactedCount reflected only successful probes. Track probeFailed and surface partialProbe:true (additive, intentionally distinct from the traversal-interrupted `partial` flag); formatImpactResult prints a lower-bound warning. Covered by a formatter unit test (a natural in-harness probe throw is unreachable — _runImpactBFS is fully self-catching under summaryOnly+skipEpistemic+ skipEnrichment). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(query): report the full match count when ambiguous candidates are truncated (#2129 review F11) The ambiguous candidate list is capped at AMBIGUOUS_MAX_CANDIDATES (6), but the CLI headline read the truncated `candidates[]` length — so a name matching 9 symbols printed "6 symbols share this name" while the JSON message stated the true count. Add an additive `totalCandidates` field carrying the full match count, include a "showing N of M" clause in the message when truncated, and have formatImpactResult report the full count. Covered by formatter unit tests for the truncated and non-truncated cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(query): run context() epistemic probe concurrently with methodMetadata (#1858 review F2) impact() overlaps the #1858 boundary probe with its BFS, but _contextImpl awaited computeEpistemicBoundary serially after every other query. Start the probe right after `symKind` is known (the earliest point it can — symKind depends on the incoming/outgoing round-trips) so it runs concurrently with the methodMetadata fetch, and await it at result assembly. Output is unchanged (covered by the existing epistemic context() tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(query): flag a leaf interface as lower-bound in context() (#1858 review F3) context() passed `symKind` to computeEpistemicBoundary, but symKind collapses a single-resolved Interface to 'Class' (resolvedLabel is '' on the single-candidate path), so the `symType === 'Interface'` self-boundary branch never fired and a directly-queried leaf interface (implements nothing, but consumed) was under-reported as 'exact'. Pass an interface-preserving type (`resolvedLabel || sym.type || symKind`) instead — enrichCandidateLabels runs before the single-candidate early return and patches sym.type to 'Interface', mirroring impact()'s derivation. impact() was already unaffected. Covered by a new context()-on-a-leaf-interface test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(query): hoist epistemic relation-type lists + add USES to the allowlist (#1858/#2129 review F4, F5) F4: promote computeEpistemicBoundary's function-local heritage/consumer relation-type lists to module-level readonly constants (EPISTEMIC_HERITAGE_RELATION_TYPES / EPISTEMIC_CONSUMER_RELATION_TYPES) next to VALID_RELATION_TYPES / IMPACT_RELATION_CONFIDENCE, so a future heritage edge type is visible to the probe. Kept as arrays (not Sets) because they bind as Cypher params. F5 (latent bug): USES is emitted (emit-references.ts) and already in the default impact relTypes + context() queries, but was missing from VALID_RELATION_TYPES — so impact({relationTypes:['USES']}) filtered to [] and silently ran the full default traversal. Add it (0.5 confidence fallback, matching FETCHES/WRAPS). Updates the security.test.ts allowlist assertions (size 15→16, USES now valid). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(query): document the _runImpactBFS enrichment skip-flag composition (#1858/#2129 review F6) The three skip-flags (skipPerSymbolEnrichment / skipEpistemic / skipEnrichment) suppress distinct sub-phases and compose implicitly. Add a JSDoc block at the opts type listing what each suppresses, the three real call patterns, and the key interaction (skipEnrichment makes skipPerSymbolEnrichment a no-op). Comment-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(cli): genericize the shared string-array validation messages (#1589/#1852 review F7) The shared `string-array` ValueKind hardcoded fetch-wrapper phrasing in three messages (non-array, identifier-shape, empty-list). Since `source` already names the config key, genericize all three so the shared normalizer carries no fetchWrappers coupling — a future string-array config key gets sensible errors. Test assertions updated to the new wording. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(query): type the ambiguous candidate summary + epistemicPromise (#1858/#2129 review F8) The ambiguous per-candidate summary was read through `any`, so a rename of _runImpactBFS's return fields would silently zero candidate counts. Name the read shape ({impactedCount, risk, summary?.direct}) at the narrowing site, and type epistemicPromise as the optional-epistemic union (the skip case's `{}` subtype) — keeping computeEpistemicBoundary's own return precise (epistemic required). Type-only; no runtime change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(routes): trust validated fetchWrappers config, drop redundant re-filter (#1589/#1852 review F9) `ctx.options.fetchWrappers` is already trimmed/shape-validated/de-duped/capped in analyze-config.ts, so the routes-phase re-trim/re-typeof pre-pass was redundant. Pass it straight through; the single Set-construction filter remains to guard the auto-detected functionName values (which don't pass through analyze-config). No behavior change — covered by the existing fetch-wrapper route suites. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(routes): make the wrapper-call boundary Unicode-aware (#1852 review F10) The consumer-scan lookbehind used ASCII `\w`, so a configured bare wrapper name preceded by a non-ASCII identifier character (`caféget('/x')`) satisfied the boundary and produced a spurious FETCHES edge. Switch to the `u` flag with Unicode property classes (`(?<![.\p{L}\p{N}_$])`). Covered by a fixture consumer (`cafédoRequest('/api/things')`) asserting no spurious edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(routes): count wrapper-scan line numbers incrementally (#1852 review F12) The wrapper consumer scan computed each match's line number via content.substring(0, match.index).split('\n').length — an O(matchIndex) allocation per match. Matches arrive in ascending index, so accumulate newlines with a running counter instead. 1-based line numbers are byte-identical (covered by the existing fetch-wrapper route suites). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(test): keep the #1858 epistemic probe from skewing the impact-pagination mock The impact-pagination mock counts every query containing `r.type IN` as a BFS depth level. Once the #1858 epistemic boundary probe was parallelized with the BFS (it fires `MATCH (x)-[r]->(iface) ... r.type IN $heritage` before the frontier loop), that query was miscounted as depth-1, shifting the real depths so multi-depth impactedCount read 50 instead of 200. Short-circuit the epistemic queries (uniquely aliased `iface`) to empty in both mock setups so only frontier queries count. Test-only; production is unaffected (the epistemic query is a separate real query there). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
36ca096e75
|
feat(ingestion): Java Spring route annotation → Route node extraction (#2078)
* feat(ingestion): add Java Spring route annotation → Route node extraction
Previously, GitNexus only supported Route node generation for JS/TS
ecosystems (Express, Next.js, Fastify, etc.) and Python (FastAPI, Flask).
Java Spring's annotation-based routing (@RequestMapping, @GetMapping,
@PostMapping, etc.) was only supported at the group contract layer
(http-patterns/java.ts) for cross-repo matching, but NOT at the
ingestion layer for generating graph Route nodes.
This commit adds ingestion-layer support:
1. JAVA_QUERIES (tree-sitter-queries.ts):
- Added method-level annotation captures (@GetMapping, @PostMapping,
@PutMapping, @DeleteMapping, @PatchMapping) → @decorator captures
- Added class-level @RequestMapping → @decorator capture (prefix)
- Supports both positional ("/path") and named (path="/path",
value="/path") annotation argument forms
2. parse-worker.ts:
- Java class-level @RequestMapping is detected and stored as a prefix
(not pushed as a standalone Route)
- After per-file capture processing, the prefix is applied to all
method-level routes in the same file via the existing
ExtractedDecoratorRoute.prefix field
- The routes phase (normalizeExtractedRoutePath) handles the prefix
joining, producing final URLs like /api/users/list
3. Tests:
- Unit test (worker-backed): 4 cases covering prefix joining,
bare routes, class-level exclusion, multi-file isolation
- Integration test (full pipeline): 6 cases covering end-to-end
Route node + HANDLES_ROUTE edge generation
Closes the feature gap where `route_map`, `shape_check`, and
`api_impact` MCP tools returned empty results for Java Spring projects.
* chore(autofix): apply prettier + eslint fixes via /autofix command
* fix: address review findings — extract spring.ts module, fix PatchMapping, multi-class support
Addresses all P2 findings from tri-review:
1. **Architecture**: Extracted Spring route logic from parse-worker.ts into
a dedicated `route-extractors/spring.ts` module (matching the pattern
of `laravel.ts` and `fastapi-router-bindings.ts`). parse-worker now
has a single dispatch line — no language-specific logic inline.
2. **PatchMapping bug**: Added `'PatchMapping'` to `ROUTE_DECORATOR_NAMES`
(was silently dropped before).
3. **Multi-class bug**: The new `extractSpringRoutes` walks each class
declaration independently with its own prefix — no more single-scalar
`javaClassPrefix` last-wins issue.
4. **Test hygiene**: Unit tests now import `extractSpringRoutes` directly
(no dist build / worker pool dependency). Tests run in all tiers.
5. **Removed JAVA_QUERIES decorator patterns**: The Spring extractor does
its own AST walk, so the tree-sitter query captures for Java annotations
are no longer needed (avoids duplicate route emission).
Additional test coverage:
- Multi-class in one file with independent prefixes
- @PatchMapping support
- Named annotation args (path= and value=) on class-level @RequestMapping
* refactor: move Spring route extraction to LanguageProvider hook
Addresses the second review comment: instead of an inline
`if (language === SupportedLanguages.Java)` dispatch in parse-worker,
the Spring route extraction is now wired through a new optional
`extractDecoratorRoutes` hook on LanguageProviderConfig.
- Added `extractDecoratorRoutes` to LanguageProviderConfig interface
- Java provider registers `extractSpringRoutes` as its implementation
- parse-worker calls `provider.extractDecoratorRoutes?.()` generically
- Removed direct import of spring.ts from parse-worker
This keeps parse-worker fully language-agnostic — no language names
appear in the dispatch path for route extraction.
* refactor: rewrite spring.ts with tree-sitter captures, fix inline imports
Addresses all 4 inline review comments:
1. Rewrote spring.ts to use a single predicate-free Parser.Query
(same pattern as group-layer JAVA_ROUTE_ANNOTATION_PATTERNS).
Two-phase loop: first pass collects class prefixes by node.id,
second pass resolves method routes via findEnclosingClass.
No more manual DFS / recursion.
2-3. Moved inline import(...) type references in language-provider.ts
to proper top-level imports (Parser, ExtractedDecoratorRoute).
4. Covered by #1 — recursive helpers removed entirely.
Added 3 extra test cases: non-route named args filtering,
prefix isolation across mixed classes, line number accuracy.
* refactor: extract shared Spring route primitives + add parity test
Addresses review follow-up on #2078:
- Extract the primitives shared by the ingestion (route-extractors/spring.ts)
and group (http-patterns/java.ts) Spring extractors into a new
route-extractors/spring-shared.ts: METHOD_ANNOTATION_TO_HTTP,
findEnclosingClass, isRouteMemberKey, and a safe unquoteSpringLiteral.
Both extractors now import from it (group -> ingestion, the layer-correct
direction) so the shared semantics can't drift apart.
- Replace spring.ts's local unquote() with the safer unquoteSpringLiteral
(returns null for non-string nodes instead of assuming a quoted string).
- Add test/unit/spring-route-extractor-parity.test.ts: runs one shared Spring
fixture through both extractors and asserts they surface the same provider
method/path combinations.
The broader HttpRouteExtractor source-scan optimization is tracked in #2138.
---------
Co-authored-by: henry <zhangwei2017@unipus.cn>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
|
||
|
|
288b96f3e5
|
fix: batch query enrichment, bake FTS extension into CLI image, add FTS memory repro (#2108)
* perf(query): batch per-symbol process/cohesion/content lookups (N+1 -> 2-3) Port of the local-backend query-batching from gitnexus-enterprise PR #222 into the OSS local MCP backend. The query tool traced each matched symbol to its processes + cohesion (+ content) with up to 3N sequential pool round-trips; batch them into 2-3 'WHERE n.id IN $nodeIds' queries keyed back to each symbol by a prepended 'n.id AS nodeId' column. Output is identical: the aggregation loop is unchanged, iterates merged in the same order, and reads pre-fetched maps instead of issuing a query per symbol. Adaptations over a blind cherry-pick (would otherwise change output): - per-nodeId first-row community pick replaces the per-symbol LIMIT 1, so each symbol keeps its own community (not one for the whole batch); - batched rows regrouped to the originating merged item by nodeId so the JS-side RRF item.score still drives process ranking; - positional fallbacks shift +1 (process row[1..6], cohesion [1]/[2], content [1]); CodeRelation{type:...} relation form kept; IN-list chunked at 100 like the impact path. Adds a regression test asserting per-node community/content association (func:login keeps comm:auth; func:validate inherits no community). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docker): bake LadybugDB FTS extension into the CLI/serve image The container runs `serve` under the default `load-only` extension policy (the read pool pins {policy:'load-only'}), so a runtime LOAD EXTENSION fts never INSTALLs. Dockerfile.cli copied the extension installer but never ran it, so the runtime user's HOME had no FTS extension: keyword search silently degraded (no FTS indexes written, ranking falls back to vector-only with only a warning field). Same class of footgun fixed for the Hub image in gitnexus-enterprise PR #222. Run install-duckdb-extension.mjs as the `node` user with the runtime HOME so INSTALL fts materializes the extension under $HOME/.lbdb/extension where the runtime LOAD resolves it offline. Pin ENV HOME=/home/node because Docker does not derive HOME from USER — without it the build-install and runtime-load would resolve different paths. Verified locally: INSTALL lands in $HOME/.lbdb/extension/0.17.0 and a fresh offline load-only `LOAD EXTENSION fts` resolves it. Dockerfile.web is unaffected (static frontend, no @ladybugdb backend). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(lbug): FTS evict->reload RSS repro + inert pool RSS tracing Settles the gitnexus-enterprise PR #222 root-cause hypothesis for OSS: does re-running LOAD EXTENSION fts on every pool evict->reload strand the native FTS arena (unbounded RSS growth in long-lived MCP serve), or does db.close() reclaim it (bounded by MAX_POOL_SIZE)? Static read could not decide — the native lbugjs.node binary documents no close->extension-unload contract. Adds gitnexus/scripts/bench/fts-evict-reload-rss.mjs: a NATIVE mode that reproduces the exact native sequence doInitLbug()+closeOne() perform (open Database -> Connection -> LOAD EXTENSION fts -> QUERY_FTS_INDEX -> close) across K self-built FTS fixtures, and a --via-pool mode that drives the real compiled pool (initLbug/executeParameterized/closeLbug) against an existing analyzed repo. Plus a behavior-neutral GITNEXUS_POOL_RSS_TRACE=1 stderr trace on pool init/close (stdout reserved for MCP JSON-RPC; single env read when disabled). RESULT (native, 24 and 40 cycles x 6 fixtures, --expose-gc): PLATEAU. RSS warms up to ~400 MB then flattens (40-cycle: +36 MB over cycles 1-10, +3 MB over 30-40; decelerating), not the linear climb a per-reload arena leak would produce (240 reloads x stranded arena = multi-GB). db.close() reclaims the FTS arena. The unbounded-leak hypothesis is NOT reproduced for the OSS path: the pool's LRU eviction + close-on-evict BOUNDS the footprint, which is exactly the protection the enterprise Hub supervisor lacked (it opened bridge DBs in-process without eviction -> 15 GB). => plan U4 (worker/process isolation) is NOT justified by this evidence; U1 + U2 are the only OSS-shared changes. Caveat: small fixtures + awaited close; a --via-pool run against a large analyzed repo over a long session is the production-faithful follow-up (instrumentation is in place for it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(review): apply ce-code-review autofix feedback (#222 migration) Adversarial review found the U3 bench PLATEAU->no-leak conclusion was over-claimed from a 600-row fixture: a size-proportional FTS-arena leak would be sub-threshold at that scale. Strengthen the bench and make its verdict honest: - scale the fixture (--rows, UNWIND batch insert), probe ALL 5 FTS indexes in --via-pool (not 2 of 5), add a --no-await-close variant (the pool fire-and-forget close shape), and replace the absolute-delta gate with a SLOPE-DECELERATION 3-way verdict (PLATEAU / CLIMB / INCONCLUSIVE) plus step-discontinuity detection. At production-representative scale the synthetic runs are noisy/INCONCLUSIVE (deceleration argues against an UNBOUNDED leak but does not prove bounded), so plan U4 stays GATED on a --via-pool run against a real large analyzed repo -- not closed. - Dockerfile.cli: source the scratch-DB size from ENV GITNEXUS_LBUG_MAX_DB_SIZE (single source of truth) and add a build-time verify-only LOAD gate that fails the build on a HOME/extension-dir mismatch instead of silently degrading runtime keyword search. - install-duckdb-extension.mjs: additive verify-only mode (LOAD-only in a fresh process) + robust size parse; back-compatible with the runtime positional-size caller (validated). - tests: wire func:validate into a second process (proc:beta-flow) so the batched STEP_IN_PROCESS row[1..6] positional shift is exercised by a genuine multi-process symbol, and assert process ranking. No blast radius (75 seed-consuming tests pass). - pool-adapter.ts: trim the traceRss narrated-code comment (DoD 2.3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(bench): classify a sustained sub-floor RSS slope as INCONCLUSIVE, not PLATEAU Tri-review P2: the FTS evict->reload verdict short-circuited to PLATEAU whenever secondHalfSlope < SUSTAIN_FLOOR, BEFORE the deceleration check — so a sustained (non-decelerating) linear leak below 0.5 MB/cycle was labeled PLATEAU ("no leak"), the label that would wrongly close plan U4. Extract median/slopeMbPerCycle/classifyVerdict into a pure, side-effect-free fts-rss-verdict.mjs (zero imports) so it is unit-testable without loading the native addon or running the bench, and fix the classifier: - epsilon-first gate: a truly flat tail (< 0.1 MB/cycle) is PLATEAU regardless of decelRatio (guards against over-correcting a real negative into INCONCLUSIVE); - a sustained sub-floor positive slope (>= epsilon, < floor, decelRatio >= 0.6) is INCONCLUSIVE — a slow creep RSS cannot distinguish from noise at this scale, so the honest label is "not resolved", never a clean PLATEAU; - the noise floor now scales with the WORKING-SET growth (peak-baseline), not the pre-DB baseline RSS (which is interpreter/addon overhead, larger in --via-pool mode, and would inflate the floor and HIDE leaks). Reconcile the stale "per-row-relative delta floor" docstring; add floor + decelRatio to the MACHINE line. New fts-rss-verdict.test.ts pins all label boundaries (flat->PLATEAU, sustained-sub-floor->INCONCLUSIVE, decelerated->PLATEAU, sustained-linear->CLIMB, step->INCONCLUSIVE, working-set floor, no import side effects). U1 does NOT add detection power for sub-floor leaks (RSS cannot attribute that magnitude) — it stops the false PLATEAU and routes that regime to the --via-pool run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(query): signal partial/warning on a real enrichment failure (not benign missing-table) Tri-review P2: when a batched enrichment query (process/cohesion/content) threw, it was caught + logged and the chunk's symbols silently fell back to `definitions` with no signal — the caller could not tell "genuinely standalone" from "enrichment failed". Track an `enrichmentDegraded` flag in the three enrichment catch blocks and, at response build, compose a single `warning` (FTS-missing and/or the enrichment message, so neither overwrites the other) plus `partial: true`. Both fields are omitted on the clean path, so the success-path response shape is byte-identical. Crucially, the flag fires ONLY for a REAL failure (timeout / lock / native fault), NOT the benign "no Process/Community table" prepare error — a repo analyzed without processes/communities is a normal config, and firing `partial` on every such query would desensitize callers (isBenignMissingTableError gates it). New unit test test/unit/query-degraded-signal.test.ts (vi.mock pool-adapter, override hybrid search to feed one matched symbol, route STEP_IN_PROCESS -> throw): real failure -> warning+partial+symbol still returned; benign missing-table -> no signal; FTS-missing + enrichment failure -> both messages in one warning. Plus a success-path no-warning/no-partial assertion in the calltool integration test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3a4247ec36
|
feat(cpp): resolve inheritance-lattice member lookup (#2077)
* feat(cpp): resolve inheritance-lattice member lookup * fix(cpp): harden inheritance-lattice lookup --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
df5ce1f49b
|
fix(ingestion): close remaining open language parsing-layer coverage gaps (#1919) (#2072)
* fix(c): skip computed #include MACRO instead of emitting a garbage import source (F5) * fix(cpp): emit a Variable per name for structured-binding declarations (F9) * fix(dart): extract static const/final class fields (F26) * fix(dart): capture old-style function typedefs (F28) * fix(dart): read real top-level variable shape instead of a dead type field (F29) * fix(kotlin): capture callable references (F47) * fix(kotlin): anchor infix-call capture to the operator only (F49) * fix(kotlin): extract secondary constructors as members (F48) * fix(kotlin): capture destructuring declarations (F51) * fix(kotlin): index companion-object properties as fields (F52) * test(kotlin): assert callable-reference coverage runs on the worker path (F47) * fix(swift): extract protocol property requirements (F75) * fix(swift): recognize enum_class_body as a method body node (F79) * test(ingestion): rebaseline swift captures-golden + scope-capture fingerprints (#1919) * fix(kotlin): attribute secondary-constructor body calls to the Constructor node (#1919 review CF1) A Kotlin secondary constructor's body executes statements like a method body, but the registry-primary scope-resolution path had no Function scope or Constructor def for it. A call inside the body resolved its caller anchor up to the enclosing Class scope, mis-attributing the CALLS edge to the class rather than the Constructor. Add `(secondary_constructor) @scope.function` to the Kotlin scope query so the body becomes its own scope, and synthesize a `@declaration.constructor` (named `constructor`, qualified `<Class>.constructor`, with parameter metadata) so the scope owns a Constructor def that bridges to the structure-phase Constructor node. Also add an arity-disambiguating lookup key for overloadable callables: two same-name secondary constructors of different arity (e.g. a zero-arg vs a 2-arg) share the qualified key whose first-write-wins assignment is source-order- dependent — so a zero-arg overload could resolve to a sibling. The structure node id encodes `#<arity>`; mirror that in the bridge keyspace and match by the def's parameterCount. Same-arity overloads collapse onto one arity key exactly as before, so no regression there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(kotlin): do not own function-local property bindings under the enclosing class (#1919 review CF3) Kotlin emits destructuring / loop bindings (`val (a,b) = pair`, `for ((k,v) in m)`) as `@definition.property` to dodge the block-scope local-symbol pruner. When such a binding sits inside a method body of a class, the structure-phase owner walk found the enclosing class and emitted a spurious HAS_PROPERTY edge (e.g. `C -> k`), treating a function-local as a class member. Guard the Property owner resolution: if a function-like ancestor is reached before any class container, the property is function-local and gets no owner edge (it falls back to a File DEFINES edge). Language-agnostic — genuine class fields sit directly in the class body with no intervening function, so they keep their HAS_PROPERTY owner edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(kotlin): guard non-companion property isStatic=false (#1919 review CF4) Add a field-extraction case for a plain non-companion class `class C { val x: Int = 1 }` asserting the property `x` has isStatic=false, guarding the `isInsideKotlinCompanion` walk against false-positives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(kotlin): dedup type_identifier lookup in extractOwnerName (#1919 review CF5) The `node.namedChildren.find(c => c.type === 'type_identifier')?.text` lookup was duplicated across the companion and non-companion branches of the Kotlin field-extractor's extractOwnerName. Hoist it into a single local, preserving the existing behavior (anonymous companion falls back to "Companion"; other nodes prefer the `name` field, else the type_identifier text, else undefined). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(dart): capture generic old-style function typedefs (#1919 review CF2) * test(dart): guard multi-name field count and top-level-var labels (#1919 review CF4) * docs(swift): correct isStatic comment re multi-modifier hasKeyword (#1919 review CF5) * test(ingestion): rebaseline dart+kotlin scope-capture fingerprints after review remediation (#1919) * fix(ingestion): correct CF3 owner-strip boundary set for accessor/init bodies and Dart signatures (#1919 review) The CF3 property-ownership guard used FUNCTION_NODE_TYPES, which (a) includes Dart bare signatures (function_signature/method_signature) — over-stripping every Dart class getter/setter's HAS_PROPERTY owner — and (b) omits Kotlin anonymous_initializer/getter/setter and Swift computed accessors — under- stripping destructuring/locals inside init{} and accessor bodies, emitting spurious Class->local HAS_PROPERTY edges. Introduces a guard-specific LOCAL_SCOPE_BODY_NODE_TYPES set (signatures excluded, accessor/init bodies included). Adds Dart accessor-ownership + Kotlin init/accessor destructuring regression fixtures. Both confirmed on the worker pipeline; no cross-language regression (1597 cross-language tests green). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f0c292f9e7
|
perf(ingestion): prune inert local value symbols (#2065) | ||
|
|
baca749e0b
|
fix(vue): F89 JSDoc fix, F90 dual-script merge, F92 lang plumbing (#1936) (#2050)
* fix(vue): F89 JSDoc fix, F90 dual-script merge, F92 lang plumbing (#1936) * fix(vue): reviewer fixes — P1 lang routing, P2 lineOffset, P2/P3 pipeline tests * fix(vue): add jsx to lang routing condition * fix(vue): update F90/F92 fixtures and test assertions for CI --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
95f87fc12a
|
perf(ingestion): Linux-kernel-scale analysis — worker-pool parse + finalize O(n²) + scope-resolution memory wall (#1983) (#2038)
* fix(ingestion): reduce parse-phase memory for huge repos (#1983)
Stop retaining full parse-cache chunks in RAM alongside the merged graph,
slim on-disk shards, defer worker ParsedFile emission for scope-resolver
languages, and add GITNEXUS_DEBUG_HEAP probes for OOM diagnosis.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ingestion): address #2038 tri-review findings (parse-phase memory)
Resolves the confirmed review findings on PR #2038:
- P1: thread exportedTypeMap through the sequential parse path
(processParsingSequential) so a no-worker run over a partially-warm
cache no longer silently drops the sequential-miss files' exported
types. Cache hits made exportedTypeMap.size > 0, suppressing the
end-of-loop buildExportedTypeMapFromGraph rebuild, but the sequential
path never populated the map. Regression test added (fails on the
pre-fix tree, passes after) plus a fully-sequential differential oracle.
- P2: saveParseCache builds its on-disk index from hashes actually
written/copied (writtenKeys), never a usedKeys hash whose shard write
or copy was skipped — no more phantom index entries.
- P2: add a unit test asserting SCOPE_RESOLUTION_LANGUAGES stays in sync
with SCOPE_RESOLVERS (asymmetric drift would lose a language's ParsedFile).
- Backfill cache coverage: loadParseCacheChunk missing/corrupt -> undefined,
pruneCache onDiskKeys branch, slim preserves nodes, saveParseCache
copy-evicted-shard round-trip.
- Cleanups: single-source heap-probe gating via isDebugHeapEnabled();
hoist the per-chunk mkdir in persistParseCacheChunk behind a
process-scoped Set; gate COBOL's unused worker-side ParsedFile
extraction (graph nodes still come from cobolPhase) while keeping
fileCount/progress unconditional.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(ingestion): remove dead worker-side ParsedFile extraction
After #2038 gated worker `ParsedFile` emission behind `!isScopeResolutionLanguage(language)`, and with all 16 SupportedLanguages registered in SCOPE_RESOLVERS, that gate was structurally always true — the worker already produced no ParsedFiles and scope-resolution re-extracts each file from source on the main thread (run.ts). Remove the now-dead machinery:
- Drop both worker `extractParsedFile` call-sites (tree-sitter processFileGroup + the standalone-provider branch) and the `result.parsedFiles.push`. The standalone branch keeps fileCount/onFileProcessed per file. `result.parsedFiles` stays declared but empty (field removal deferred).
- Remove the now-orphaned `scopeSourceKind` var + `ScopeCaptureSourceKind`/`extractParsedFile`/`isScopeResolutionLanguage` imports.
- Delete the consumerless `migrated-languages.ts` (isScopeResolutionLanguage + SCOPE_RESOLUTION_LANGUAGES) and its drift-guard test — parse-worker was their only importer. Also improves AGENTS.md "shared ingestion code must not name languages" compliance.
`extractParsedFile` and the scope-extractor-bridge stay (scope-resolution/run.ts + Vue resolver use them). Behavior-preserving: worker-sequential-parity passes before and after; tsc/eslint clean; no baseline/golden drift.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(ingestion): worker-pool-only parsing; remove sequential parser (#1983)
Completes the #1983 huge-repo parse-OOM effort by making the worker pool
GitNexus's sole parse path.
Parallel serialization (the perf core): workers serialize their ParsedFiles to
a disk store in parallel and stream them back to scope-resolution, so the main
thread no longer re-parses every file (the tree-sitter native-memory leak that
caused the OOM). Adds chunk merge-pipelining + work-proportional chunk sizing so
the pool stays saturated.
Remove the sequential parser: `--workers 0`, `GITNEXUS_WORKER_POOL_SIZE=0`, and
`skipWorkers` now hard-error (no silent degrade — #1741); the small-repo
threshold no longer selects an in-process path; pool creation stays lazy /
cache-miss-gated so warm all-hit runs never spawn workers.
Worker-path parity fixes — removing sequential surfaced two pre-existing gaps
that tiny-fixture tests had masked by running below the worker threshold, both
fixed by carrying per-file metadata as DATA across the worker boundary (never
re-parsing on the main thread, preserving the OOM fix):
- C++: templateConstraints wired into worker node identity (SFINAE overload
disambiguation) + ADL / inline-namespace capture side-channel serialized
onto the ParsedFile.
- Kotlin: companion-scope side-channel serialized the same way (companion /
static dispatch).
Validation: tsc + build clean; full suite green (10,190 pass — the only
deterministic failures were the now-fixed C++/Kotlin worker-path gaps; the 2
remaining full-run failures are pre-existing load flakiness, green in
isolation); cpp-pipeline benchmark stays linear on a 1-worker pool.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ingestion): wire C static-linkage side-channel + ADL O(1) collect + tri-review cleanups (#1983)
Follow-up to the worker-pool-only refactor, from a tri-review of the parse path.
- C static-linkage side-channel (P1): cProvider had no collect/applyCaptureSideChannel,
so on the now-sole worker path C `static` file-local marks were lost across the worker
boundary -> false cross-file CALLS edges + over-broad #include wildcard visibility on
every C analysis (the Linux kernel is C). Mirror the C++/Kotlin wiring: serialize
`staticNames` per file onto ParsedFile.captureSideChannel and restore it on the main
thread (no re-parse). + a worker-path regression test (the existing c-static-isolation
fixture passed vacuously — its collision resolves via #include before the global
free-call fallback ever consults static-linkage).
- captureSideChannel `kind` discriminant: add `kind:'cpp'`/`kind:'c'` tags + guards
(Kotlin already had one) now that C/C++/Kotlin share the single generic field.
- Perf: collectCppAdlSideChannel scanned the whole argInfoBySite/noAdlSites maps per file
(O(F^2) per sub-batch, ~100M parseSiteKey calls at kernel scale). Add per-filePath
lockstep indexes -> O(1) collect; serialized snapshot byte-identical.
- Cleanups: inline the one-line processParsingWithWorkers wrapper into processParsing;
drop the always-empty WorkerExtractedData.calls/assignments/constructorBindings fields;
remove the voided astCache param from processParsing; refresh stale "sequential
fallback" JSDoc.
Validation: tsc + build clean; cpp 297/297, c 8/8 (incl. the new worker-path
static-linkage guard), typescript + parsedfile-store green; cpp ADL benchmark stays linear.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* perf(scope-resolution): index C/C++ #include resolution in finalize (O(n²)→O(n))
Kernel-scale C/C++ analysis ground in finalizeScopeModel because three
per-#include operations each did a full O(F) scan with no index — the
finalize O(n²) that surfaced once the #1983 parse-phase OOM was fixed:
- expand{C,Cpp}WildcardNames: parsedFiles.find() per wildcard edge → O(R·F)
- resolveImportTarget: new Set(allFilePaths) rebuilt per #include
- resolveCImportTarget: suffix-match scanned all workspace paths
Each is replaced with a WeakMap-per-pass index keyed on the stable
parsedFiles/allFilePaths references that scope-resolution run.ts passes
once per pass:
- Map<ScopeId,ParsedFile> for wildcard expansion (c/static-linkage.ts +
cpp/file-local-linkage.ts)
- memoized augmented header set (c/scope-resolver.ts + cpp/scope-resolver.ts)
- basename-bucketed suffix index in resolveCImportTarget (c/import-target.ts),
shared by C and C++ since resolveCppImportTarget delegates to it
Collapses the C/C++ finalize from O(R·F) to O(R+F). Pure-perf, byte-identical
edge output: 962 targeted tests green (490 C + 472 C/C++ scope-resolution);
the basename index preserves the exact endsWith('/'+target) match and the
fewest-path-components-then-lexicographic tie-break.
The kernel's ~25-30k .h headers are classified C++, so both providers must
be fixed. Proven on the Linux kernel: the C finalize completed
(sr-post-finalize lang=c → sr-end lang=c), which the pre-fix run never
reached in 16+ min of grinding.
Build-independent follow-ups (separate from this finalize fix), documented
for later: emitFreeCallFallback same-name buckets (emit phase),
buildGraphNodeLookup + precount global setup, the ParsedFile store-load,
the dart/go/ruby expand-wildcards .find siblings, and the ~26GB
scope-resolution memory floor (full kernel completion needs >~40GB RAM).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(bench): regenerate C scope-capture baseline for the #1983 c-static-linkage-worker fixture
bench/scope-capture/measure.mjs fingerprints emitCScopeCaptures over the
lang-resolution/c-* fixture corpus. The #1983 PR added the
c-static-linkage-worker fixture (caller.c/lib.c/lib.h/local.c — the
worker-path static-linkage side-channel test) but did not regenerate the C
baseline, so `--check` has been red on this branch (main, lacking the
fixture, still matches 0de009b).
Pure fixture-corpus drift — no c/captures.ts or query change branch-vs-main,
existing fixtures' captures byte-identical (c-captures.test.ts 45/45),
scaling stays linear (~0.97). Regenerated: 0de009b -> 39f3a83. Bench now
PASS (14 languages). Unrelated to the finalize O(n²) fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* perf(scope-resolution): lower kernel-scale resident memory floor + setup cost
Reduce the scope-resolution resident-memory floor and setup throughput on
huge repos (Linux kernel), the wall that remains after #1983 (parse OOM) and
the finalize O(n^2) fix (
|
||
|
|
89b02286ad
|
fix(csharp): qualified/alias constructor names, : base/: this initializers, generic type-arg strip (#2046)
* fix(csharp): bind qualified constructor names, capture : base/: this, fix generic strip Mirrors the Java #1928 parsing-layer fixes for the C# scope-resolution path — the same three defect classes exist verbatim in C#: - Qualified / qualified-generic / alias-qualified constructor calls (`new Ns.Foo()`, `new A.B.Foo()`, `new Ns.Box<int>()`, `new MyAlias::Foo()`, `new global::Foo()`) bound only `@reference.call.constructor.qualified` with no `@reference.name`, so the central extractor fell back to the whole-expression anchor and the reference name became the raw `new Ns.Foo()` text (never resolved). Derive the simple-name tail via the existing `terminalTypeNameNode` helper (handles qualified_name, generic tail, and alias_qualified_name), and add a query arm for the top-level `alias_qualified_name` shape that was not captured at all. - `: base(...)` / `: this(...)` explicit constructor initializers, modeled by tree-sitter as `constructor_initializer` and never matched by the scope query, dropped the chained-constructor CALLS edges. Synthesize them: `this` → enclosing type name; `base` → the base type's bare name (first base-list entry, which C# requires to be the base class). Arity attached for overload disambiguation. - `interpretCsharpTypeBinding`'s qualifier strip used `lastIndexOf('.')` over the whole string, cutting inside a qualified generic type ARGUMENT (`Dictionary<string, Ns.User>` → `User>`). Make stripQualifier generic-aware: reduce only the segment before the first `<`, re-attaching the generic suffix — multi-arg generics stay intact so the `.Values`/`.Keys` collection-accessor unwrap keeps working. Tests: capture-level unit tests for every constructor shape (incl. alias-qualified, double-match guard) and `: base`/`: this` (incl. struct/record/mixed-base); interpretCsharpTypeBinding unit tests (the corruption case + nullable/nested/ unknown-generic edges); end-to-end resolver tests with new fixtures. The csharp-captures golden was regenerated — drift is purely additive (only the new fixtures; zero existing-fixture digests changed). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(csharp): enhance constructor resolution and namespace qualification - Implemented qualified constructor name binding to resolve collisions between types in different namespaces. - Added support for `: base(...)` and `: this(...)` constructor initializers to ensure correct edge emission in the scope resolution. - Improved generic argument stripping to prevent incorrect parsing of qualified types. - Introduced tests for new features, including handling of interface-only base classes and qualified constructor calls. This update addresses issues related to constructor resolution and namespace qualification, ensuring accurate type references in C# code. Tests have been added to validate these changes. * fix(csharp): implement namespace prefix tagging for file-level type definitions - Updated the C# ingestion process to tag file-level type definitions with their enclosing namespace path using a new `namespacePrefix` field, without altering the `qualifiedName`. - Enhanced the scope resolver to utilize the `namespacePrefix` for resolving same-tail collisions in constructor calls, improving accuracy in type resolution. - Added unit tests to validate the new functionality, ensuring that namespace prefixes are correctly applied to both block-scoped and file-scoped types, while leaving namespace-free types untagged. This change addresses issues related to namespace qualification and constructor resolution in C# code, facilitating better handling of type references. * refactor(scope-resolution): share isOverloadableCallable via util Extract the ctor/function/method overload predicate into callable-labels.ts so graph-bridge registration and lookup stay aligned without duplicated private copies in ids.ts and node-lookup.ts. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
281ce2600c
|
fix(java): close parsing-layer coverage gaps F35/F38/F41 (#1928) (#2045)
* fix(java): close parsing-layer coverage gaps F35/F38/F41 (#1928) Registry-primary scope-resolution path (the live one post-#942/#943): - F35 [HIGH]: qualified / qualified-generic constructor calls. `new pkg.Foo()` parses as a `scoped_type_identifier` that the query bound only as `@reference.call.constructor.qualified` with no `@reference.name`, so the scope extractor fell back to the whole-expression anchor and the reference name became the raw `new pkg.Foo()` text (never resolved). Bind the simple -name tail (end-anchored last child) and add an arm for the previously uncaptured `new pkg.Box<String>()` (qualified + generic) shape. - F38 [MEDIUM]: `super(...)` / `this(...)` explicit constructor invocations, modeled as `explicit_constructor_invocation` and never matched by the scope query, dropped the chained-constructor CALLS edges. Synthesize them with the target resolved structurally (this -> enclosing type name; super -> superclass tail via the shared javaBaseLookupNameNode, skipping implicit Object) plus arity for overload disambiguation. - F41 [LOW]: interpretJavaTypeBinding stripped the qualifier before generics, so a qualified generic type arg (`Map<String, com.example.User>`) was cut inside the generic into `User>`. Strip generics first, then the qualifier; make the erasure fallback qualifier-tolerant. F36/F37 already landed upstream (#1940/#1956); F39/F40 are legacy-bank remnants that are no longer consumed (legacy @import skipped in parse-worker; legacy @call never read in parse-impl) so they are intentionally left untouched. Tests: low-level capture unit tests (constructor shapes incl. double-match guard; super/this/enum/implicit-Object), interpretJavaTypeBinding unit tests (qualified generic args + the corruption case), and end-to-end resolver tests with new fixtures asserting the CALLS edges resolve to the correct constructors. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(scope-resolution): register Constructor overload keys so this()/super() chains don't self-loop (#1928 F38 review) Review of #2045 caught two gaps; both confirmed by reproduction. P2 — F38 this() emitted a self-loop. On the java-explicit-constructor fixture, Child(int){ this(); } produced CALLS Child()#0 -> Child()#0 instead of Child(int)#1 -> Child()#0. Root cause is the language-agnostic graph-bridge: the parse phase mints distinct Constructor nodes (Child#0, Child#1) carrying parameterTypes, but node-lookup.ts registered the parameter-types / shape overload keys only for Function/Method, never Constructor, so both ctors collapsed onto the first-wins qualified/simple key and the caller Child(int) resolved to Child#0 (the this() target). Extend the overload keys to Constructor in both node-lookup.ts (registration) and ids.ts (lookup) via a shared isOverloadableCallable predicate. Verified the edge now connects distinct nodes (Child#1 -> Child#0); super(1)->Base#1 still correct. No cross-language regressions (the 9 worker-path failures reproduce identically on clean HEAD). Also harden the integration test: it matched the this() edge on name only, which a self-loop satisfies; now assert the endpoints are DISTINCT constructors. P3 — F41 order-regression guard was inert (List<Map<String,User>> normalizes to List under both strip orders). Add List<com.x.Foo<String>> -> List, which is corrupted to Foo<String>> under the old order and only correct generics-first. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(java): update fingerprint and add notes for constructor query captures in baselines.json Updated the fingerprint for the Java section and added detailed notes regarding the enhancements in constructor query captures, including qualified and qualified-generic constructor queries. This change reflects ongoing improvements in the parsing layer coverage and fixture updates. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
3b195ec100
|
fix(csharp): normalize primary base receiver type (#2036) | ||
|
|
bd59fa95ce
|
refactor(ingestion): delete legacy resolution context + tiered-lookup plumbing (RING4-2, #943) (#2033)
* test(ingestion): characterize Laravel route → controller CALLS edges (RING4-2 #943) Pins the current processRoutesFromExtracted edge-emission behavior (which had no direct coverage) before migrating it off the legacy ResolutionContext.resolve tiered lookup. Locks edge target, reason, and confidence values. * refactor(ingestion): resolve Laravel route controllers via type registry (RING4-2 #943) Migrate processRoutesFromExtracted off the legacy ResolutionContext.resolve tiered lookup onto model.types.lookupClassByName (global class resolution) + model.symbols.lookupExactAll (same-file method lookup). Drops the TIER_CONFIDENCE dependency for a fixed ROUTE_EDGE_CONFIDENCE constant matching the prior global-tier confidence. Characterization tests (6) stay green — behavior preserved. * refactor(ingestion): delete ResolutionContext.resolve tiered lookup (RING4-2 #943) Removes the legacy tiered name resolution — resolve/resolveUncached, TieredCandidates, ResolutionTier, TIER_CONFIDENCE, walkBindingChain, the package-dir index, the per-file resolve cache, and tier-hit stats. The context is now a thin holder for the live SemanticModel plus the (now-dead) per-file import maps, which the follow-up prune removes. Deletes the dedicated resolution-context.test.ts and symbol-resolver.test.ts (both exercised the removed .resolve tiered lookup). Full unit suite green (the 3 analyze worker-pool tests are pre-existing load flakes — pass isolated). * refactor(ingestion): delete legacy import-map plumbing + wildcard synthesis (RING4-2 #943) The per-file importMap / namedImportMap / packageMap / moduleAliasMap that fed the retired tiered resolver are now dead — nothing reads them (IMPORTS edges come from scope-resolution's imports-to-edges bridge, independent of these maps). Removes: - wildcard-synthesis.ts (synthesized the dead namedImportMap/moduleAliasMap) - import-processor's resolution path (processImports/processImportsFromExtracted/ wireImplicitImports/buildImportResolutionContext), keeping only the live preprocessImportPath path-cleanup helper - the parse-impl orchestration that drove them The parse phase now threads its SemanticModel to scope-resolution directly (parseOutput.model) instead of wrapping it in the resolution context. Deletes the obsolete wildcard/import-processor unit tests; trims the dead processImports cases from sequential-language-availability (processParsing coverage kept). * refactor(ingestion): delete resolution context + named-binding plumbing (RING4-2 #943) Completes the legacy-resolution retirement. With the tiered resolver gone, the entire per-file import-extraction chain is dead — its only consumer was the deleted ResolutionContext.resolve, and scope-resolution emits IMPORTS edges from its own finalized ImportEdges: - delete model/resolution-context.ts (the legacy context); the parse phase now hands its SemanticModel to scope-resolution as parseOutput.model - delete the named-bindings/ extractors + the namedBindingExtractor provider hook (built the dead NamedImportMap) across all 8 providers + the worker - delete the orphaned implicitImportWirer hook + Swift implementation + providersWithImplicitWiring (scope-resolution owns implicit imports now) - drop the dead ExtractedImport type + worker/sequential import accumulation (result.imports / WorkerExtractedData.imports) - import-processor.ts and its preprocessImportPath helper are now unreferenced Deletes the obsolete named-bindings + preprocessImportPath unit tests. tsc clean; full unit suite green (3 analyze worker-pool tests are pre-existing load flakes); 1229 import/cross-file/resolver integration tests pass incl. the wildcard-import languages (Go/Ruby/C++/Swift) that previously used synthesis. * docs(ingestion): scrub stale references to deleted resolution-context machinery (RING4-2 #943) * docs(ingestion): reword route resolver comment to clear acceptance grep gate (#943) * fix(review): apply autofix feedback (RING4-2 #943) Code-review autofixes from the multi-agent pass: - delete orphaned dead code the deletion missed: swift.ts groupSwiftFilesByTarget + SwiftPackageConfig import (live copy is target-grouping.ts), import-resolvers EMPTY_INDEX export (no consumers after the importCtx reset was removed) - scrub stale comments referencing deleted symbols (processImports, preprocessImportPath, moduleAliasMap, NamedImportMap/PackageMap, wildcard-synthesis) and fix a broken comment fragment in parse-impl.ts - document the intentional global-resolution convergence for route controllers (the import-scoped tier was deleted with the resolver): confidence flattens 0.9→0.5 but resolved edges stay at the 0.5 process-trace/community gate; only the narrow imported-controller-with-unresolved-method guessed edge crosses it - add an overloaded-method characterization case pinning lookupExactAll[0] * style(ingestion): prettier-format parse-impl unwind + route characterization test (#943) * refactor(ingestion): address tri-review findings (RING4-2 #943) From the PR #2033 tri-review (Codex + CE lanes): - delete the now-dead importSemantics provider field + ImportSemantics type (wildcard-synthesis.ts was its sole consumer; zero readers remain) across language-provider.ts + 7 providers + DEFAULTS - correct the processRoutesFromExtracted JSDoc: the import-disambiguated controller skip is STRICTER than the legacy global-tier guard (the legacy import-scoped tier resolved aliased / same-short-name controllers and emitted the edge); document the aliased-import missed-edge case explicitly - add an aliased-controller characterization test pinning the documented global-resolution convergence (no edge for an aliased/unresolvable controller name) - scrub stale parse-impl.ts docstrings/comments that still listed the removed import-resolution / wildcard-synthesis / heritage passes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ingestion): capture routes-file use/FQN map for Laravel controller resolution (#943) Adds ExtractedRoute.controllerQualifiedName: the Laravel route extractor now builds the routes file's `use`-import alias map (local→normalized dot-joined FQN, via splitNamespaceUseDeclaration) and captures inline qualified ::class references, threading the disambiguating FQN through every route. Normalized via the shared normalizeQualifiedName so it matches the type registry's key shape (issue #1982). Foundation for qualified-first route→controller resolution (U2). * fix(ingestion): resolve Laravel route controllers qualified-first (#943) processRoutesFromExtracted now resolves the controller via model.types.lookupClassByQualifiedName(route.controllerQualifiedName) when the extractor disambiguated it (aliased use / same-short-name / inline FQN), falling back to the short-name lookupClassByName (which still skips on ambiguity). This restores the route→controller CALLS edges the PR #2033 tri-review (Codex F1 + ce-adversarial) found dropped, without re-adding the deleted per-file import map. Method resolution, guessed-id, and confidence are unchanged. JSDoc rewritten to qualified-first precedence; the aliased characterization test flips from no-edge to edge; adds duplicated-name-disambiguated + stale-FQN-fallback cases. * test(ingestion): end-to-end Laravel route→controller qualified resolution + PSR-4 disambiguation (#943) Adds an integration test that parses real namespaced PHP controllers + a routes file through the worker pipeline and asserts the route CALLS edges target the correct namespaced controller — the authoritative gate the unit tests can't be (hand-built models). It surfaced that PHP's statement-form `namespace X;` leaves the structure-phase qualifiedName as the SHORT name, so lookupClassByQualifiedName misses; resolveControllerByQualifiedName now adds a PSR-4 file-path disambiguation (FQN namespace tail ↔ file directory tail) to pick the right same-short-name controller. Forces the worker path (workerThresholdsForTest) since route extraction is worker-only. * style(ingestion): prettier-format Laravel route resolution changes (#943) * test(ingestion): regenerate php-captures golden for the new php-laravel-routes fixture (#943) * test(ingestion): move route fixture out of the php-* scope-capture corpus (#943) The laravel route-resolution fixture lived under lang-resolution/php-laravel-routes, which the php scope-capture golden + benchmark both glob (lang-resolution/php-*), drifting their fingerprints. The fixture is for route resolution, not php scope-capture parity, so rename it to lang-resolution/laravel-route-resolution to decouple it. Reverts the golden's php-laravel-routes entries; bench scope-capture --check passes (php back to baseline). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7cbc544299
|
fix(php): import decomposition, enum cases, anonymous class scope — F53,F54,F55 (#1931) (#1989)
* fix(php): import decomposition, enum cases, anonymous class scope — F53,F54,F55 (#1931) * chore: fix unused imports, format, rebuild gitnexus-shared for macro type * chore(bench): update PHP scope-capture baseline to CI-computed hash * fix(php): reviewer fixes — grouped prefix, dead code removal, test precision * feat: add F55 anonymous class pipeline test * chore: fix format and benchmark baseline * chore: regen PHP golden after F53/F54/F55 query changes * chore: remove pipeline test, add grouped-prefix test, update fingerprint * chore: remove unused beforeAll and path imports --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
560291ad6e
|
fix(ingestion): qualify Ruby same-tail nested mixin modules + route IMPLEMENTS by scope (#1991) (#2006)
* fix(ingestion): qualify Ruby same-tail nested mixin modules + route IMPLEMENTS by scope (#1991) A Ruby `module` maps to the Trait label but is not a typeDeclaration, so the structure phase never qualified its node id: two same-tail nested mixin modules (App::Loggable / Web::Loggable) collapsed onto one Trait:f.rb:Loggable node and the bare-name `include Loggable` cross-wired IMPLEMENTS (first-wins tail). Structure phase: expose buildQualifiedName as a `qualifyScopeName` ClassExtractor hook and thread it for Trait nodes in parsing-processor + parse-worker (lockstep), so a module node keys by its qualified scope path (App.Loggable). Not Option A — `Trait` is not in CLASS_LIKE_LABELS and the qualified-id selection gates it out; qualifyScopeName bypasses the typeDeclaration gate that makes extractQualifiedName bail on modules. getQualifiedOwnerName also falls back to qualifyScopeName so methods inside a nested module own through the same qualified Trait id (no dangling HAS_METHOD). Resolution: emitRubyMixinEdges resolves a bare mixin reference lexically by the including class's enclosing scope (`App::S` + `Loggable` -> `App::Loggable`), and the simple-tail fallback is now delete-on-collision (refuse to guess on a same-tail tie) instead of first-wins. New single-file fixture + tests: two distinct Trait nodes, S IMPLEMENTS App.Loggable only, T IMPLEMENTS Web.Loggable only, no dangling HAS_METHOD; both resolver legs + worker path. Module->Trait preserved; Trait NOT added to CLASS_LIKE_LABELS. ruby-captures-golden regenerated additively. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ingestion): single-source the Ruby Trait scope-label predicate; regen ruby bench baseline (#1991) F5 follow-up to #1991: replace the four hardcoded `nodeLabel === 'Trait'` checks (two each in the sequential parsing-processor.ts and worker parse-worker.ts definition paths) with a single isQualifiableScopeLabel() in ast-helpers.ts so the lockstep paths can't drift. Value-identical predicate — no behavior change. Also regenerate the ruby scope-capture bench baseline: #1991 added the ruby-nested-mixin-tail-collision fixture (and updated the ruby captures-golden), but the bench baseline was never regenerated, so the order-independent fingerprint drifts (bf6b13a -> f0d9b4c6, fixture_count 85 -> 86). Pure fixture-corpus drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
083aedbc41
|
refactor(ingestion): delete legacy call-resolution DAG + heritage processor (RING4-1, #942) (#2023)
* refactor(ingestion): delete legacy call-resolution DAG + heritage processor (#942) RING4-1: all 16 production languages (incl. Vue #940) are registry-primary, so the legacy resolution legs only ran under the now-removed CI parity gate. Calls and inheritance now resolve exclusively through scope-resolution (Registry.lookup, preEmitInheritanceEdges, emitHeritageEdges, buildMro → MethodDispatchIndex). Removed: - Call-resolution DAG: call-processor.ts legacy body (processCalls, processCallsFromExtracted, resolveCallTarget + all resolver/dispatch/chain helpers), model/resolve.ts MRO-via-HeritageMap, model/heritage-map.ts, type-env DAG types; inferImplicitReceiver/selectDispatch LanguageProvider hooks + Ruby impls; DispatchDecision/ImplicitReceiverOverride/ReceiverEnriched. - Legacy heritage path: heritage-processor.ts, heritage-types.ts, heritage-extractors/, @heritage.* tree-sitter queries, heritageExtractor/ heritageDefaultEdge/interfaceNamePattern wiring, worker + parse-impl heritage passes (parse-worker/parsing-processor lockstep), cross-file-impl DAG pass. - Scope-parity infrastructure entirely (no legacy↔registry parity left to run): scripts/run-parity.ts, scripts/ci-list-migrated-languages.ts, ci-scope-parity.yml, test:parity, and the scope-parity ci.yml gate. Resolver integration tests still run via the normal tests job. Kept (shared infra, NOT call-DAG-only): type-env.ts buildTypeEnv (field extraction / structure phase / embeddings), model/resolve.ts c3Linearize + gatherAncestors (mro-processor mroPhase), route/fetch/exported-type-map helpers in call-processor.ts, preEmitInheritanceEdges (legacy-edge dedup simplified). Acceptance: grep for resolveCallTarget/inferImplicitReceiver/selectDispatch/ buildHeritageMap/HeritageMap/processHeritage/heritageExtractor/@heritage. is zero across src + test. tsc clean (both packages); resolver integration suite green (bit-compatible EXTENDS/IMPLEMENTS/CALLS); scope-capture fingerprints unchanged (python re-baselined: removed redundant ignored captures). ARCHITECTURE.md updated to scope-resolution-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(review): apply autofix feedback (#942) ce-code-review autofix pass on the RING4-1 deletion: - parse-cache.ts: bump SCHEMA_BUMP 2→3 — ParseWorkerResult lost its `heritage` field, so stale on-disk caches must invalidate (prevents a rollback replaying a heritage-less cache into legacy code) [api-contract P2]. - parse-impl.ts: drop 3 now-unused type imports (ExtractedCall, ExtractedAssignment, FileConstructorBindings) left by the deferred-block removal — would fail the eslint CI gate [correctness+maintainability P1]. - AGENTS.md / CLAUDE.md / scope-resolver.ts contract doc: fix stale pointers to the deleted "§ Call-Resolution DAG" section + removed hooks; preserve the language-neutrality rule [project-standards P1]. - registry-primary-flag.ts / cross-file.ts / parse-impl.ts: refresh stale comments referencing deleted symbols (legacy DAG, runCrossFileBindingPropagation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ingestion): remove the vestigial isRegistryPrimary flag (#942) With the legacy call-resolution DAG deleted, the per-language `REGISTRY_PRIMARY_<LANG>` / `isRegistryPrimary` / `MIGRATED_LANGUAGES` flag had only one meaningful state — every production language resolves via scope-resolution — and an explicit `=0` override could only *disable* resolution with no fallback (a footgun the review flagged). Removing it. - Delete `registry-primary-flag.ts` and the now-dead `shadow-harness.ts` (legacy↔registry shadow-parity tool) + its test. - Collapse the three flag gates to their behavior-preserving outcome (`SCOPE_RESOLVERS == MIGRATED_LANGUAGES`, so this is a no-op): - scope-resolution phase now runs for every registered `SCOPE_RESOLVERS` entry (was `∩ MIGRATED_LANGUAGES`). - import-processor `addImportGraphEdge` + parse-impl `shouldAccumulate`: the legacy emit/accumulate paths were already inert for migrated languages (scope-resolution owns IMPORTS via the imports-to-edges bridge); drop the flag term. - Collapse flag-branching tests to the scope-resolution path and delete the csharp legacy-`=0`-leg describe blocks; remove the ruby/rust-scope env-forcing hooks (no-ops now). - Refresh docs/comments (ARCHITECTURE.md "one registration", scope-resolver cookbook, phase deps) — adding a language is now a single `SCOPE_RESOLVERS` registration. Verified: tsc clean (both packages); resolver integration tests green (747 assertions across cobol/csharp/ruby/rust/typescript/go, IMPORTS edges intact); grep for the flag symbols is zero across src + test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(format): prettier formatting on #942 changes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): drop legacy heritage-capture tests + re-baseline scope-capture fingerprints (#942) Two CI failures from the #942 cleanup, surfaced by the tri-review + CI: - tree-sitter-languages.test.ts: two tests asserted `@heritage.*` captures (Rust trait-impl, Dart extends/implements/with) that this PR removed. The acceptance grep used `@heritage\.` (with `@`); these reference the runtime capture name `heritage.trait` (no `@`), so they slipped the earlier sweep. Inheritance is now covered by the resolver integration suite. (fixed macos-latest) - Re-baselined the scope-capture bench fingerprints for csharp/rust/ruby/java/ javascript/kotlin (baselines.json) + python (python-scope/baseline-fingerprint.txt). The earlier test-cleanup reworded comments inside the lang-resolution fixture files (Shapes.cs, child.rs, derived.rb, IA.java/Plain.java, Service.js, F.kt, app.py) to scrub deleted-symbol references for the acceptance grep; those are the bench corpus, so capture node positions shifted. Capture LOGIC is unchanged — verified `--check` passes for all 14 langs + python. (fixed benchmarks) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs/chore: scrub remaining REGISTRY_PRIMARY + deleted-symbol references (#942) Tri-review P3 follow-ups (verified): - TESTING.md: rewrite the "Scope-resolution parity" section — the legacy dual-leg (REGISTRY_PRIMARY_<LANG>=0/1) and `npm run test:parity` no longer exist; resolver tests run once on the sole scope-resolution path in the normal tests job. - scripts/bench-scope-resolution.ts: drop the inert `REGISTRY_PRIMARY_PYTHON=1` env set + usage hint (the flag is gone). - ruby/scope-resolver.ts, php/captures.ts: re-point doc-comments off the deleted heritage-map.ts / heritage-processor.ts to the current behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): prettier format + regenerate scope-capture goldens (#942) Two more CI failures, same root cause as the bench re-baseline (the test-cleanup reworded comments in lang-resolution bench/golden-corpus fixtures): - quality/format: prettier on tree-sitter-languages.test.ts (blank line left by the deleted heritage-capture tests) + TESTING.md (the rewritten section). - tests/ubuntu/coverage: `csharp-captures-golden` (and python/ruby/rust) drifted because the edited fixtures feed the per-language capture-golden snapshots too (not just the bench). Regenerated via UPDATE_GOLDEN=1. Verified safe: only the edited-fixture entries changed; csharp `captureGroups` unchanged (38) — digest shifted from comment-position only; capture LOGIC untouched. 1168 scope- resolution tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(resolvers): drop createResolverParityIt wrapper, use vitest it directly The parity-aware `it` wrapper became a no-op when #942 removed the legacy call-resolution DAG (it just returned vitest's `it`). Remove it entirely so the resolver tests call vitest's `it` directly instead of shadowing it with a local `const it` (or `pit`/`rustParityIt`): - helpers.ts: delete createResolverParityIt + its now-unused vitestIt import and VitestIt type. - 16 files: drop `const it = createResolverParityIt('x')` and import `it` from vitest instead. - ruby.test.ts (pit) + rust.test.ts (rustParityIt): rename calls to `it`. - Scrub every comment that described the removed wrapper / dual-mode parity skip / legacy_skip gate (vue-scope, js/ts/dart/php/python headers, rust x2, cpp, swift x4, rust-coverage). Genuine test rationale is kept; only the vestigial two-leg framing is dropped. Accurate "legacy DAG (removed in #942)" historical notes are retained. No fixtures touched (no bench/golden re-baseline). tsc clean; rust+ruby resolver suites green (323 tests, incl. #1992 worker-path parity after a local dist build). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9f3bcee7fc
|
fix(cpp): resolve cross-namespace same-tail inheritance bases bridge-held (#1993) (#2005)
* fix(cpp): resolve cross-namespace same-tail inheritance bases bridge-held (#1993) PR #1981's bridge fixed within-namespace same-tail heritage (NS::A::Inner vs NS::B::Inner). The residual: a cross-namespace same-tail base (NS1::A::Inner vs NS2::A::Inner) both key the namespace-omitted `A.Inner` in the qualifiedNames index, so resolveQualifiedInheritanceBase couldn't pick a winner and the deriving classes cross-wired (DB's EXTENDS bound to NS1's A::Inner). Fixed bridge-held via the existing `namespacePrefix` sidecar — no qualifiedName invariant flip, no resolution-index re-keying: (1) tagNamespacePrefixes also tags defs declared directly in a namespace (the deriving NS1::DA), composed identically to the class-nested path; (2) resolveQualifiedInheritanceBase breaks a same-tail tie by preferring the candidate whose namespacePrefix matches the deriving class's. Two-phase lookup, UDC, brace-init, file-local linkage untouched (def.qualifiedName + index keys unchanged). New cpp-cross-namespace-same-tail fixture + registry-primary test (in the cpp parity expected-failures). Verified: cpp suite 287/287 primary, 209 + 78 skips legacy — no regression; tsc + prettier clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cpp): worker-path parity for #1993 cross-namespace tie-break + correct narrative Add the missing parse-worker.ts parity describe for the #1993 cross-namespace same-tail heritage tie-break, mirroring the #1982/#1995 worker siblings (workerThresholdsForTest minFiles:1/minBytes:1, workerPoolSize:2, usedWorkerPool guard, and the same NS1.DA→NS1.A.Inner / NS2.DB→NS2.A.Inner base assertions), and register both worker test names in LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES['cpp'] (registry-primary-only, like the sequential entry). Closes the DoD sequential≡worker gap flagged in the tri-review of PR #2005. Also correct the fixture/test narrative: the pre-fix failure is a CROSS-WIRE (DB's EXTENDS binds NS1::A::Inner via the refuse-on-tie scope-walk fallback), not a silent miss — the empirical pre-fix run shows the edge exists but points at the wrong target. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(scope-resolution): type the namespacePrefix sidecar; regen cpp bench baseline (#1993) F4 follow-up to #1993: declare `namespacePrefix?: string` on SymbolDefinition (gitnexus-shared) and drop the six `as { namespacePrefix?: string }` casts in walkers.ts / graph-bridge/ids.ts that #1993 introduced. Pure type-level — the `as` assertions erase at compile time, runtime is byte-identical, and the field stays a sidecar (no graph-node identity; the qualifiedName-keyed index is untouched). Also regenerate the cpp scope-capture bench baseline: rebased onto main (now carrying #1995's cpp fixtures), #1993 adds cpp-cross-namespace-same-tail, growing the cpp-* corpus 272->273 and drifting the fingerprint d63ded6->6d6207ae. Pure fixture-corpus drift — no scope-extractor change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e316222cd5
|
fix(cpp): distinct nodes for union- and anonymous-namespace-nested same-tail types (#1995) (#2004)
* fix(cpp): qualify types nested in a named union by their union scope (#1995) `union_specifier` was missing from cppClassConfig.ancestorScopeNodeTypes, so a struct nested in `union U1` and one in `union U2` both qualified to the bare `Inner` and merged onto one Struct:...:Inner node — from_u1/from_u2 cross-wired (invisible to findDanglingEdges). Adding `union_specifier` lets buildQualifiedName pick up the named union's `name` segment, materializing distinct `U1.Inner` / `U2.Inner` nodes. Anonymous unions have no `name` child and correctly contribute nothing (members inject into the enclosing scope); the separate C config is untouched. New fixture + positive-identity tests (sequential + worker, both legs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cpp): distinct nodes for anonymous-namespace-nested same-tail types (#1995) An anonymous `namespace { }` is a namespace_definition with no `name` child, so the scope walker dropped it (empty segment) and two `namespace { struct Inner {} }` blocks in one TU collapsed onto a single `Inner` node — from_anon_a/from_anon_b cross-wired. A C++ `extractScopeSegments` override (the first consumer of the existing config hook) gives each anonymous namespace a deterministic per-block discriminator from its start byte, keeping the nested types distinct. Named scopes (incl. `inline namespace`) and anonymous unions are unaffected. Deterministic across the sequential and worker full-file parses. New fixture + tests assert node DISTINCTNESS (count==2 / distinct owners), not the non-portable discriminator value. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cpp): regenerate cpp scope-capture bench baseline for #1995 fixtures Rebased onto main (which now carries #1992 + its rust baseline). #1995 adds the cpp-union-nested-tail-collision and cpp-anon-ns-tail-collision fixtures, growing the cpp-* corpus 270->272 and drifting the order-independent fingerprint (538e8be -> d63ded6). Pure fixture-corpus drift — no scope-extractor change; existing fixtures' captures byte-identical. (cpp has no captures-golden gate, so only the bench baseline needs regenerating.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c11f50a06e
|
fix(ingestion): own generic Rust inherent-impl methods through the mod-qualified Impl node (#1992) (#2003)
* fix(ingestion): own generic Rust inherent-impl methods through the mod-qualified Impl node (#1992) A generic inherent-impl target (`impl<T> Inner<T>`) is a `generic_type` node, which the inherent-impl owner walk (findEnclosingClassInfo) did not match — so the walk returned null and the method got `File -> DEFINES` with NO HAS_METHOD edge (orphaned, and invisible to findDanglingEdges). The Impl node was already correctly mod-qualified (the @name capture drills into the inner type_identifier, tree-sitter-queries.ts), so this is an owner-walk-only fix: drill into the generic base and mirror the node gate so the owner id == the node id byte-for-byte. A scoped-generic target (`impl<T> a::Inner<T>`) materializes no Impl node and is left orphaned (deferred) rather than minting a phantom owner. The owner walk is shared by the sequential and worker paths. New fixture + tests assert positive HAS_METHOD ownership through distinct `a.Inner` / `b.Inner` nodes on both resolver legs and the worker path, plus a negative scoped-generic guard. rust-captures-golden regenerated additively for the new fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): qualify className for same-tail Rust generic impls + regen rust bench baseline (#1992) F3 follow-up to #1992: two same-tail generic inherent impls under sibling mods that ALSO share a method name (`mod a { impl Inner { fn m } }` + `mod b { impl Inner { fn m } }`) keyed the method node id `${className}.${name}` with the bare tail (`Inner.m`) and collapsed onto one Function node (graph addNode is first-write-wins), silently dropping the second. The owner Impl `classId` was already mod-qualified, masking the collision behind distinct HAS_METHOD sources. Qualify `className` (`a.Inner` / `b.Inner`) in the bare inherent-impl arm so the node id inherits the mod scope; symmetric with the call-resolution fallback, and the HAS_METHOD owner anchors on the unchanged qualified classId. New same-method-name fixture + sequential & worker-parity tests; holds on both legs. Also regenerate the rust scope-capture bench baseline: the new rust-nested-tail-collision-generic (#1992) + rust-generic-impl-same-method-name (F3) fixtures grow the rust-* corpus, so the order-independent fingerprint drifts (56ffc1c0 -> b00aea0f, fixture_count 127 -> 129). Pure fixture-corpus drift — no scope-extractor change; existing fixtures' captures byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(rust): regenerate rust-captures golden for the F3 same-method-name fixture (#1992) The rust-* scope-capture corpus is fingerprinted by TWO gates: the bench baseline (bench/scope-capture/baselines.json, already updated) and the rust-captures-golden unit test (test/fixtures/rust-captures-golden/expected-captures.json). Adding the F3 fixture rust-generic-impl-same-method-name grew the corpus 128->129 entries, so the committed golden drifted too. Regenerated additively (UPDATE_GOLDEN=1) — only the new fixture's entry is added; existing fixtures' captures are byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c2b4ec6c31
|
feat(vue): migrate Vue SFC to scope-based resolution (RFC #909 Ring 3, closes #940) (#1950)
* feat(vue): migrate Vue SFC to scope-based resolution (RFC #909 Ring 3, closes #940) Adds `vueScopeResolver` and wires Vue into the scope-resolution pipeline (`SCOPE_RESOLVERS`, `MIGRATED_LANGUAGES`). Vue's `<script>` / `<script setup>` blocks are TypeScript — `emitVueScopeCaptures` extracts the script block via the existing `extractVueScript` utility and delegates to `emitTsScopeCaptures`, keeping grammar identity consistent with the cached tree the parse-worker already builds. - `languages/vue/captures.ts` — `emitVueScopeCaptures` - `languages/vue/import-target.ts` — `makeVueResolveImportTarget` (TS resolver + tsconfig path-alias support; explicit `.vue` imports resolve via the exact-path branch) - `languages/vue/scope-resolver.ts` — `vueScopeResolver` - `languages/vue/index.ts` — barrel + known-limitations doc - `languages/vue.ts` — `emitScopeCaptures` hooked up - `scope-resolution/pipeline/registry.ts` — Vue entry added - `registry-primary-flag.ts` — `SupportedLanguages.Vue` added to `MIGRATED_LANGUAGES` (production default → registry-primary) - `vue-composition-api` — `<script setup lang="ts">`, defineProps / defineEmits macros, cross-file TS imports, computed refs - `vue-options-api` — `defineComponent({methods, computed, data})`, this-based method calls, imported utility calls - `vue-cross-file` — composable functions returning class instances, multi-level import chains, UserModel/PostModel method calls - `fieldFallbackOnMethodLookup: true` — Options API `this.X()` calls may not resolve through the type-binding layer (no formal class); fallback catches common patterns via declared field names. - `allowGlobalFreeCallFallback: false` — Vue uses explicit imports; workspace-wide unique-name fallback would produce spurious edges for built-ins (ref, reactive, defineProps, …). - Template expression calls intentionally out of scope: component- reference CALLS edges are already emitted by the legacy template extractor. Remaining template gaps tracked in #1647. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(vue): address P0/P1 review findings from #1950 ## P0 #1 — missing scope-resolution hooks in vueProvider `pass3CollectImports` early-returns when `interpretImport` is undefined, producing zero IMPORTS and zero cross-file CALLS edges. Add the four hooks to `vueProvider` in `vue.ts`: - `interpretImport: interpretTsImport` - `interpretTypeBinding: interpretTsTypeBinding` - `bindingScopeFor: tsBindingScopeFor` - `importOwningScope: tsImportOwningScope` Also add `receiverBinding`, `mergeBindings`, `arityCompatibility`, and `resolveImportTarget` to complete the scope-resolution contract. ## P0 #2 — template-component CALLS dropped when Vue is registry-primary `isRegistryPrimary(Vue) → true` makes the main call-processor loop skip Vue files entirely, silencing the inline `vue-template-component` CALLS emitter at ≈L1506. Add a dedicated post-loop pass in `call-processor.ts` that emits template-component CALLS for Vue files whenever Vue is registry-primary. Update the stale `vue/index.ts` limitation comment to reflect the new emit site. ## P1 #3 — worker-mode double-extraction → zero captures In worker mode (≥15 files) the parse worker pre-extracts the `<script>` block and passes `scriptContent` as `sourceText`. `emitVueScopeCaptures` was calling `extractVueScript` a second time, getting null, and returning `[]`. Fix: if extraction returns null and the content has no SFC block- level markers (`<template`, `<style`), treat it as already-extracted script text and delegate directly to `emitTsScopeCaptures`. ## Test assertion strictness Replace all `toBeGreaterThanOrEqual(1)` assertions with exact `toBe(N)` counts. IMPORTS counts reflect per-symbol scope-based edges (value imports only; `import type` is not emitted as an IMPORTS edge). CALLS counts are 1 per single-call-site. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(vue): template-derived edges + pipeline benchmark (#1950 review) Addresses the reviewer's request for template edge attribution and a performance benchmark. ## Template event-handler CALLS (`vue-template-callback`) Add `extractTemplateEventHandlers` to `vue-sfc-extractor.ts`. Extracts bare single-identifier handlers from `@event="methodName"` and `v-on:event="methodName"` attributes. Inline expressions with arguments or operators (`@click="toggle(item)"`) are intentionally excluded. Wire into the dedicated registry-primary Vue template pass in `call-processor.ts`. For each extracted handler name, `ctx.resolve` finds the in-file Function/Method node and emits a CALLS edge with `reason: 'vue-template-callback'`. ## Template attribute-binding ACCESSES (`vue-template-attribute`) Add `extractTemplateAttributeBindings` to `vue-sfc-extractor.ts`. Extracts bare single-identifier values from `:prop="varName"` and `v-bind:prop="varName"` bindings. Member-access (`:key="post.id"`) and literals are excluded by the identifier-boundary regex. Wire into the same template pass. For each extracted variable, `ctx.resolve` finds the in-file node and emits an ACCESSES edge with `reason: 'vue-template-attribute'`. ## `vue/index.ts` limitations comment Updated to accurately describe all three categories of template-derived edges and explicitly document the complex-expression exclusions. ## Tests Add 6 new assertions in `vue-scope.test.ts`: - `@click="handleSave"` → CALLS `handleSave` (UserProfile.vue) - `@select="onPostSelected"` → CALLS `onPostSelected` (App.vue composition) - `@keyup.enter="addTodo"` → CALLS `addTodo` (TodoList.vue) - `@loaded="onUserLoaded"` → CALLS `onUserLoaded` (App.vue cross-file) - `:userId="currentUserId"` → ACCESSES `currentUserId` (App.vue composition) - `:posts="allPosts"` → ACCESSES `allPosts` (App.vue composition) Add `vue` entry to `LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES` in `helpers.ts` documenting which assertions are registry-primary-only (IMPORTS cardinality, template-derived edges, `<script setup>` export). ## Benchmark Add `vue-pipeline-benchmark.test.ts` (gated by `GITNEXUS_BENCH=1`). Generates N-component synthetic repos (10 / 25 / 50 / 100) and asserts that wall-clock and node counts scale sub-quadratically with component count, guarding against O(n²) regressions in the template extraction or scope-resolution passes. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(vue): BINDS_EVENT_HANDLER/EMITS_EVENT edges via ScopeResolver hook Per maintainer feedback on PR #1950: - Do not edit call-processor.ts (will be removed when all languages migrate) - Model Vue component-event system with dedicated edge types to avoid CALLS noise in deep component hierarchies (per contributor discussion) Changes: - gitnexus-shared: add BINDS_EVENT_HANDLER and EMITS_EVENT to RelationshipType - vue-sfc-extractor: add extractComponentEventBindings, extractNativeElementEventHandlers, and extractScriptEmitCalls - ScopeResolver contract: add optional emitPostResolutionEdges hook - run.ts: wire emitPostResolutionEdges after emitImportEdges - vue/scope-resolver: implement emitPostResolutionEdges emitting: 1. CALLS (vue-template-component) — PascalCase component File refs 2. CALLS (vue-template-callback) — @event on native HTML elements 3. BINDS_EVENT_HANDLER (vue-event: @name) — @event on component elements; source = handler fn in parent, target = child component File (not CALLS) 4. EMITS_EVENT (vue-emit: name) — emit() calls; self-loop on component File, joinable with BINDS_EVENT_HANDLER via Cypher for impact tracing 5. ACCESSES (vue-template-attribute) — :prop="var" bindings - call-processor.ts: revert dedicated Vue post-loop pass; moved to scope resolver - Tests and parity expected-failures updated accordingly Co-authored-by: Cursor <cursoragent@cursor.com> * fix(vue): close review gaps in scope/parity extraction Resolve the new PR #1950 review findings by widening Vue scope context to include TS/JS import closures, fixing BINDS_EVENT_HANDLER endpoint assertions, hardening emit/event extraction to avoid comment/property false positives, supporting kebab-case component tags, and ensuring parity runs include vue-scope suites. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(vue): address second review round — regex safety, emit coverage, arch Closes items raised in the Jun 2 review comment on PR #1950. Correctness fixes: - ReDoS mitigation: bound attribute-capture spans to [^>]{0,512}? in all three template tag regexes to prevent pathological backtracking. - Kebab-case misclassified as native: added (?![A-Za-z0-9-]) negative lookahead to NATIVE_TAG_RE so <post-list> is no longer split as native tag `post` with attrs `-list ...`. - Hyphenated event names dropped: widened TAG_EVENT_RE from [\w:.]+ to [\w:.-]+ so @user-loaded and @update:model-value are captured. - this.$emit silently dropped: collectBareEmitEventNames now allows this.$emit(...) by looking back past the '.' to verify preceding token is exactly `this`; socket.emit etc. remain blocked. - Event names with colon rejected: extended validator to accept update:modelValue and update:model-value patterns. Architecture fix: - Moved collectVueScopeFilePaths out of shared phase.ts into a new collectScopeContextPaths optional hook on ScopeResolver, keeping shared pipeline code language-agnostic. vueScopeResolver implements the hook. - Fixed memory leak: preExtractedByPath cleanup now iterates filePaths (all context files) not just primaryFilePaths (only .vue files). Cleanup: - Removed unused extractTemplateEventHandlers and duplicate EVENT_HANDLER_RE. - Fixed skipped comment numbers in emitPostResolutionEdges (1,2,4,5,6 -> 1-6). - Updated vue/index.ts: four categories -> five (added EMITS_EVENT). - Fixed gitnexus-shared EMITS_EVENT JSDoc to reflect File->File reality. Tests: 7 new unit tests covering hyphenated events, this.$emit, kebab-case native-tag exclusion, and update:modelValue event name validation. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(vue): eliminate double file-read and per-file template re-scans Two performance fixes from the self-review pass: 1. **No more double read of .vue files in phase.ts**: primary files were previously read once for `collectScopeContextPaths` (via `entryFileContents`) and again in the blanket `readFileContents(filePaths)` call. Now the primary-file map is passed directly and only the extra context files (TS/JS import closure) require a second I/O round-trip. 2. **Single template parse per .vue file in emitPostResolutionEdges**: previously each of the five extractor functions (components, native handlers, component event bindings, emit calls, attribute bindings) ran `TEMPLATE_RE.exec(content)` independently — five full-file scans per `.vue` file. Replaced with a new `extractVueTemplateEdgeData` batching helper that parses the template and script blocks once and feeds all five extractors from the pre-extracted content. emitPostResolutionEdges now calls a single function and destructures the results. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(parity): exclude TypeScript HOC/HOF/JSX scope-resolver tests from legacy DAG parity gate Three test files introduced in prior PRs exercise scope-resolver-only correctness wins: HOC-wrapped const declarations, HOF-callback caller attribution, and JSX-as-call CALLS edges. The parity runner's ${slug}-*.test.ts glob now picks them up, causing typescript [legacy] failures in CI. Fix: convert each file to use createResolverParityIt('typescript') and register all 26 legacy-failing test names in LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.typescript with explanatory comments. Legacy mode: 11+11+4 tests skipped, zero failures. Registry-primary mode: all 37 tests pass as before. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(test): remove registry-primary-flag unit tests after migration complete All languages are now in MIGRATED_LANGUAGES; the per-language flip tests are no longer needed. Addresses PR #1950 review feedback. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
c60ad9f7ab
|
fix(ingestion): fully-qualified nested-type identity for C++/Ruby — structure (#1978) + resolution (#1982) (#1981)
* fix(ingestion): qualify nested-type node identity for C++/Ruby (#1978) Nested types sharing a tail name in one file — C++ `Outer::Inner` vs `Other::Inner`, Ruby `Outer::Inner` vs `Other::Inner` modules — silently merged into a single graph node keyed by the simple tail (`Struct:file:Inner`), cross-wiring their methods/properties onto one owner. Key class-like type nodes (Class/Struct/Interface/Enum/Record) by their normalized fully-qualified path (`Struct:file:Outer.Inner`) instead of the simple name. Gated per-language by a new `qualifiedNodeId` config flag (default false → byte-identical for every other language); enabled here for C++ and Ruby. - class-types.ts / generic.ts: `qualifiedNodeId` flag on ClassExtractor + config - ast-helpers.ts: findEnclosingClassInfo gains an optional getQualifiedOwnerName hook + EnclosingClassInfo.qualifiedClassId, so member-owner edges resolve to the qualified class node id (owner id == node id by construction) - parsing-processor.ts + parse-worker.ts: flag-gated qualified node-id + owner edges on both the sequential and worker parse paths (incl. routed properties) - call-processor.ts: same qualifier in the routed-property pre-pass (lockstep with the worker `kind === 'properties'` block) - configs/c-cpp.ts, configs/ruby.ts: qualifiedNodeId: true Method/Property node ids stay simple-qualified; only type nodes get the qualified id. Deferred to a resolution-side follow-up: Ruby SAME-TAIL routed-property/mixin owner identity under registry-primary (`emitRubyMixinEdges` keys owners by the simple tail name, last-wins); and Rust inherent-impl methods (impl_item is not a typeDeclaration — its #1978 test is describe.skip). Tests: same-tail collision fixtures + #1978 resolver tests for C++/Ruby (positive owner identity, R7), a worker-path parity block, and an unambiguous nested attr_accessor case; the C++ #1975 out-of-line test updated to assert qualified-id distinctness (forward-decl + out-of-line now unify). Verified green on both parity legs, the worker path, and tsc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ingestion): scope #1978 resolver tests to registry-primary leg; fix lint - helpers.ts: exclude the new #1978 C++/Ruby resolver tests from the legacy parity leg (LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES). They PASS on legacy too — the fix lives in the SHARED structure phase, not the legacy resolution path — so this is a deliberate registry-primary-only scoping (not a legacy gap), keeping the legacy path untouched and uncoupled from the new node-identity behavior. - rust.test.ts: drop the `eslint-disable vitest/no-disabled-tests` directive. That rule isn't configured in this repo, so eslint errored "Definition for rule 'vitest/no-disabled-tests' was not found" and failed `quality / lint`. The describe.skip needs no disable directive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(test): satisfy CI for the new #1978 fixtures (format + golden + fingerprint) Adding the {cpp,ruby,rust}-nested-tail-collision fixtures changed the lang-resolution corpus, which the scope-capture golden snapshots and the fingerprint baselines gate on. These are pure fixture-corpus additions — #1978 does not touch the scope-capture phase (captures.ts / emit*ScopeCaptures are unchanged). Verified: the regenerated ruby/rust golden diffs are additive-only (no existing fixture's capture digest changed), so the cpp/ruby/ rust fingerprint drift is solely the new fixtures. - prettier --write test/integration/resolvers/{ruby,rust}.test.ts - regenerate ruby/rust captures-golden snapshots (UPDATE_GOLDEN=1; +1 fixture each) - rebaseline cpp/ruby/rust scope-capture fingerprints (bench/scope-capture/baselines.json) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ingestion): extract shared qualified-name normalizer (#1982) Move normalizeQualifiedName/splitQualifiedName out of class-extractors/ generic.ts into utils/qualified-name.ts so the structure-phase buildQualifiedName, the scope-resolution inheritance resolver, and the per-language capture emitters can all key against ONE normalizer. A raw '::' qualifier must normalize to the exact '.'-joined key the QualifiedNameIndex already holds, or the qualified lookup silently misses (the #1982 resolution-side foundation). Pure relocation — byte-identical function bodies; tsc clean; existing C++ nested-collision tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): resolve same-tail C++ nested-type heritage to the correct qualified node (#1982) Registry-primary C++ inheritance (preEmitInheritanceEdges -> resolveInheritanceBaseInScope) resolved a same-tail nested base by its SIMPLE TAIL with first-wins, so `struct DerivedB : Other::Inner` mis-resolved EXTENDS to Outer.Inner (the wrong sibling; 0 dangling, so undetected). The namespace qualifier was discarded at the C++ inheritance capture. Fix (additive, qualified-first): - ReferenceSite gains an optional `rawQualifiedName`; the C++ inheritance capture emits `@reference.qualified-name` (qualifier-preserving, template-stripped: Other::Inner, ns::Base<T> -> ns::Base) only when the base is qualified, registered as a sub-tag so it can't shadow the `@reference.inherits` anchor. - resolveInheritanceBaseInScope resolves the qualifier against the full-path QualifiedNameIndex FIRST (which already carries Outer.Inner / Other.Inner keys from the structure phase), with progressive-prefix lookup for relative bases and refuse-on-tie, falling through to the existing simple-tail walk on miss — so unqualified bases and the single-candidate cross-file case are unchanged. Registry-primary cpp.test.ts 278/278 (incl. worker-path: rawQualifiedName survives worker serialization). Legacy leg unaffected (207 pass / 71 skip) — the new resolution-side assertions are registry-primary-only via helpers.ts. tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): resolve same-tail Ruby mixin/attr_accessor owners to the correct qualified node (#1982) emitRubyMixinEdges keyed its owner map by the SIMPLE tail (def.qualifiedName split-popped) with last-wins, and the __heritage__/__property__ markers carried only the immediate owner name — so `module Outer; class Inner` and `module Other; class Inner` collapsed onto one `Inner` key and cross-wired their include/attr_accessor edges onto whichever Inner was processed last. Fix (lockstep, full-qualified): - ruby/captures.ts: build the marker owner from the FULL enclosing class/module chain (buildEnclosingQualifiedName walks all ancestors, normalizing the compact `class Outer::Inner` scope_resolution form via the shared splitQualifiedName) so the marker owner byte-matches the resolution def's qualifiedName. - ruby/scope-resolver.ts: key graphIdByName by the full def.qualifiedName instead of the simple tail. Top-level owners/mixins are unchanged (full == simple). Registry-primary ruby.test.ts 142/142 incl. a new worker-path block (the deferred note's duplicate-edge concern: markers survive worker serialization, exactly one HAS_PROPERTY per attr). Legacy leg unaffected (136 pass / 6 skip) — new assertions registry-primary-only via helpers.ts. tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ingestion): rebaseline #1982 golden/fingerprint + lint/format sweep Cross-cutting verification artifacts for the #1982 same-tail resolution fix: - ruby capture golden regenerated: ONLY the ruby-nested-tail-collision fixture drifts (+10 capture groups from its new include/attr_accessor + the now full-qualified __heritage__/__property__ marker owner). All other ruby fixtures byte-identical (proves the owner-qualification is localized to nested owners). - bench/scope-capture/baselines.json: rebaseline cpp + ruby fingerprints (the only two that drift; 12 other languages byte-identical). cpp = additive @reference.qualified-name capture; ruby = the localized owner change. Provenance notes record both. scaling linear (~1.0), 14/14 PASS. - generic.ts: drop the now-unused normalizeQualifiedName import (lint error). - walkers.ts / ruby.test.ts: prettier formatting. Verified: cpp 278/278 + ruby 142/142 (registry-primary), both legacy legs clean (skips registry-primary-only assertions), go/java/csharp 542 (cross-language regression — the qualified-first branch is gated on rawQualifiedName, set only by C++, so non-C++ inheritance resolution is unchanged). tsc + eslint(0 errors) + prettier clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): resolve nested Ruby mixin included by short name (#1982) emitRubyMixinEdges keyed graphIdByName by the full def.qualifiedName on the owner side, but the __heritage__ marker carries the mixin target as the bare written name (arg.text). A nested mixin module included by its short name (include Loggable where it is App::Loggable) missed the full-qn map and its IMPLEMENTS edge was silently dropped (0 dangling, undetectable). The shipped same-tail fixture used only top-level mixin modules, so CI stayed green. Add a secondary simple-tail fallback map consulted only when the full-qn mixin lookup misses; owner lookups stay full-qn so same-tail owner disambiguation is preserved. Characterization test + fixture (registry-primary only); golden regenerated additively. Addresses PR #1981 review (4417182679) P1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): normalize qualified Ruby mixin arg in heritage marker (#1982) `include Outer::Mixin` embedded the raw `Outer::Mixin` into the ':'-delimited __heritage__ marker, so the `::` collided with the field separator and emitRubyMixinEdges mis-split it (className became empty), dropping the IMPLEMENTS edge. Normalize the mixin arg via splitQualifiedName(...).join('.') before emit so the marker carries the dotted form, which both parses correctly and matches the mixin def's qualifiedName. Simple names are unchanged (no golden drift). Addresses PR #1981 review (4417182679) secondary R2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): resolve C++ same-tail nested heritage inside a namespace (#1982) A namespace-nested C++ type's scope-model qualifiedName carried its enclosing CLASS chain (A.Inner) but dropped the enclosing NAMESPACE, while the structure-phase graph node is keyed by the full path (NS.A.Inner). resolveDefGraphId's qualifiedKey therefore missed and fell back to simpleKey('Inner'), collapsing same-tail nested bases across sibling namespace members — DB : B::Inner pointed at NS.A.Inner. The shipped fixture was top-level only, so it could not catch this. Fix without disturbing the qualifiedName-keyed resolution index (an earlier attempt that rewrote qualifiedName regressed brace-init / UDC / two-phase namespace resolution): tagNamespacePrefixes records each namespace-nested def's enclosing-namespace prefix on a sidecar field, and resolveDefGraphId retries the node lookup with the namespace-prefixed key before the simpleKey fallback. The helper is language-agnostic (acts only on Namespace scopes) and opt-in — only the C++ provider calls it. Namespaced fixture + sequential & worker tests (registry-primary only). All 280 cpp resolver tests pass; tsc clean. Addresses PR #1981 review (4417182679) P2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ingestion): worker-path parity for Ruby mixin IMPLEMENTS + C++ DerivedA (#1982) The Ruby worker-path parity block asserted only attr_accessor (HAS_PROPERTY); add an IMPLEMENTS assertion so a dropped/cross-wired mixin owner on the worker path is caught (the __heritage__ marker owner must survive serialization). The C++ worker heritage block asserted only DerivedB; add a DerivedA assertion with a toHaveLength(1) duplicate guard. Registry-primary only. Addresses PR #1981 review (4417182679) test-coverage gap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): distinct Rust same-tail nested-mod inherent-impl ownership (#1982) Rust methods live in `impl Inner` blocks, and findEnclosingClassInfo keyed the inherent-impl owner by the target's RAW tail (`Impl:lib.rs:Inner`), so two same-tail `impl Inner` blocks under different mods (mod outer / mod other) collapsed onto ONE Impl node and their methods cross-wired. The shipped fixture test for this was skipped/deferred. Qualify an UNSCOPED inherent-impl target by its enclosing `mod_item` scope (`outer.Inner`) in BOTH the owner walk (ast-helpers.qualifyRustImplTargetByModScope) and the Impl-node materialization (parsing-processor + parse-worker, lockstep) so the owner edge and node id agree byte-for-byte. Gated on the Impl label + impl_item + an unscoped type_identifier target — Rust-impl-exclusive, so C++/Ruby and the rust captures golden are untouched; a SCOPED `impl a::Inner` keeps its full raw text (#1975, unchanged). The previously-skipped distinct-ownership test is now active and passing; rust 170/170, cpp+ruby+golden 437/437, tsc clean. Done in-PR at maintainer request (was deferred as a follow-up). Addresses PR #1981 review (4417182679) test-coverage gap R7. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ingestion): single qualified-name normalizer + module-scoped Ruby PROPERTY_PREFIX (#1982) Replace cpp/captures.ts's parallel normalizeCppNamespaceQName with the shared normalizeQualifiedName (behaviorally equivalent for C++ qualified-identifier inputs: '::'->'.' with leading/trailing-:: handling; no interior whitespace reaches it). Promote Ruby's PROPERTY_PREFIX to module scope alongside HERITAGE_PREFIX (was function-local — asymmetric with no behavioral effect). Maintainability only; cpp+ruby resolver suites 428/428, tsc clean. Addresses PR #1981 review (4417182679) maintainability item. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf+fix(ingestion): single enclosing-class walk + root-anchored base guard (#1982) U7 (perf): preEmitInheritanceEdges resolved the deriving class AND resolveQualifiedInheritanceBase re-walked findEnclosingClassDef for the same site. Resolve callerClass once and thread it into resolveInheritanceBaseInScope -> resolveQualifiedInheritanceBase -> enclosingScopeSegments, so the enclosing class is walked once per qualified site. Add a 'program' early-exit to buildEnclosingQualifiedName (ruby/captures.ts). Behavior-preserving. U8 (P3): a root-anchored C++ base ": ::A::Inner" names the GLOBAL type, but resolveQualifiedInheritanceBase prepended the deriving class's enclosing segments and could mis-bind to an enclosing-relative same-path type. Detect the leading "::" on the raw qualifier and try only the root-anchored key. Discriminating fixture + test (registry-primary only). cpp+ruby+rust resolver suites 599/599; tsc clean. Addresses PR #1981 review (4417182679) perf + P3 items. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ingestion): rebaseline ruby+cpp scope-capture fingerprints for new #1982 fixtures The four new fixtures (ruby-nested-mixin-shortname, ruby-qualified-mixin, cpp-namespaced-collision, cpp-global-base-anchor) grow the lang-resolution corpus, drifting the ruby and cpp order-independent capture fingerprints. Verified purely additive: the ruby captures golden shows only the two new fixtures added (existing byte-identical), and removing the two cpp fixtures reverts the cpp fingerprint to the prior baseline (so the U3/U6/U8 code changes are scope-resolution / behavior-preserving, not capture-emission). measure.mjs --check PASS (14 languages). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(ingestion): prettier-wrap ruby resolver test call (#1982) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f1b8438388
|
perf(cpp): index ADL candidates once instead of per-site rescans (#1990)
* perf(cpp): index ADL candidates once instead of per-site rescans C++ scope-resolution `emit` dominated large-repo analysis (~6.76h on a 5,969-file repo — ~70% of the total run). `pickCppAdlCandidates` ran once per unresolved ADL-eligible call site and each time: - rescanned every parsed file (rebuilding a per-file scope map per call), - scanned every workspace def (`findCppClassDefBySimpleName`), and - used an O(scopes²) child-scope walk for hidden friends. That is O(unresolved sites × files); with hundreds of thousands of unresolved C++ sites the emit phase went super-linear. `resolve` (registry lookup) was only 3.5s — the cost was entirely in fallback edge emission. Build an `AdlCandidateIndex` once per run (lazy, guarded by `parsedFiles` identity, reset in `clearCppAdlState`) and query it per site: - `classDefsBySimple` — preserves `defs.byId` order so first-match / ambiguous semantics are identical to the legacy linear scan. - `nsCandidates` — namespace-owned callables, with inline-namespace transparency. - `friendCandidates` — hidden-friend + class-member callables; a parent→children scope index replaces the O(scopes²) walk. - `nsFunctionsByQName` / `nsFunctionsBySimple` — function-reference ADL path. A monotonic `seqByNodeId` (file-major; namespace defs before friend/member defs within a file) lets the per-site query merge candidates across associated namespaces, dedup by nodeId, and sort — reproducing the exact legacy candidate set and order. Per-site cost drops from O(sites × files) to O(associated namespaces); the emit phase goes from linear-in-sites to flat. Benchmark (files=80): emit at 1000 sites 232ms → 9ms, 2000 sites flat at 17ms; the eliminated term scales with file count, so the speedup is ~1000×+ on the real 5,969-file repo. Behavior is unchanged: synthetic candidate output is byte-identical before/after, all 270 C++ integration resolver tests and 4/4 resolver-parity-expected-failures pass, and tsc + eslint are clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(cpp): correct ADL state-lifecycle and cache-guard comments The header lifecycle block listed three module-level maps and named clearFileLocalNames as the reset caller; both became inaccurate when the candidate index was added. Enumerate all five state pieces, name the real caller (loadResolutionConfig), and document that ensureAdlIndex's staleness guard keys on parsedFiles identity while the index also depends on scopes and classToNamespaceQualifiedName. Addresses PR #1990 tri-review (U1, U3). Doc-only; no behavior change. * test(cpp): guard the ADL seq-coverage invariant in dev/test pickCppAdlCandidates sorts merged candidates by seqByNodeId with a `?? 0` fallback. That fallback is unreachable today (every bucketed def is seq-assigned in the same build block), but a future regression could break it and silently collapse two seq-0 candidates, dropping a CALLS edge with no error. Add validateAdlSeqCoverage and run it from buildAdlIndex under the resolver's opt-in validation gate (NODE_ENV!=production && VALIDATE_SEMANTIC_MODEL!=0), so a broken invariant throws loudly in dev/CI instead. Production behavior and the hot path are unchanged. Unit-tested; 270/270 cpp integration tests pass with the guard active. Addresses PR #1990 tri-review (U2). * test(cpp): parity fixture for ADL hidden-friend + namespace-callable merge pickCppAdlCandidates merges friendCandidates (hidden friends of associated classes) and nsCandidates (namespace-owned callables) for a single associated namespace. The byte-identical-parity claim rested only on an uncommitted harness. Add a fixture that reaches one callable through each bucket — combine only via a hidden friend, process only via a namespace member — so dropping either bucket from the merge fails the suite. Candidate order is not observable (narrowing resolves a unique survivor or suppresses), so the guard is on the set. Addresses PR #1990 tri-review (U4). * test(cpp): add ADL emit-scaling benchmark Guards the PR #1990 optimization against reintroducing the O(sites x files) ADL candidate scan. Generates many UNRESOLVED ADL sites (class-typed arg + a callee declared nowhere) and co-scales files and sites with N, so the old cost is O(N^2) and the new cost O(N). Isolates the scope-resolution emit ms from parse-dominated wall time via the logger test destination (capture verified) and asserts the end-to-end emit ratio stays under fileRatio^1.5. Gated by GITNEXUS_BENCH=1; runs build-free (workerPoolSize: 0). Addresses the benchmark request alongside PR #1990 (U5). * test(cpp): add cpp pipeline file-count benchmark Fills the one missing per-language pipeline benchmark (cobol/csharp/go/php/ ruby/rust already have one); modeled on cobol-pipeline-benchmark.test.ts. Generates synthetic C++ with constant per-file work and constant header fan-out, sweeps file count through the full pipeline, and guards linearity with a coarse time-ratio bound plus a deterministic node-ratio bound (the non-flaky guard against reintroducing O(fileCount^2) work). Gated by GITNEXUS_BENCH=1; runs build-free (workerPoolSize: 0). Addresses the benchmark request alongside PR #1990 (U6). * style(cpp): prettier-format adl benchmark * test(cpp): rebaseline scope-capture fingerprint for new ADL fixture The U4 parity fixture (cpp-adl-ns-plus-hidden-friend-same-name) lives under test/fixtures/lang-resolution/cpp-*, so its lib.h + app.cpp join the cpp scope-capture bench corpus (bench/scope-capture/measure.mjs). That is pure fixture-corpus growth — no scope-extractor change, existing fixtures' captures byte-identical — so the cpp fingerprint legitimately drifts (fixture_count 265->267). Rebaseline cpp to match, as #1965/#1975 did for earlier fixture additions. Verified: --check PASS for all 14 languages. Addresses PR #1990 tri-review (U4 follow-on). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
04ade15451
|
fix(rust): scope-resolution coverage gaps — F66,F68,F71,F72 (#1934) (#1974)
* fix(rust): scope-resolution coverage gaps — F66,F68,F71,F72,F73 (#1934) * fix(rust): reviewer fixes — macro namespace, revert pattern:(_), drop variadic * fix(rust): wire macro resolution end-to-end + materialize unions (#1974 review) Addresses the outstanding #1974 review (second batch). Per maintainer decision, F72 is FULLY WIRED rather than documented capture-only. F72 macro — was a capture-only no-op (@reference.macro dropped downstream): - gitnexus-shared: add 'macro' ReferenceKind + Reference.kind; add MACRO_KINDS (['Macro']) and a MacroRegistry that resolves a macro invocation ONLY to a macro_rules! definition — never a same-named free function (the disjoint-namespace guarantee the review required). - scope-extractor: referenceKindFromAnchor @reference.macro -> 'macro'; normalizeNodeLabel 'macro' -> Macro. - resolve-references: route 'macro' sites through MacroRegistry. - emit-references / graph-bridge edges: 'macro' -> USES (kept out of the CALLS keyspace, which denotes function/method dispatch). - node-lookup isLinkableLabel: Macro is linkable, bridging the registry def to the legacy @definition.macro graph node. - rust query: capture macro_rules! as @declaration.macro; fix the scoped macro arm to capture the tail identifier, not the full path (P3). F71 union — the @declaration.struct scope capture had no graph node to resolve to (legacy RUST_QUERIES never captured union_item): - legacy query: capture union_item as @definition.struct so the union is materialized as a Struct node and is genuinely resolvable. - query.ts: document the deliberate union->Struct downgrade rationale. Tests: - rust.test.ts (parity-gated): pipeline-level union resolution + macro resolution (USES to the Macro, exactly one CALLS to fn, none to Macro). Macro resolution is registry-primary-only -> listed in LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES['rust']. - rust-coverage.test.ts: scoped-macro tail + macro-def capture assertions; reframed as capture-layer only, pointing at the pipeline tests. - new fixtures rust-macro, rust-union. F73: dropped from baselines.json _note (variadic was never implemented). Rebaselined the rust capture golden + scope-capture fingerprint (a5fdff2c..., scaling ~0.99, fixture_count 126). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(rust): prettier-format the Reference.kind union (#1974) CI quality/format gate — collapse the multi-line 'macro' addition back to one line (fits the 100-col print width). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <abhigyan1.patwari@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5dcffde9e8
|
fix(go): generic composite literal constructor inference (F33) (#1976) | ||
|
|
5f0d690c60
|
fix(ingestion): materialize graph nodes for scoped class/module/impl declarations (#1975) (#1977)
* test(ingestion): failing target tests + graph-integrity helper for scoped-declaration nodes (U1, #1975) Adds findDanglingEdges() and pipeline-level tests asserting that Ruby namespaced class/module declarations materialize a Class/Trait node with a resolving HAS_METHOD edge. Red by design on the pre-fix base (5 failing) — the fix lands in U2 (shared core) + U3 (Ruby enablement). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): materialize graph nodes for Ruby namespaced class/module declarations (U2/U3, #1975) Widen the Ruby legacy structure query so `class Foo::Bar` / `module Baz::Qux` (name field is a scope_resolution node) match @definition.class/.module as separate top-level patterns. The node is keyed by its full scoped name, which matches the HAS_METHOD owner id that findEnclosingClassInfo derives from the same name field — so the previously-dangling ownership edges now resolve, and distinct namespaces (Foo::Bar vs Baz::Bar) stay distinct nodes (no collision). No change to findEnclosingClassInfo (zero call-resolution blast radius) and no scope-extractor/golden/bench impact — the fix is purely the legacy structure query gate. Finalizes the U1 target assertions to the qualified-name identity. Validated: 134/134 Ruby resolver tests pass on BOTH legs; tsc --noEmit clean; dangling HAS_METHOD edges on the ruby-namespaced fixture drop from 3 to 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): resolve C++ out-of-line nested definition method ownership (U4, #1975) For an out-of-line `struct Outer::Inner { ... }`, the container name is a qualified_identifier, so findEnclosingClassInfo derived the owner id from the full `Outer::Inner` text — but the type is keyed by its in-class declaration (the nested `Inner` node), leaving the method's HAS_METHOD edge dangling. Reduce a qualified_identifier container name to its tail segment for the owner id/name, matching how inline nested definitions are already keyed. Node-type scoped, so Ruby's scope_resolution names stay full (distinct-by-namespace) and no language is named in shared code. Only out-of-line-def methods (already dangling) change behavior — zero impact on bare classes or call resolution. Validated: C++ 268/268 default leg, 205+63-skip legacy leg, no regression; 2 new target tests pass both legs; Ruby namespaced tests still pass; tsc clean; scope-capture bench rebaselined (cpp +cpp-out-of-line-class fixture) — --check PASS (13 langs). Dangling HAS_METHOD on the new fixture: 1 -> 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): resolve Rust scoped impl-target method ownership (U5, #1975) `impl path::Type` and `impl Trait for path::Type` name the target with a scoped_type_identifier. Two coordinated fixes: - findEnclosingClassInfo: reduce a scoped_type_identifier impl target to its trailing type name (both the trait-impl `for` branch and the inherent branch), matching the type's own tail-keyed declaration. - tree-sitter-queries: add a @definition.impl arm for scoped inherent impls so the Impl node is materialized (keyed by the same tail) instead of missing. Together the trait-impl method owns through the real Struct node and the inherent-impl method owns through a real Impl node — no dangling edges. Rust's scoped_type_identifier has a name: field, so the tail extraction is exact. Validated: Rust 163/163 on BOTH legs, no regression; new target test passes; C++/Ruby suites unaffected; tsc clean; scope-capture bench rebaselined (rust +rust-scoped-impl fixture) — --check PASS (13 langs). Dangling 1 -> 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ingestion): cross-namespace collision test + regenerate ruby/rust captures goldens (U6, #1975) - Add ruby-tail-collision fixture + test: Foo::Bar and Baz::Bar share the tail 'Bar' but must stay two distinct Class nodes (locks the KTD-2 anti-collision guarantee from full-scoped-name keying). No dangling, no cross-wiring. - Regenerate the ruby + rust captures goldens for the fixtures added in U3-U6 (ruby-tail-collision, rust-scoped-impl). Both diffs are additive-only — a single new entry each, existing entries byte-identical (no capture-logic drift; the fixes are in the legacy structure query + findEnclosingClassInfo, not the scope-extractor). - Re-baseline the ruby scope-capture fingerprint (81->82 fixtures). N/A-language verification: C#/Java/PHP have no class-declaration scoped-name gap and show no regression (606 passed; the 2 C# worker-pool failures are the known worktree 'parse-worker.js not built' limitation, unrelated to this change). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * revert(ingestion): drop C++/Rust scoped-owner reduction; ship Ruby-only (#1975) The self-tri-review of PR #1977 (review 4411683756) found — and reproduced — that the C++/Rust tail-reduction in findEnclosingClassInfo collides same-tail types declared in the same file (struct Outer::Inner + struct Other::Inner -> one Struct:Inner node, methods silently mis-attributed; same-named members merge). Root cause is pre-existing: GitNexus keys nested-type nodes by their tail name within a file, so even plain inline same-tail nested types already merge. A correct fix needs fully-qualified nested-type node identity — a broad change deferred to #1978. This reverts the C++ (qualified_identifier) and Rust (scoped_type_identifier impl) owner reductions in ast-helpers.ts, the Rust @definition.impl scoped arm, and the cpp/rust fixtures+tests+golden+bench entries. The Ruby fix is unaffected (it keys the node by the full scoped text — no collision) and stays: namespaced class/module node materialization + the cross-namespace collision test. Validated Ruby-only: 136/136 both legs; ruby+rust captures goldens 19/19; bench --check PASS (14 langs); tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): collision-safe C++/Rust scoped-declaration node ownership (#1975) Re-introduces the C++/Rust fix the tri-review reverted, using a collision-safe approach instead of owner tail-reduction (which merged same-tail types in one file). Key the scoped DECLARATION's node by its full qualified text so it matches the owner id and stays distinct from a same-tail type elsewhere: - C++: widen the legacy structure query to materialize a node for out-of-line defs (class/struct Outer::Inner — name is qualified_identifier), keyed by the full text. No findEnclosingClassInfo change needed — BASE already derives the full-text owner, which now matches. Outer::Inner and Other::Inner stay distinct; 3-level A::B::C resolves. (A redundant forward-decl node remains.) - Rust: @definition.impl arm for scoped inherent impls (keyed full) + findEnclosingClassInfo inherent-impl branch accepts scoped_type_identifier with full text. impl a::Inner and impl b::Inner stay distinct. Collision-aware fixtures + positive owner-identity assertions (per the tri-review) replace the single-type fixtures. Deferred to #1978: Rust trait impls on a scoped struct path (impl T for a::Inner) and the pre-existing inline same-tail node collision — both need qualified struct-node identity. Validated: Ruby 136/136, C++/Rust 434/434 both legs (371+63-skip legacy); ruby+rust captures goldens 19/19 (additive); bench --check PASS (14 langs); tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(format): apply prettier to scoped-declaration changes (#1975) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
de0248c5db
|
refactor(ingestion): migrate Dart to registry-primary call resolution (#939) (#1970)
* feat(scope-resolution): migrate Dart to registry-primary call resolution (#939) Add a Dart scope-resolution module (languages/dart/) mirroring the Swift template and flip Dart to registry-primary. Resolution edges (CALLS/IMPORTS/ACCESSES/EXTENDS/IMPLEMENTS/METHOD_IMPLEMENTS) now route through the shared registry pipeline with byte-for-byte parity against the legacy DAG: test/integration/resolvers/dart.test.ts passes 53/53 under both REGISTRY_PRIMARY_DART=0 and =1 (scripts/run-parity.ts --language dart: 2/2). Dart-specific handling: - Function scopes are synthesized to span signature..body (tree-sitter function_signature/function_body are siblings, not parent/child). - extends rides @reference.inherits (EXTENDS via the generic pre-pass); implements/with are carried as __heritage__ side-effect imports and emitted as IMPLEMENTS, since Dart `implements <class>` must be IMPLEMENTS regardless of the target's symbol kind. - imports are wildcard (whole-library) with expandsWildcardTo so imported return types propagate cross-file (var u = getUser(); u.save()). - getInnerSignature now self-returns a bare signature node so top-level function params/return/name extract (legacy-safe: legacy only ever passes method_signature/declaration wrappers). Also: add Dart scope-capture bench coverage (linear ~0.99 scaling); update two tests that used Dart as a non-migrated control (Vue / forced legacy). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(scope-resolution): close Dart registry-primary parity gaps from review Adversarial review of #1970 surfaced real divergences from the legacy DAG on constructs the 10 fixtures don't exercise. All fixed; parity gate still 2/2 (now 55/55 each mode): - Implicit-constructor construction (`Foo()` with no explicit ctor): the legacy DAG emits `caller -> Foo` (Class) but registry emitted nothing (callee tagged @reference.call.free never reaches constructorCallTargetsClass). Re-tag UpperCamelCase free-callees to @reference.call.constructor (Dart types are UpperCamelCase) so they link to the Class. Locked in with a regression fixture + test that passes in BOTH modes. - Cascade calls (`list..add(1)..sort()`) were dropped — cascade_section has no `selector` wrapper, so the reference walk never saw them while legacy emitted them as free calls. Add a cascade_section handler. - BUILT_INS (setState/then/push/pop/listen/...) were not suppressed on the registry path, so a user symbol shadowing one produced a spurious CALLS edge the legacy DAG suppresses. Skip built-in-named call refs at capture time (extract the set to a leaf module shared with the provider). - Enhanced-enum methods mis-parented to Module (no enum scope). Add `(enum_declaration) @scope.class` so enum members are owned by the enum. Re-baseline the Dart scope-capture fingerprint (linear ~0.95). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(scope-resolution): apply issue #1926 F24/F25 findings to the Dart scope path Issue #1926 catalogs Dart parsing-layer coverage gaps. Apply the two that the registry-primary scope-resolution path owns (call edges + call attribution), registered as legacy-expected-failures since they are scope-resolver-only wins. - F24: the scope path's unified tree-walk already captures member calls (obj.method()) in return / list-literal / named-argument / arrow-body contexts — the legacy DAG only captures them under expression_statement / initialized_variable_definition. Lock it with the dart-member-call-contexts fixture + tests. - F25 (constructor portion): a constructor's body is a sibling of the WRAPPING method_signature (class_body > method_signature > constructor_signature, then function_body), so findFunctionBody now walks up to the method_signature wrapper. Constructor bodies get a Function scope and their body-calls attribute to the Constructor (a valid caller anchor) instead of the class. Add the dart-constructor-body fixture + test. Switch dart.test.ts to createResolverParityIt('dart') and add the dart entry to LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES (5 wins). Both modes pass: run-parity --language dart → 2/2 (registry 60/60; legacy 55 pass + 5 skipped). Not applicable to the scope path (structure-phase / shared-pipeline, tracked by #1926's legacy fix): F25 getter/setter (Property is not a caller anchor) and operator (no Method node emitted by the structure phase) bodies; F26 (static field Property nodes); F27 (no generic_type reference in the scope module); F28/F29 (typedef/variable node extraction). Re-baseline the Dart scope-capture fingerprint (linear ~1.0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(scope-resolution): fix Dart named-constructor file-drop + container-name mis-binding (tri-review) Multi-engine tri-review (GitNexus + CE personas + Codex gpt-5.5) of #1970 found a P0 the parity gate missed plus a P2 wrong-edge: - P0 (file drop): a named constructor with a body (`class A { A.named() {…} }`, idiomatic Dart) parses as ONE constructor_signature carrying multiple `name:` fields, so the scope query matched it more than once and synthesized two identical-range @scope.function captures → ScopeTreeInvariantError(duplicate- scope-id) → extractParsedFile swallowed it → the WHOLE file was dropped from registry-primary resolution (CALLS=0 vs legacy CALLS=2). Introduced by the #1926 F25 findFunctionBody change that started giving constructors body scopes. Fix: dedup function-like declarations by their statement node so each is emitted once. Add dart-named-constructor-body fixture + a parity guard test (both modes) that fails if the file is dropped, plus the named-ctor F25 attribution win (registry-only). - P2 (wrong edge): normalizeDartType's Future<X>/List<X> unwrap is unreachable (generic args are stripped upstream to a bare `Future`/`List`), so a return/ field type binding to the bare container name let a same-named user class (`class Stream {…}`) capture the receiver — a wrong CALLS edge legacy didn't emit. Suppress type bindings that normalize to a bare container name (leaving the call unresolved, matching legacy) instead of binding to the container. Both modes still pass: run-parity --language dart → 2/2 (registry 62/62; legacy 56 + 6 skipped). Re-baseline the Dart scope-capture fingerprint. Also: refresh the captures.ts module doc (constructors get scopes; cascade calls). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(scope-resolution): address Dart tri-review follow-ups (heritage collision + polish) - P2 heritage cross-file name collision: emitDartHeritageEdges resolved both child and base by a global last-write-wins simple-name map, so two files each declaring `class Logger` (one `implements Logger`) produced a wrong-file IMPLEMENTS edge. Resolve with same-file affinity (prefer a same-file class, then a workspace-unique match, else refuse to guess) — the #1951 file-affinity pattern. Add dart-heritage-name-collision fixture + a parity test (both modes resolve same-file). Also reason-qualify the dedup key so `implements X` + `with X` keep distinct edges. - Polish: buildDartMro uses Sets instead of Array.includes-in-loop; merge-bindings uses named tier constants matching swift; drop the dead no-op stripQuotes in import-target (targetRaw already arrives quote-stripped). Both modes pass: run-parity --language dart → 2/2 (registry 63/63; legacy 57 + 6 skipped). Re-baseline the Dart scope-capture fingerprint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6643afbcda
|
fix(ruby): scope-resolution namespaced class/module definitions — F62 (#1933) (#1972)
* fix(ruby): namespaced class/module definition captures — F62 (#1933) * chore(bench): regenerate Ruby golden captures after F62 scope_resolution patterns * fix(ruby): namespaced class/module definition captures — F62 (#1933) * chore: remove unused imports from ruby-namespaced test * chore: add comment about capture-only scope in ruby-namespaced test --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
052319324d
|
feat(go): infer structural interface implementations (#1966)
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Devcontainer Smoke / Config-transform unit tests (push) Has been cancelled
Devcontainer Smoke / Build devcontainer image (push) Has been cancelled
|
||
|
|
fcddbb0818
|
fix(python): scope-resolution coverage gaps — F57, F58, F61 (#1932) (#1964)
* fix(python): scope-resolution coverage gaps — F57, F58, F61 (#1932) F57: heritage patterns for qualified/subscripted bases F58: decorator patterns for nested-attribute decorators F61: lambda captured as @scope.function F59 already closed by #1920, F60 legacy-only * chore(bench): update Python scope-capture baseline after F57/F58/F61 * chore: lower coverage thresholds after F57/F58/F61 query additions * P0-P6 review fixes: F58 decorator wiring, deduplication, e2e test, golden regeneration, thresholds reverted, baseline update * chore: remove unused imports from python-parsing-coverage test --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
4f7697c43b
|
fix: COBOL parsing-layer coverage gaps — F17-F23 (#1925) (#1959) | ||
|
|
0fc0211d26
|
fix(ingestion): migrate all languages' inheritance to scope-resolution on the worker path (#1951) (#1956) | ||
|
|
070c3d5154
|
fix(ingestion): skip File->Member DEFINES edges for class members (#1949)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(ingestion): skip File->Member DEFINES edges for class members Previously every symbol — including Methods and Properties that belong to a class — received a direct File->Symbol DEFINES edge. This caused the radial-layout view to show File linking directly to all Properties and Methods, bypassing their enclosing class node and flattening the OOP hierarchy. Fix: gate the DEFINES emission on the symbol having no owner. Class members are already reachable through the File->Class DEFINES edge and the Class->Member HAS_METHOD / HAS_PROPERTY edges, so the redundant direct edge is unnecessary and misleading in the graph. The same guard is applied in all four emission sites: - parsing-processor.ts (sequential fallback path) - parse-worker.ts (main symbol loop + routed-property branch) - call-processor.ts (routed-property branch) Pipeline-graph golden updated: DEFINES 21→20, relationships 74→73. Closes #1944 Co-authored-by: Claude <noreply@anthropic.com> AI-model: claude-sonnet-4-6 * fix(wiki): extend getFilesWithExports to include exported class members The PR that removed File→Member DEFINES edges left getFilesWithExports() under-reporting: its one-hop MATCH only reaches top-level symbols. Add a UNION leg that follows File→DEFINES→Class→HAS_METHOD/HAS_PROPERTY→Member so exported class methods and properties appear in wiki/cluster export summaries again. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
7d40156003
|
feat(swift): migrate Swift to scope-based registry resolution (#937) (#1948)
* feat(swift): migrate Swift to scope-based registry resolution (#937) Ring 3 of RFC #909 — Swift is the final language migrated to the scope-based registry resolution pipeline. Flips Swift into MIGRATED_LANGUAGES so registry-primary call resolution is the production default, with dual-mode parity proven: the resolver suite passes 77/77 under both the legacy DAG (REGISTRY_PRIMARY_SWIFT=0) and the registry-primary path (REGISTRY_PRIMARY_SWIFT=1). New language module src/core/ingestion/languages/swift/ (mirrors csharp/): query, captures, interpret, import-decomposer, receiver-binding, signature-bindings, arity (+metadata), merge-bindings, simple-hooks, import-target, target-siblings, implicit-imports, sibling-type-bindings, scope-resolver, cache-stats, index. Parse-time hooks wired into the existing flat languages/swift.ts (coexists with the swift/ dir, like kotlin) and the resolver registered in the scope-resolution registry. tree-sitter-swift 0.7.1 specifics handled in the Swift module (not in shared code): - class / struct / extension all parse to class_declaration; extensions are re-keyed onto the extended type so members hoist (like C# partial). - if-let / guard-let have no if_let_binding node — the optional binding is synthesized from if_statement / guard_statement. - the name: field is reused for func name, param labels, param types and return type, so param/return type-bindings are synthesized in code (signature-bindings.ts) rather than via a multi-name query. - no `new` keyword: Type(...) and Type.init(...) are synthesized into constructor type-bindings. Shared-pipeline additions are language-agnostic (AGENTS.md: no language names in shared ingestion code): - constructorCallTargetsClass on the ScopeResolver contract + free-call-fallback option + run.ts wiring: when true, Type(...) links to the Class def rather than its explicit init Constructor. - pickUniqueGlobalClass: constructor-branch global fallback for cross-file types absent from the call site's lexical bindings, deduped by qualifiedName so extension/partial fragments aren't seen as ambiguous. - emitImplicitImportEdges: same-module File->File IMPORTS edges (Swift whole-module visibility has no syntactic import to drive the generic ImportEdge pipeline). Import resolution: rewrote the O(n^2) module scan in import-resolvers/configs/swift.ts with a WeakMap-memoized index. Benchmarks & guards: - Swift added to bench/scope-capture/measure.mjs + baselines.json (fingerprint + 1.5x scaling budget); `--check` passes for all 7 languages, Swift scaling 0.98 (linear). - golden capture-parity test (test/unit/scope-resolution/swift/swift-captures-golden.test.ts + fixtures/swift-captures-golden/) mirrors the csharp golden. - O(n) scope-capture tripwire (test/integration/swift-scope-capture-tripwire.test.ts). Full Swift test glob: 3 files / 88 tests pass; tsc --noEmit clean; no cross-language resolver regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(swift): green CI — Dart canary, cascade-safe availability test, prettier, comment/order nits (U1) * test(swift): wire createResolverParityIt('swift') + empty legacy skip-set (U2) * fix(swift): group same-module files by SPM target subtree in registry-primary hooks (U3) * fix(swift): correct member-write, class-func self, multi-clause if-let, nested-extension (U4) * perf(scope-resolution): build global class index once for pickUniqueGlobalClass (U5) * fix(swift): re-baseline scope-capture fingerprint after member-write capture change (U4) * style(swift): prettier-format pick-unique-global-class test (U5 follow-up) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
bb7a02d8b7
|
fix(typescript): fix HOC pattern false positives and add export default HOC support (#1943)
* fix(typescript): fix HOC pattern false positives and add export default HOC support Fixes two related issues from #1876: 1. False positives: `const x = arr.map(a => ...)` was incorrectly classified as a Function node. Split HOC patterns into identifier vs member_expression variants and apply a #not-any-of? blocklist for 36 array methods (map, filter, reduce, forEach, etc.) across all four query files. Runtime safety-net in tsExtractFunctionName uses a module-level ARRAY_METHODS constant (avoids per-call Set re-allocation). 2. Missing support: `export default defineEventHandler(async (e) => { ... })` and similar HOC-wrapped default exports were invisible. Added 4 export_statement patterns (TS + JS, legacy + registry-primary) and extended tsExtractFunctionName to derive the function name from the callee identifier. Now correctly distinguishes: - `const data = arr.map(account => ({...}))` → Const only (was Function+Const) - `const Button = forwardRef(...)` → Function:Button (unchanged) - `const Card = React.memo(...)` → Function:memo (unchanged) - `export default defineEventHandler(...)` → Function:defineEventHandler (new) Tests: add 2 fixture files and 4 test cases to typescript-hoc-wrapped suite covering the export default HOC positive case and array method exclusion negative case. Closes #1876 Co-authored-by: Claude <noreply@anthropic.com> AI-model: claude-sonnet-4-6 * fix(ingestion): tighten HOC callback attribution Share the TypeScript and JavaScript HOC blocklists across query and runtime paths, suppress stale array-method and built-in export-default wrappers, and derive export-default HOC names from the file instead of the wrapper helper. Also update the pinned unit and integration tests so CI reflects the new callback suppression contract. Co-authored-by: Claude <noreply@anthropic.com> AI-model: claude-sonnet-4-6 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
d1d2a64d0f
|
perf(ingestion): linearize scope-capture across all languages + Python import resolution (O(n²)→O(n)) (#1918)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* bench(python-scope): build-free measure harness + baseline fingerprint for emitPythonScopeCaptures
ce-optimize scaffolding for the python-scope-capture run. Mirrors the Go
scope-capture harness (#1848): imports the .ts hotpath via tsx, times
emitPythonScopeCaptures on a synthetic DAO source at 250/800 entities, and
pins an order-independent sha256 capture fingerprint over the whole
lang-resolution/python-* corpus + a fixed 20-entity DAO as the correctness gate.
Baseline (current code) is O(n^2): 250->800 entities (3.2x) -> 10.7x time
(1062->11343ms), scaling_ratio 3.34.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* optimize(python-scope-capture): thread captured nodes to kill O(n^2) findNodeAtRange re-walks
emitPythonScopeCaptures re-derived each tree-sitter match's AST node via
findNodeAtRange(tree.rootNode, ...) on every match, scanning all of root's named
children per call -> O(matches x rootChildren) ~ O(n^2). The same #1848 bug Go
had (fixed in
|
||
|
|
d5514f5cb3
|
fix(cpp): handle variadic pack dependent lookup (#1909)
* fix(cpp): handle variadic pack dependent lookup * fix(cpp): preserve helper calls in pack mixins --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
f5915ca9ab
|
perf(go): kill O(n²) scope-capture re-walks (resolves #1848 quarantine) (#1915)
* test(go): add #1848 Go pipeline + worker-pool benchmark Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * optimize(go-scope-capture): thread captured nodes to kill O(n^2) findNodeAtRange re-walks emitGoScopeCaptures re-derived each match's AST node via findNodeAtRange from the tree root on every query match, giving O(matches x rootChildren) ~ O(n^2) behaviour (the #1848 root cause: a 250-struct generated DAO took ~10.8s, 800 structs ~100s+ — long enough to trip the worker sub-batch idle timeout and get quarantined). Thread the query-captured SyntaxNode (c.node) through a parallel tag->node map and use it directly (or via a bounded local parent walk for the import_declaration ancestor case) instead of re-walking from root. Output is byte-identical (capture fingerprint over the DAO file + all 89 go-* fixtures unchanged; capture_groups=13501). 250 entities: 10835ms -> 114ms (95x). 800 entities: ~100s -> 384ms. Go resolver + scope-resolution suites: 165/165 pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(go-scope-capture): address code-review findings Self-review (ce-code-review) polish on the #1848 fix + benchmark: - benchmark: tighten the scaling guard from timeRatio/fileRatio < 3 to < 1.5. At the 2.5x/2x scale steps, a quadratic regression yields ratio == fileRatio (2.5, 2.0), which < 3 waved through — the guard could not detect the O(n^2) it exists for. Measured O(n) ratios are 0.45/0.59, so < 1.5 has headroom. - benchmark: add a non-gated O(n^2) regression tripwire that calls emitGoScopeCaptures on a 400-struct source directly (no worker, no GITNEXUS_BENCH gate) so the regression is actually guarded in CI. - benchmark: clearTimeout the Promise.race timer in finally (no lingering rejection); set the worker-suite env vars inside the try so finally always restores them. - captures.ts: clarify the isRawMultiAssignTypeBinding comment to name both var-form cases (assertion + call-return). Comment-only. Left as-is: resolveImportNode's defensive range-equality branch — deleting it as dead code would remove the self-documentation of the grammar invariant the threaded-node logic depends on (reviewer tension; a wash). Verified: tsc clean; 165/165 Go resolver + scope tests; new tripwire passes (237ms); scaling suite passes at <1.5; #1848 worker suite still green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(go): golden capture-parity guard for emitGoScopeCaptures (#1848 U1) Pins emitGoScopeCaptures output across all 89 go-* fixtures + a synthetic DAO shape as a committed golden (test/fixtures/go-captures-golden/expected-captures.json), so future drift in the Go scope-capture path fails CI instead of only the coarse perf tripwire. Match-grouped, order-independent sha256 canonicalization; regenerate intentionally with UPDATE_GOLDEN=1. Mirrors test/integration/pipeline-graph-golden.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(go): cover func_literal, var-form bindings, single import, generics (#1848 U2) Adds smoke cases for the Go shapes the #1915 captured-node refactor reasons about but no lang-resolution fixture exercised: func_literal under @scope.function (no receiver synthesized), var-form @type-binding.assertion and .call-return (not dropped by isRawMultiAssignTypeBinding), a single unparenthesized import through resolveImportNode, and a generic function declaration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(go): tighten O(n^2) tripwire budget 10s -> 5s (#1848 U3) The fixed path is ~250ms; a quadratic regression at 400 structs is ~25s. 5s keeps ~20x headroom over the fixed path while tripping a ~20x regression (vs the prior ~40x). Correctness is guarded separately by the U1 golden test, so this stays a pure perf tripwire. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * test(go): fail on a missing golden in CI via a pure resolveGoldenAction helper (#1848 U1) Extracts the golden test's missing-file gate into a pure resolveGoldenAction({update,exists,isCI}) -> regenerate|compare|fail helper, so a missing golden no longer self-heals + passes in CI (Codex F2). The rule is unit-tested directly across all combos with no filesystem mutation (can't corrupt the committed golden). CI detection uses a truthy check (!!process.env.CI) so it fires on any runner. Locally a missing golden still regenerates as first-run convenience. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(go): make the golden digest order-sensitive (#1848 U2) Drops the cross-match .sort() in digestCaptures so the digest reflects emission order — a true byte-identical guard that catches a reordering refactor (Codex F1), not just a set-equality check. Safe because emitGoScopeCaptures output is deterministic. Within-match key order stays normalized (a CaptureMatch is a Record). Replaces the order-independence test with an order-sensitivity assertion and regenerates expected-captures.json under the new scheme (all 90 digests). Trade-off: a tree-sitter-go grammar bump that reorders matches now requires a deliberate UPDATE_GOLDEN=1 regen — intentional (a tree-shape change deserves a look). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(go): strengthen func_literal smoke case to a positive receiver assertion (#1848 U3) The old case used a closure-only source and only asserted ABSENCE of @type-binding.self, so it would pass even if the method_declaration receiver branch regressed (Codex F3). The fixture now has both a method and a closure, and positively asserts exactly one @type-binding.self from the method (name=u, type=User — the type also confirms *User pointer-stripping) and none from the closure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(test): remove TOCTOU file-system race in golden test + format CodeQL flagged a high-severity 'potential file system race condition': the golden test did fs.existsSync(GOLDEN_FILE) then later writeFileSync/readFileSync on it. Replace the existsSync-then-use with a single race-free read (ENOENT => missing), reusing the read content for the compare path. Behaviour is unchanged (the pure resolveGoldenAction helper still decides regenerate/compare/fail). Also applies prettier formatting to the file (fixes the quality/format check). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <abhigyan1.patwari@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
4b787be835
|
fix(csharp): stop spurious IMPORTS edges from ungated using-resolution (#1881) (#1908)
* fix(csharp): eliminate O(S·D) BindingRef OOM in namespace siblings Types declared in the C# global (default) namespace are visible from every file, so the previous per-scope augmentation materialized O(scopes × defs) BindingRefs — on large Unity solutions (tens of thousands of global types) this caused severe slowness and OOM. Route global-namespace types through a single workspace-level binding channel (workspaceFqnBindings, consulted by lookupBindingsAt) for O(D) memory. Also fix quadratic costs in the non-global path: append defs in place instead of copying (was O(D²) per bucket), pre-index the first scope per file (was O(S²·D)), and seed de-dup sets instead of repeated .some scans. Add csharp-pipeline-benchmark.test.ts (mirrors the PHP benchmark) with spread and concentrated-global-namespace scenarios to track elapsedMs, peakHeapMB, nodeCount, and edgeCount. Post-fix runs show linear scaling and stable heap. Co-authored-by: Cursor <cursoragent@cursor.com> * perf(csharp): scanner fallback for namespace siblings on the worker path Worker threads can't return tree-sitter Trees across MessageChannels, so the cross-phase tree cache is empty for worker-parsed files. The C# same-namespace pass (populateCsharpNamespaceSiblings -> extractFileStructure) then re-parsed every file with tree-sitter to find namespace / using-static nodes — effectively parsing a large solution a second time during scope resolution. Add a line-scanner fallback (extractCsharpStructureViaScanner) used only when no cached Tree is available, mirroring PHP's fix for issue #1741. It extracts the same namespaces / usingStaticPaths the AST walk produces for the common line-anchored forms (file-scoped + block namespaces, plain / global / aliased `using static`). The AST walk stays authoritative on the sequential / warm-cache path. Micro-benchmark over 3000 synthetic files: scanner is ~188x faster than parse+walk (0.001 vs 0.251 ms/file) with identical output on the parity spot-check; real-world files are larger, so the worker-path saving is bigger. Adds csharp-namespace-extraction.test.ts (12 cases) covering all declaration forms plus negative cases (using var, plain using, comments). Co-authored-by: Cursor <cursoragent@cursor.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(csharp): cover global-namespace workspaceFqnBindings path + doc + using-static perf Addresses the production-readiness review of the namespace-siblings OOM fix. - Add a unit test proving global-(default-)namespace C# types route to indexes.workspaceFqnBindings (one entry per simple name) with ZERO bindingAugmentations — pinning the O(D) invariant behind the #1871 Unity-scale OOM fix and guarding against a revert to per-scope O(scopes x defs) augmentation. (The csharp-hooks mock now supplies workspaceFqnBindings, which the global fast path reads directly.) - Correct the workspaceFqnBindings doc comment: it is shared by PHP (backslash-FQN keys) and C# (global-namespace simple-name keys); the two key formats are disjoint. - Pre-index parsedFiles by path before the `using static` member-injection loop, replacing an O(files) find-per-import with an O(1) Map lookup. Verified: tsc --noEmit clean; csharp-hooks + csharp-namespace-extraction suites pass (38 tests); prettier clean; eslint 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(csharp): apply PR-review polish to namespace-siblings (tests, types, docs) Addresses the multi-agent code review of this PR — the concrete, defensible findings. Two items intentionally deferred (below). - namespace-siblings.ts: couple the augmentation bucket + its de-dup set into one nullable lifecycle, removing the seen!/bucketArr! non-null assertions (identical runtime, still lazy). - validate-bindings-immutability.ts: extend the dev-mode immutability validator to the third channel (workspaceFqnBindings) + a test; complete the validator test mock with workspaceFqnBindings. - walkers.ts: document that namesAtScope deliberately excludes the scope-independent workspaceFqnBindings channel (enumerating workspace names at every scope would flood per-scope callers; lookupBindingsAt still consults it when resolving a specific name). - scope-resolution-indexes.ts: reframe the workspaceFqnBindings doc to describe the key-format contract language-neutrally (examples, not language branching). - csharp-hooks.test.ts: assert workspace entries carry origin:'namespace'; add a partial-class test (same simple name, distinct nodeIds across global files → both kept); rename the stale "parses" cache-miss test to "scans". - csharp-pipeline-benchmark.test.ts: clearTimeout the Promise.race budget timer (dangling handle when the pipeline won the race). - csharp.test.ts: correct the #1066 comment — extractFileStructure no longer re-parses on cache miss (line scanner); only emitCsharpScopeCaptures re-parses. Deferred (surfaced, not applied): (1) worker-path scanner mis-reads namespace/using-static inside block comments and verbatim/raw strings — an explicitly documented trade-off mirroring the PHP scanner; hardening it to track comment/string state is a separate decision. (2) workspaceFqnBindings is read via an `as Map` cast; a type-safe mutable handle from finalize-orchestrator is a cross-module contract change. Verified: tsc --noEmit clean; 49 unit tests pass (incl. 3 new); prettier clean; eslint 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(csharp): harden worker-path scanner + localize workspace-map cast Addresses the two deferred PR-review findings plus the remaining test gap. #1 — Worker-path scanner false positives: the line scanner now tracks block- comment and string state across lines (advanceCsScanState), so a `namespace` / `using static` keyword at the start of a line inside a block comment, verbatim string (@"..."), or raw string literal ("""...""") is no longer mistaken for a declaration on the worker cache-miss path. It matches only at code-state line starts. 5 new scanner tests cover the block-comment / raw / verbatim cases. #4 — workspaceFqnBindings type safety: the ReadonlyMap->Map cast is localized to one documented line, and global-namespace writes go through a new getWorkspaceBucket helper (mirroring getAugmentationBucket) rather than an inline `.set()` at the mutation site. #2 — lookupBindingsAt workspace-channel coverage: walkers-augmentations.test.ts now exercises the third (workspace) channel: workspace-only, append-after- finalized/augmented, and dedup-loses-to-finalized/augmented precedence. #5 — OOM CI guard: the deterministic O(D) invariant (zero per-scope augmentation for global types) is already asserted by the always-on csharp-hooks unit tests added earlier; the scale/time benchmark stays appropriately opt-in (skipIf). Verified: tsc --noEmit clean; 69 unit tests (4 suites) + 210 C# integration resolver tests pass; prettier clean; eslint 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(csharp): replace remaining O(A) .some dedup scans with seeded Sets The using-static member-injection loop and the cross-namespace import loop both de-duped via `bucketArr.some((b) => b.def.nodeId === ...)` — O(A) per item. Both now use a per-file `Map<simpleName, Set<nodeId>>`, seeded lazily from the augmentation bucket (capturing entries from earlier passes), matching the global and named-namespace paths. Same dedup semantics, O(1) amortized. Verified: tsc --noEmit clean; csharp-hooks unit (27) + C# integration resolver (210) tests pass; prettier + eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(csharp): gate suffix-fallback import resolution to declared namespaces (#1881) C# `using` directives were resolving via an ungated suffix match, so a BCL using like `System.Threading.Tasks` matched a coincidental local `Tasks.cs` and emitted spurious IMPORTS edges. Add a declared-namespace gate that only permits suffix-fallback when the import plausibly refers to an in-repo namespace (exact, immediate-parent-declared, or ancestor-of a declared namespace anchored at an in-repo root). Both resolution legs — the legacy DAG and the registry-primary scope resolver — thread the same evidence to the gate, including the no-csproj path. Declared namespaces are collected with #1905's comment/string-aware scanner (extractCsharpStructureViaScanner, lazily imported) instead of a regex, so `namespace` tokens in comments/strings can't seed phantom namespaces. Scan truncation or unreadable subtrees fail OPEN (gate disabled) and are logged. Stacked on #1905 (fix/csharp-namespace-scope-oom). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(csharp): cap per-file size in namespace scan; fail open on skip (#1881) scanCSharpProject read every .cs/.csproj in full with no size guard and issued per-directory reads with no concurrency bound, an OOM/FD-exhaustion vector on large or generated repos. Add an fs.stat size guard before each read, reusing getMaxFileSizeBytes() (the same 512KB cap the Phase-1 walker uses). An oversized or unreadable .cs now signals truncation so the #1881 suffix-fallback gate fails OPEN rather than wrongly suppressing an import whose declaring namespace lived in the skipped file (previously a silent return left the scan looking complete). Adds a size-cap scan test. * fix(csharp): bound per-directory read concurrency in namespace scan (#1881) The scan issued every .cs/.csproj read in a directory at once via Promise.all, so in-flight file descriptors scaled with the largest directory's file count. Issue reads in bounded windows (32, mirroring the Phase-1 filesystem-walker) via Promise.allSettled; an unexpected read/scan rejection now trips truncation (fail open) instead of rejecting the whole scan. Behavior-preserving for namespace collection (C# scope-resolution parity passes on both legs). * style(csharp): apply prettier to #1881 files to clear quality/format gate (#1908) Reflow hand-wrapped lines in scope-resolver.ts and the csharp integration test that prettier collapses under printWidth 100. Formatting only, no behavioral change; clears the failing quality/format CI gate. * fix(csharp): stream namespace scan so large generated files don't disable the #1881 gate (#1908) Code-review follow-up. The scan read each .cs fully into a string behind a 512KB size cap (the tree-sitter parse budget); a single larger generated file (*.g.cs, EF/gRPC output) tripped `truncated`, making the #1881 suffix-fallback gate fail open repo-wide and silently undoing the fix on real repos. Stream each .cs line-by-line via createReadStream + readline into a new incremental scanner (createCsharpStructureScanner) instead of buffering the whole file. Memory is now constant regardless of file size, so the per-file size cap is dropped for the namespace line-scan and large generated files are fully collected. extractCsharpStructureViaScanner is reimplemented on the same incremental scanner (byte-identical; C# parity 2/2). collectDeclaredNamespaces returns 'ok' | 'truncated' (truncation now only from an unreadable file) and the truncation warn lists its real causes. csproj reads keep their size guard. Prior art: ripgrep/ctags/Node readline stream rather than cap for line scans; GitHub (384KB) and Sourcegraph (1MB) cap only their full-content indexes. * fix(csharp): cap .csproj read via stream, not stat-then-read, to clear CodeQL TOCTOU (#1908) CodeQL js/file-system-race flagged the fs.stat + fs.readFile size guard in readCsprojConfig as a check-then-use filesystem race. Replace it with a length-capped createReadStream (readFileTextCapped) — same memory bound on untrusted input, no stat-then-read race, and consistent with the streamed .cs scan. Behavior is unchanged for real .csproj files (parity 2/2). * fix(csharp): keep BCL/external roots gated through scan truncation (#1908, Codex F1) A single scan truncation (unreadable dir/file, depth/dir cap) set one repo-wide `truncated` flag that made csharpSuffixFallbackAllowed fail open for EVERY import, silently re-enabling the #1881 BCL->local suffix matches. Add a CSHARP_EXTERNAL_ROOTS denylist (System/Microsoft/...): an external-rooted using that does not align with an in-repo declared namespace stays BLOCKED even under truncation, while genuinely local-looking usings still fail open. A repo that declares the root is allowed via the alignment escape hatch. Shared predicate, so both legs inherit it. * fix(csharp): gate the registry no-csproj direct-match path (#1908, Codex F2) In the no-csproj branch of resolveCsharpImportTarget, resolveDirectMatch ran BEFORE the gate, so a path-aligned Legacy/System/Threading/Tasks.cs satisfied 'using System.Threading.Tasks;' even though System.* is not a declared in-repo namespace — while the legacy leg (gate-first) blocked it, so the legs were not equivalent. Run csharpSuffixFallbackAllowed first (return null on fail), then direct-match, then progressive stripping — mirroring the legacy ordering. Adds a no-csproj fixture with a deep path-aligned Tasks.cs and dual-leg integration describes (registry + forced-legacy), plus a path-aligned unit case. Parity 2/2. * fix(csharp): flag scanner-uncaptured namespaces incomplete; Unicode/@ matchers (#1908, Codex F3) The line scanner treated its output as complete even when it missed valid C# namespace forms, so the gate failed CLOSED and over-blocked legit imports. Make CS_NAMESPACE_RE/CS_USING_STATIC_RE Unicode-aware (\p{L}\p{N} + u flag) and strip leading/segment @ so verbatim/Unicode identifiers are captured to match the AST. For forms the regex still can't capture (split across lines, not at line start, attributed), set a per-file 'incomplete' flag; collectDeclaredNamespaces returns 'truncated' for such files so the #1881 gate fails OPEN instead of dropping the namespace. High-precision detectors + guard tests keep ordinary forms (incl. // namespace comments) from tripping incomplete. * fix(csharp): stream the .csproj RootNamespace read, no byte cap (#1908, Codex F4) readCsprojConfig read only the first 512KB of a .csproj and, on a match-miss, couldn't tell 'no RootNamespace' from 'RootNamespace past the cap' — both synthesized a filename root. A wrong authoritative root makes imports under the real root resolve to nothing AND suppresses the fallback. Replace the capped read with a streamed early-stop search (findCsprojRootNamespace) that reads until the tag or EOF: filename fallback ONLY on genuine read-to-EOF absence; on a soft-budget cap-hit or unreadable file, OMIT the config so the no-csproj fallback stays reachable. Removes the now-unused readFileTextCapped + getMaxFileSizeBytes cap from the scan. Parity 2/2. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e234dac849
|
feat(cpp): add template partial ordering (#1885)
* feat(cpp): add template partial ordering * fix(cpp): harden template partial ordering --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
252fbabd51
|
fix(ingestion): stop emitting phantom Function defs for array-method callbacks (#1906)
* fix(ingestion): stop emitting phantom Function defs for array-method callbacks The HOC-wrapped-arrow scope-query pattern (`const X = HOC(args => ...)`), added for React idioms such as forwardRef/memo/useCallback, also matched array higher-order-method callbacks like `const x = arr.map(a => ...)`. Those produced a spurious `@declaration.function` named after the binding, on top of its value def, so calls inside the callback attributed to a phantom `Function:x` instead of the enclosing scope. - Add a shared `isArrayMethodCallbackArrow` detector (`ARRAY_CALLBACK_METHODS` blocklist) and suppress the `@declaration.function` emit-side in both the JS and TS scope-captures emitters, leaving the value binding as the sole def. - Add `selectNodeBearingDef` in scope-extractor: the tested collapse-rule contract (function-like > value > first) the deferred node-creation migration will consume to keep one graph node per binding. This corrects the registry-primary scope model and CALLS-edge attribution (calls inside array-method callbacks now source from the enclosing File scope). The duplicate graph *node* itself is still created by the legacy parse-worker path and is removed by the follow-up node-creation migration. Refs #1876 Co-authored-by: Cursor <cursoragent@cursor.com> * test(ingestion): strengthen array-callback coverage; document receiver-blind suppression Follow-ups from the production-readiness review of PR #1906: - array-callback.ts: document that isArrayMethodCallbackArrow is receiver-blind — an in-set method name on a NON-array receiver (Map/Set.forEach, RxJS observable.map, query-builder .sort, lodash chain .filter) is also suppressed. Accepted limitation, not a bug: the binding holds the call's result value, not a callable. - captures unit tests (JS + TS): add a non-array-receiver characterization case, and extend the it.each lists to cover findLast, findLastIndex, reduceRight — the full 13-entry ARRAY_CALLBACK_METHODS set is now exercised in both languages. - js-array-method-callback-attribution integration test: tighten the File-sourced CALLS assertions from toBeGreaterThan(0) to toHaveLength(1) (now also catches over-attribution). - scope-extractor.ts: note that the dead selectNodeBearingDef export is intentional and tracked by #1876 (deferred node-creation migration). Comment-and-test only; no production behavior change. Verified locally: tsc clean, prettier/eslint clean, captures unit 106 passed, scope-extractor 31 passed, integration 3 passed. Refs #1876 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b565c7c990
|
feat(ingestion): resolve FastAPI include_router(prefix=...) cross-file routes (#1877)
* feat(ingestion): resolve FastAPI include_router(prefix=...) cross-file routes
FastAPI sub-route files declare paths via @router.<verb> while the entry
file mounts the router with app.include_router(<router>, prefix='/x').
Previously both the ingestion-layer Route graph nodes and the group-layer
ExtractedContract URLs lost the cross-file prefix, breaking provider <->
consumer matching.
Ingestion layer:
- parse-worker emits routerIncludes / routerImports + decoratorReceiver
- parsing-processor / parse-impl thread the new fields and aggregate
prefixesByModule across chunks; decorator routes whose receiver is
'router' are duplicated once per matching prefix
- routes.ts joins prefix via normalizeExtractedRoutePath
Group layer:
- HttpLanguagePlugin gains an optional prepareRepo() pre-pass and a
repoContext arg to scan(); python.ts builds prefixesByModule and
falls back to the bare path when no entry matches
- http-route-extractor caches one repoContext per plugin
Tests:
- 3 new http-route-extractor cases (attr / named-import / no-prefix)
- ParseWorkerResult literals in 3 test files updated to the new shape
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ingestion,group): address PR #1877 review — relative imports, cross-package collisions, host names, ingestion tests
Follow-ups to the FastAPI `include_router(prefix=...)` cross-file fix
based on PR #1877's automated production-readiness review. Three
correctness gaps and one test coverage gap addressed:
1. Relative-import support in the worker regex (FINDING 2)
`FROM_IMPORT_ROUTER_RE` now accepts module paths starting with a
`.` (e.g. `from .calls import router as calls_router`). The
previous `[A-Za-z_][\w.]*` rejected leading dots and silently
dropped every relative-import Shape-B include — a real pattern
from the PR description's own motivating example. The matching
helpers now strip leading dots before keying so absolute and
relative imports collapse to the same module key.
2. Cross-package same-name module collisions (FINDING 3)
Two-tier module keying replaces the previous basename-only key:
• short key — `users` (file basename without `.py`)
• long key — `api/users` (parent dir + stem)
`prefixesByLongKey` is consulted first and only falls back to
`prefixesByShortKey` when no long-key match is available. Both
the ingestion pipeline (parse-impl.ts) and the group extractor
(http-patterns/python.ts) carry the same scheme so the graph
nodes and HTTP contracts agree on which prefix applies.
New protocol field `ExtractedRouterModuleAlias` (parse-worker →
parsing-processor → parse-impl) lets Shape-A
`<host>.include_router(<mod>.router, prefix='/x')` calls promote
to a long key when the same file imports `<mod>` via
`from <pkg> import <mod>`. Without this, `api/users.py` and
`admin/users.py` collided on the basename `users` and the admin
file's routes inherited the `/users` prefix that was only meant
for `api/users.py`.
3. Non-`app` host variable names (FINDING 4)
The group-layer `INCLUDE_ROUTER_*_PATTERNS` queries pinned the
host identifier to the literal `"app"` and dropped every
`application = FastAPI()` / `api = FastAPI()` pattern — the
constraint was redundant given that the call shape
(`include_router` invoked with a router argument and a
`prefix=` keyword) is already specific enough. The pin is
removed; the ingestion regex was already unrestricted.
4. Ingestion-layer regression tests (FINDING 1)
The previous PR added group-layer tests
(`http-route-extractor.test.ts`) but zero in-tree tests for the
ingestion path. Two new suites pin the
worker → parse-impl → routes flow:
- `test/unit/fastapi-router-bindings.test.ts` (23 cases):
`extractFastAPIRouterBindings()` is split into a stand-alone
module so it can be unit-tested without booting a worker
thread, then pinned for regex shape, two-tier key emission,
relative-import support, and negative cases.
- `test/integration/fastapi-prefix-pipeline.test.ts` (5 cases)
plus `test/fixtures/fastapi-prefix-app/` — runs the full
`runPipelineFromRepo()` against a realistic multi-package
fixture (containing both `api/users.py` and `admin/users.py`)
and inspects the resulting `Route` graph nodes for cross-file
prefix joining and absence of cross-package bleed.
Verification
- `npx tsc --noEmit`: pass
- PR-touched test suites (6 files / 117 cases): all green
- `npx prettier --check`: pass on touched files
- `npx eslint`: 0 errors on touched files
Cache / compatibility
The new `routerModuleAliases?` field on `ParseWorkerResult` and
`routerModuleAliases` on `WorkerExtractedData` are optional /
guarded with `?? []`, so historical parse-cache entries continue
to load without forced re-scan.
Refs PR #1877.
* refactor(ingestion): move fastapi-router-bindings out of workers/ — pure module, not a worker
Addresses @magyargergo's `CHANGES_REQUESTED` review on PR #1877:
> Sorry I just found that we are introducing a new worker in the PR.
`gitnexus/src/core/ingestion/workers/fastapi-router-bindings.ts` was a
**pure-function module** — it never imported `worker_threads` or
`parentPort`, never spawned a worker, and was never registered as a
worker entry. It was placed in `workers/` purely because it was split
out of `workers/parse-worker.ts` to make its functions unit-testable
without booting a worker thread (parse-worker is itself the worker
entry and cannot be loaded from the main thread).
To remove the misleading directory placement:
• The implementation moves to
`gitnexus/src/core/ingestion/route-extractors/fastapi-router-bindings.ts`,
alongside the other framework-specific route extractors (`expo`,
`nextjs`, `php`, `laravel`, `middleware`, `response-shapes`).
• `workers/parse-worker.ts` keeps a thin re-export so the worker
entry can keep using `extractFastAPIRouterBindings` directly. The
re-export now carries an explicit comment stating that the imported
file is **not** a worker and that the `workers/` directory
deliberately hosts only true worker entries (`parse-worker.ts`,
`worker-pool.ts`, `quarantine.ts`).
• The new file's leading docstring opens with "NOT A WORKER" and
explains why it exists where it does.
• The unit test (`test/unit/fastapi-router-bindings.test.ts`) is
updated to import from the new path.
No behaviour change. The function body, signatures, and exported types
are identical.
Verification
• `npx tsc --noEmit`: pass
• `npx tsc` (dist rebuild): pass
• `test/unit/fastapi-router-bindings.test.ts` (23 cases): all green
• `test/integration/fastapi-prefix-pipeline.test.ts` (5 cases): all green
• `test/unit/group/http-route-extractor.test.ts` (63 cases): all green
• `npx prettier --check` on touched files: pass
• `npx eslint` on touched files: 0 errors
Refs PR #1877.
* refactor(ingestion): drop parse-worker re-exports; consumers import router types directly from route-extractors
Addresses @magyargergo's two remaining review comments on PR #1877:
1. **`gitnexus/src/core/ingestion/workers/parse-worker.ts:247`** —
"Can you please remove them and update the call sites?"
The `export type { ExtractedRouterInclude, ExtractedRouterImport,
ExtractedRouterModuleAlias } from '../route-extractors/...'` block
in parse-worker.ts is gone. The remaining `import type {…}` is
purely local — used only to type the corresponding fields on
`ParseWorkerResult` below — and the leading comment now says so
explicitly ("this file does NOT re-export them"). The
`extractFastAPIRouterBindings` symbol is also no longer re-exported
from parse-worker.ts; it's still imported here so the worker entry
can call it per file, but downstream consumers must reach it via
`route-extractors/fastapi-router-bindings` directly.
Call sites updated:
- `gitnexus/src/core/ingestion/parsing-processor.ts`
- `gitnexus/src/core/ingestion/pipeline-phases/parse-impl.ts`
Both files now `import type { ExtractedRouterInclude,
ExtractedRouterImport, ExtractedRouterModuleAlias }` directly from
`route-extractors/fastapi-router-bindings.js`. The worker types
they still need (`ParseWorkerResult`, `ExtractedToolDef`, etc.)
keep coming from `workers/parse-worker.js`.
The unit + integration tests already imported from the new path,
so no test changes were required.
2. **`gitnexus/src/core/ingestion/parsing-processor.ts:168`** —
suggested simplification:
for (const item of result.routerIncludes ?? []) allRouterIncludes.push(item);
for (const item of result.routerImports ?? []) allRouterImports.push(item);
for (const item of result.routerModuleAliases ?? []) allRouterModuleAliases.push(item);
Applied verbatim. Replaces the previous `if (result.…) for …`
guards. The cache-compat semantics are unchanged — historical
parse-cache entries that lack these fields still load cleanly,
the new form just spells the fallback inline.
No behavior change, no tests touched, no public API change.
Verification
• `npx tsc --noEmit`: pass
• `npx tsc` (dist rebuild): pass
• PR-touched test suites (6 files / 117 cases): all green
• `npx prettier --check` on touched files: pass
• `npx eslint` on touched files: 0 errors
Refs PR #1877.
* refactor(ingestion): hoist fastapi-router-bindings type imports to top of parse-worker.ts
Move the `import type { ExtractedRouterInclude, ExtractedRouterImport,
ExtractedRouterModuleAlias }` block to the top of the file with the
other type imports, and drop the comment that previously sat next to
ExtractedDecoratorRoute.
---------
Co-authored-by: henry <zhangwei2017@unipus.cn>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
|
||
|
|
97c1f85e87
|
refactor(cpp): Use function-type ADL entities (#1822)
* fix(cpp): use function-type ADL entities * test(hooks): stabilize concurrency burst reporting * Fix C++ return type capture subtag handling * Harden C++ function-type ADL extraction --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
99168be773
|
feat(ingestion): trace indirect call patterns — FastAPI Depends() and frontend HTTP consumers (#1852) |