mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-14 23:22:54 +00:00
532 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
36ff15151f
|
fix(typescript): name HOC-wrapped const declarations (forwardRef / memo / useCallback / useMemo / observer) (#1261)
Some checks are pending
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
* fix(typescript): name HOC-wrapped const declarations (forwardRef / memo / useCallback / useMemo / observer / debounce) Follow-up to issue #1166 / PR #1175. After fixing HOF callbacks (Promise fan-out, queryFn pair-arrows, multi-action Zustand stores) and JSX-as-call, the dominant residual 0%-capture pattern in real React UI codebases was the HOC-wrapped variable declaration: const Button = React.forwardRef((props, ref) => { ... }) const Card = memo((props) => { ... }) const handleClick = useCallback(() => { ... }, []) const computed = useMemo(() => { ... }, []) const debouncedSearch = debounce((q) => { ... }, 250) All share the AST shape `lexical_declaration > variable_declarator > call_expression > arguments > arrow_function`. Pre-fix, neither the registry-primary `query.ts` nor the legacy `tree-sitter-queries.ts` had a `@declaration.function` pattern matching this shape, and the legacy DAG's `tsExtractFunctionName` only walked `variable_declarator` and `pair` parents — `arguments` parents fell through with `funcName = null`. Result: every shadcn/Radix component, every memoised React component, and every `useCallback` / `useMemo` callback bound to a const registered as anonymous; calls inside attributed to the file. Sourcerer-fe audit: ~296 declarations affected (~57 forwardRef + ~21 memo + ~161 useCallback + ~57 useMemo). Fix: - 4 new tree-sitter patterns in `languages/typescript/query.ts` (registry-primary), anchored on the inner arrow_function / function_expression — same anchor discipline as the existing `lexical_declaration` and `pair` patterns from PR #1175. - 8 mirrored patterns in `tree-sitter-queries.ts` (4 in TYPESCRIPT_QUERIES, 4 in JAVASCRIPT_QUERIES) for the legacy DAG and the CI parity gate. - New `arguments`-parent branch in `tsExtractFunctionName` that walks `arguments → call_expression → variable_declarator` and returns the const's name. Three guards keep it strictly scoped to HOC-wrapped declarations; bare statement-level HOC calls fall through anonymous. Tests: - 11 integration tests + 9 minimal TS/TSX fixtures exercising forwardRef / memo / useCallback / useMemo / observer / debounce, with positive (named-Function + correct CALLS edge), negative (no phantom Functions for unbound HOCs, no phantom self-loops, no first-sibling-wins leakage), and cross-pollination assertions. - 8 new unit tests in `call-attribution-issue-1166.test.ts` pinning the legacy-DAG path: 6 attribution tests + 2 @definition.function capture tests. Trade-off documented inline: chained array-method declarations (`const x = arr.find((y) => p(y))`) match the same shape and produce a mostly-harmless phantom `Function:x` with one outgoing edge. The false-positive cost is negligible vs. the React UI coverage gain. Verification: - 11/11 typescript-hoc-wrapped (registry-primary) - 26/26 call-attribution-issue-1166 (8 new + 18 pre-existing) - 266/266 across all 4 typescript resolver test files (registry) - 236/236 typescript.test.ts on legacy DAG (CI parity gate) - 1693/1693 across all non-Kotlin/Swift resolver test files - tsc --noEmit clean; prettier clean; eslint clean (no new warnings) Co-authored-by: Cursor <cursoragent@cursor.com> * test(typescript): pin documented HOC trade-offs and close var-form parity gap Addresses the four findings on PR #1261 (Claude bot review for #1261). All findings flagged missing assertion tests for behaviour already documented in code comments — none reported a real bug. The verdict was "production-ready with minor follow-ups"; these tests strengthen the documentation-to-test contract. [medium #1] Array-method false-positive Pin `const found = items.find((item) => predicate(item))` → `predicate.attributedTo === 'found'` as an accepted FP. The const is a value, never invoked, so no incoming CALLS edge ever points at it; the outgoing edge is a minor mis-attribution we accept rather than maintain a HOC allowlist. [medium #2] Nested HOCs (`memo(forwardRef(...))`) — no phantom Function:Wrapped Two integration tests in `typescript-hoc-wrapped.test.ts`: 1. `Wrapped` is NOT a Function node (the outer call's first arg is a call_expression, not an arrow — no @declaration.function pattern matches the outer shape). 2. The deepest arrow's `helper()` call is NOT attributed to Function:Wrapped (the deepest arrow is anonymous because call_expression.parent is `arguments`, not `variable_declarator`), and no Function-sourced CALLS originate from `nested.tsx`. [medium #3] Multi-arrow argument dedup Pin `const x = call(() => first(), () => second())` — both arrows share the same `arguments → call_expression → variable_declarator` ancestor chain on the legacy DAG, so both attribute to "x". Documents the registry-primary dedup story alongside. [low #4] `var X = HOC(...)` parity gap Registry-primary `query.ts` had `(variable_declaration ...)` HOC patterns but legacy `tree-sitter-queries.ts` (TS + JS) did not. Closes the gap by mirroring two `(variable_declaration ...)` HOC patterns into both legacy sections so the parity gate stays tight even if a codebase mixes `var X = HOC(...)` with `const X = HOC(...)`. Validation - Targeted: 41/41 (28 unit + 13 integration) on registry-primary. - Broader TS suite: 60/60 across 4 resolver test files. - CI parity gate (`typescript.test.ts`): 236/236 on legacy DAG and 236/236 on registry-primary. - Prettier clean. ESLint clean (5 pre-existing non-null-assertion warnings in the test file, unrelated). tsc --noEmit clean. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
7f8b01d506
|
refactor(ingestion): consolidate per-language patterns into LanguageProvider (#1279)
* refactor(ingestion): consolidate per-language patterns into LanguageProvider Move entry-point name patterns and AST framework detection patterns from shared maps in entry-point-scoring.ts and framework-detection.ts into each LanguageProvider. The shared files now build their lookup tables dynamically from the provider registry at module load. This aligns with the architecture principle that shared pipeline code must not name languages. Adding a new language no longer requires modifying entry-point-scoring.ts or framework-detection.ts — the provider file is the single source of truth for all language-specific data. New LanguageProvider fields: - entryPointPatterns: RegExp[] (default: []) - astFrameworkPatterns: AstFrameworkPatternConfig[] (default: []) * test(ingestion): add provider-registry, multiplier/reason, and Kotlin/Dart/Ruby entry-point coverage Addresses review feedback on the per-language pattern consolidation: - Runtime guard that providers map covers every SupportedLanguages member, catching enum/registry drift that the compile-time `satisfies` cannot. - Multiplier/reason parity assertions for nestjs (3.2/nestjs-decorator), spring (3.2/spring-annotation), and fastapi (3.0/fastapi-decorator) so a silent value change during future relocations would fail loudly. - Entry-point pattern coverage for Kotlin (Android lifecycle, ViewModel, Service), Dart (Flutter widget lifecycle), and Ruby (call/perform/execute) — the three providers whose patterns moved without representative tests. * refactor(ingestion): apply satisfies AstFrameworkPatternConfig[] to remaining providers The c-cpp, dart, php, ruby, and swift providers imported AstFrameworkPatternConfig but never used it, which the root ESLint config flagged as a hard error in the quality / lint CI gate. Use the type the same way csharp/go/java/kotlin/python/rust/typescript already do — as a satisfies assertion on the astFrameworkPatterns array. This both clears the unused-import error and gives every provider compile-time validation of pattern shape, narrowing the gap that the original review flagged about lost exhaustiveness on the optional astFrameworkPatterns field. --------- Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
114d5304d9
|
fix(mcp): avoid git from non-repo cwd in sibling cwd match (#1138) (#1293)
* fix(mcp): avoid git shellout from non-repo cwd for sibling match checkCwdMatch used getGitRoot(cwd), which runs git rev-parse from the launch cwd (often \C:\Users\gergo in MCP stdio). Resolve the cwd git root via ancestor .git checks first, then keep existing remote-based sibling logic. Fixes #1138 Co-authored-by: Cursor <cursoragent@cursor.com> * test(mcp): address PR #1293 review follow-ups Three test gaps flagged by review on the #1138 fix: - sibling-clone-drift.test.ts: the existing "non-git cwd" test only asserted match=none, which the pre-fix code also returned (by silently failing the spawn). Wrap child_process / node:child_process with passthrough vi.fn() spies and assert no execSync/execFileSync call is recorded when checkCwdMatch runs against a non-git cwd, so a regression that re-introduces the spawn fails loudly. - git.test.ts: add coverage for findGitRootByDotGit's three untested inputs — a `.git` FILE (linked worktree / submodule), a path that does not exist, and a file path inside a repo (must walk from the parent dir). Each asserts no subprocess was spawned. No production code changes. Test additions only. --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
1fe3bf9399
|
feat(mcp): add tool safety annotations (#1127)
* feat(mcp): add tool safety annotations * test(mcp): address PR #1127 review follow-ups - Replace private `_requestHandlers` SDK access in server.test.ts with `Client` + `InMemoryTransport.createLinkedPair()` for the tools/list annotation propagation test. The new path uses supported public APIs and surfaces SDK changes loudly instead of silently degrading. - Extract `OPEN_WORLD_READ_ONLY_TOOLS` set in tools.test.ts so future read-only open-world tools can be added without rewriting the invariant; preserves the current "only `query` is open-world" guard. - Add inline rationale on `group_sync` annotations explaining the conservative `idempotentHint: false` (writes contracts.json on every call even when output is deterministic). No runtime behavior change. Annotations themselves and tools/list shape are unchanged. --------- Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
db22a89021
|
fix(embeddings): bridge HF_ENDPOINT env var to transformers.js env.remoteHost (#1205) (#1252) | ||
|
|
b9a17f553d
|
feat(group): auto-discover Rust workspace cross-crate contracts (#1256)
Some checks are pending
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
|
||
|
|
bc722b9d8f
|
fix(group): resolve custom manifest links against graph symbols (#1254) | ||
|
|
368049576b
|
fix(python): make multi-segment suffix fallback deterministic (#1253) | ||
|
|
0418cbb347
|
fix(cli): keep GitNexus ignores inside .gitnexus (#1248)
Some checks are pending
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
* fix(cli): keep GitNexus ignores inside .gitnexus Avoid mutating analyzed repositories' root .gitignore while keeping generated GitNexus state untracked via .gitnexus/.gitignore. Made-with: Cursor * fix(cli): also use git info exclude for GitNexus storage When an analyzed repo has a real .git directory, add .gitnexus/ to .git/info/exclude so local Git metadata ignores generated storage without touching root .gitignore. Made-with: Cursor * fix(cli): keep skip-git subdir indexes ignored Ensure full analyze always writes the internal GitNexus ignore file so parent Git repositories stay clean for --skip-git subdirectory indexes. Made-with: Cursor |
||
|
|
4be4abe8e4
|
fix(group): contract extractors honour .gitnexusignore via shared IgnoreService (#1185) (#1247)
* fix(group): contract extractors honour .gitnexusignore via shared IgnoreService (#1185) The HTTP, gRPC, and topic contract extractors each globbed the repo with a hardcoded `ignore: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**', '**/vendor/**']` array, bypassing the shared `IgnoreService` that the rest of the ingestion pipeline uses for `.gitnexusignore` and `.gitignore` parsing. Result: a vendored Python venv (`mentor_env/`), generated stubs, or any user-defined exclusion silently produced false-positive contracts. Replace each hardcoded array with `createIgnoreFilter(repoPath)`, mirroring the canonical pattern in `filesystem-walker.ts`. The 5 hardcoded names are all in `DEFAULT_IGNORE_LIST`, so default behaviour is preserved; users now also get `.gitnexusignore` patterns, the rest of the hardcoded list (e.g. `__pycache__`, `.pytest_cache`), and the `.gitnexusignore` negation semantics introduced in #771. The topic extractor additionally filters Go `*_test.go` at the glob level. That filter is preserved via a small wrapper around `createIgnoreFilter` that short-circuits before delegating, so glob-level pruning still applies and the existing `_test.go` skip test (with new content asserting the pruning is real) still passes. Tests added to all three `*-extractor.test.ts` files exercising `.gitnexusignore` honouring end-to-end via real temp directories. * test(group): exercise gRPC source-scan ignore + add .gitignore-only coverage (#1185) Addresses two findings from the @claude review on PR #1247: [medium] The gRPC ignore test claimed to cover both proto-context and source-scan paths but only wrote a .proto file under mentor_env/. Added a Python `_pb2_grpc.<Name>Stub(channel)` consumer file under the same ignored dir (mirroring the canonical pattern from `test_extract_python_stub_returns_consumer`); without the `.gitnexusignore` filter that file would emit a consumer contract. The test now exercises both `createIgnoreFilter` calls inside the gRPC extractor (`buildProtoContext` + `extract`) in a single run, with both defence-in-depth path-prefix assertions and a specific `role: consumer` LeakedService assertion. [low] Added one shared .gitignore-only test on the HTTP extractor. `createIgnoreFilter` reads both `.gitignore` and `.gitnexusignore` via `loadIgnoreRules`, but no extractor-level test exercised the `.gitignore` path. One shared test is sufficient because all three extractors consume the same filter object — verified at `IgnoreService` level already. The remaining [low] finding — "negation semantics (!pattern) not tested at extractor level" — is deferred deliberately, not skipped. Three reasons: 1. The negation logic (introduced in #771) lives entirely inside `createIgnoreFilter`'s `hasExplicitUnignore` ancestor-walk in `ignore-service.ts`. The extractors only consume the returned filter object — they never inspect patterns, never call `hasExplicitUnignore` directly, and have no code path that could diverge from the IgnoreService's negation behaviour. 2. Negation is already locked in by 8 dedicated unit tests in `test/unit/ignore-service.test.ts` (the #771 suite), plus the `!parent/` + `parent/child/` last-match-wins regression test added in PR #1046. An extractor-level negation test would re-prove the same code path and would not catch any failure mode the existing tests don't already catch. 3. The bot itself flagged the gap as "Acceptable to leave as follow-up referencing existing IgnoreService negation tests" — the deferral matches its own recommendation. If a future change inserts an extractor-side wrapper around the filter (as topic-extractor.ts already does for `*_test.go`) that could plausibly affect negation, an extractor-level negation test should be added at that point — not pre-emptively here. |
||
|
|
59acfb2261
|
fix(python): walk ancestors for multi-segment dotted imports (#1241)
* fix(python): walk ancestors for multi-segment dotted imports (#1240) Single-segment Python imports (`from middleware import X`) already get an ancestor-directory walk in `resolvePythonImportInternal`, so they resolve correctly when the importer and the imported module share a parent directory (e.g. both under `backend/`). Multi-segment dotted imports (`from services.sync import X`) were only resolved against the workspace root. In a `backend/`-prefixed repo, `from services.sync import X` from `backend/routers/cron.py` would not resolve because `services/sync.py` does not exist at the workspace root — only `backend/services/sync.py` does. The IMPORTS edge was dropped, the imported names were never bound, and downstream CALLS edges to those names were silently lost. The fix mirrors the single-segment ancestor walk for multi-segment paths in `resolveAbsoluteFromFiles`, and widens `hasRepoCandidate` to accept nested `/segment/` matches so it does not bail before the walk runs. Includes a new fixture and 5 integration tests covering: - IMPORTS resolution for `from services.sync`, `from services.alerts`, `from routers.alerts` from `backend/routers/cron.py`. - CALLS edge counts for every multi-segment-imported callee. - Regression check: single-segment ancestor walk (`from auth_utils import …`) still resolves correctly. The django-app-imports regression suite (which prevents `accounts.apps` from spuriously matching a local `apps.py`) continues to pass — the new nested-namespace check in `hasRepoCandidate` is bounded by an explicit `/segment/` substring, and the workspace-root candidate check still runs first. * fix(python): scope hasRepoCandidate widening to importer ancestors + tighten ancestor-walk loop Address review findings from PR #1241: 1. hasRepoCandidate's nested check now requires the matching directory to sit on an ancestor of the importer. Previously any nested /SEGMENT/ path satisfied the gate, which would let a vendored copy of an external package (e.g. vendor/django/urls.py) gate-pass an external import like 'from django.urls import path' issued from app/main.py. 2. Loop bound in resolveAbsoluteFromFiles tightened from 'i >= 0' to 'i > 0' to skip a redundant root-candidate recheck (the workspace-root direct check above already covers that case). 3. Doc-comment in resolveAbsoluteFromFiles now states the precedence order explicitly: workspace root > closest ancestor > suffix fallback. Tests added: - Vendored-external false-positive guard (vendor/django/urls.py must not resolve from app/main.py). - Workspace-root vs ancestor precedence (root services/sync.py wins over backend/services/sync.py for a backend/routers/cron.py importer). 215/215 python integration tests pass (+4 from this change). tsc --noEmit green. |
||
|
|
6372b0bfeb
|
fix(cli): --skip-git treats cwd as index root instead of walking up to parent git repo (#1245)
|
||
|
|
71e1e8a3f0
|
fix(deps): pin tree-sitter-c/cpp to fix Windows segfault (#1242) (#1243)
* fix(deps): pin tree-sitter-c/cpp to fix Windows segfault (#1242) `tree-sitter-c@0.23.2` ships native prebuilds compiled against tree-sitter ABI 14 (tree-sitter-cli >=0.24), while GitNexus is pinned to the tree-sitter@0.21.1 JS runtime. On Windows the JS runtime hits `Cannot read properties of undefined (reading '161')` inside `unmarshalNode` and a native segfault in the parse-worker pipeline on real C codebases (e.g. STM32 headers from the issue reporter). Two coordinated registry pins fix the root cause without any override gymnastics or vendoring: - `tree-sitter-c` -> `0.21.4` (last release built against the tree-sitter@0.21 ABI; declared peer `^0.21.0`). - `tree-sitter-cpp` -> `0.23.2` (last 0.23.x release before tree-sitter-cpp added a runtime dep on the broken-ABI `tree-sitter-c@^0.23.1`; pinning here lets us drop the previous global override entirely). `npm ls tree-sitter-c` is now clean: single deduped 0.21.4, no `overridden` annotations, no nested copy. Parser loader collapsed to one declarative table: - One `SOURCES` map with `{ load, unavailableNote, optional? }` rows for every grammar including TSX. Adding/removing a grammar is one entry; `unavailableNote` is mandatory and the type checker enforces it, so failures are never silent and never generic. - Single `loadGrammar(key)` does lazy require + cache + per-failure classification. Required failures `console.error` the note and rethrow the original (preserves stack); optional failures `console.warn` and report the language as Unsupported. One warn-once `Set` deduplicates per language key. - The previous bespoke `warnCUnavailable` + `cWarningEmitted` state and 4 conditional spreads in the language map are gone. Per-grammar `unavailableNote` strings name the package, list the most likely failure mode for that grammar, and link the relevant tracking issue (#1013, #1125, #1130, #1242) where applicable. Tests: new `C parser ABI compatibility (#1242)` block under parser-loader.test.ts exercises the actual failure paths (non-trivial parse + tree walk + Query.captures + TreeCursor descent). The original report's `unmarshalNode` crash sits on exactly the traversal hot path these tests now cover. Validation: - npx tsc --noEmit: clean - npx vitest run test/unit: 4808 passed, 10 skipped - npx vitest run test/integration/resolvers/cpp.test.ts: 133/133 - minimal C parse + walk + query + cursor verified manually under tree-sitter@0.21.1 + tree-sitter-c@0.21.4 on Win11 x64 / Node 22 Closes #1242. Does not unblock the broader tree-sitter@0.25 upgrade tracked in #858. Made-with: Cursor * chore(ci): redesign tree-sitter upgrade-readiness report (#858) The daily script that owns the body of #858 used to dump one giant matrix and leave a human to figure out which grammars are actually ready to bump. After pinning `tree-sitter-c@0.21.4` and `tree-sitter-cpp@0.23.2` for #1242, several rows in that matrix now look like regressions when in fact they are deliberate. The report now classifies each grammar instead of just listing them. What changed in `check-tree-sitter-upgrade-readiness.py`: - New `INTENTIONAL_PINS` table documents grammars deliberately held below `npm latest`, with a one-line rationale and a tracking issue per row (#1242 for C and C++, #1013 for C#). The script reads pins straight from `gitnexus/package.json` so a future bump cannot drift away from this report. - New `_classify_grammar(...)` produces one primary disposition per grammar: Ready for 0.25 / Intentionally pinned / Waiting on upstream npm release / Blocked on upstream / Could not check. The dispositions drive the report layout. - New `vendored_drift_summary(...)` covers all three vendored parsers (`tree-sitter-proto`, `tree-sitter-dart`, `tree-sitter-swift`) uniformly: ABI from `parser.c` when present, upstream npm + GitHub status, and the rationale extracted from each vendor's `_vendoredBy` field. Prebuilt-only vendors (Swift today) report `ABI 'prebuilt'` instead of `None`. - Report layout: top-of-page TL;DR + counts, an actionable "What you can do today" section, then one section per disposition bucket, then a dedicated "Vendored parsers" section. The original raw matrix is preserved inside a collapsible `<details>` block so the row-diff bot that watches this issue still has stable input. - `sys.stdout.reconfigure(encoding="utf-8")` so the workflow no longer crashes on Windows when the report contains arrows or em-dashes. No workflow / cron changes; the daily job posts the new body the next time it runs. #858 itself was updated by hand in the meantime to keep the tracker readable. Made-with: Cursor * fix(parser-loader): log C grammar load failures at error severity (#1242) Addresses review feedback on #1243. `tree-sitter-c` is in `dependencies` (not `optionalDependencies`) so a load failure on a supported platform always indicates a real install problem the user needs to see — corrupted node_modules, unsupported Node version, or an ABI mismatch with the bundled runtime. Previously the optional-grammar machinery downgraded that to `console.warn`, which can be missed in long log streams and silently drops C analysis for an entire repo. Decouples log severity from throw behavior: - `GrammarSource.severity?: 'warn' | 'error'` is a new optional field that overrides the default log level for a load failure. Default is `error` for required grammars and `warn` for optional ones, matching the prior behavior for every existing row. - `LoadResult` carries the resolved severity through `loadGrammar` so `logFailure` no longer derives it from `fatal`. - `tree-sitter-c` row sets `optional: true, severity: 'error'`. The pipeline still degrades gracefully (callers see Unsupported instead of a thrown error), but the diagnostic is loud and the `unavailableNote` now spells out what to try first (`npm rebuild tree-sitter-c`, reinstall) and links the tracker. No test changes needed: `parser-loader.test.ts` exercises behavior on the success path and on optional-failure dispatch; severity is a display-only concern routed through `console.error` vs `console.warn`, which the existing tests don't assert on. Made-with: Cursor * fix(ci): treat intentional pins as 0.25 blockers in readiness report Addresses review feedback on #1243. `_classify_grammar` returned bucket `intentional` before checking `target_compat`, and the per-grammar status loop only added a row to `blockers` when npm-latest was incompatible with the target runtime. The combination meant: if every other grammar resolved tomorrow but we were still holding `tree-sitter-c@0.21.4` and `tree-sitter-cpp@0.23.2` (both incompatible with `tree-sitter@0.25.x`), the script would emit "**Ready** — all grammars are 0.25-compatible" and mislead maintainers into thinking the runtime upgrade was unblocked. Fix: - The status loop now adds an entry to `blockers` whenever a grammar is in `INTENTIONAL_PINS`, regardless of npm-latest's peer dep. The blocker message names the pinned spec, embeds the rationale from `INTENTIONAL_PINS`, and tells the reader the pin must be lifted before the target runtime upgrade. When the pin is removed (entry deleted from `INTENTIONAL_PINS`), the grammar resumes standard classification on the next run. - `bump_now` now excludes intentional pins so they never show up in the "What you can do today" section. Bumping an intentional pin requires a deliberate edit to both `INTENTIONAL_PINS` and `package.json`, not a one-line dependency bump. Verified locally: TL;DR now reports 8 blockers (6 upstream + 2 intentional) where it previously reported 6, and the verdict correctly remains **Blocked** even in the hypothetical future where all upstream blockers clear. Made-with: Cursor |
||
|
|
b278926bb6
|
chore(deps)(deps): bump onnxruntime-node in /gitnexus (#1213)
Bumps [onnxruntime-node](https://github.com/Microsoft/onnxruntime) from 1.24.3 to 1.25.1. - [Release notes](https://github.com/Microsoft/onnxruntime/releases) - [Changelog](https://github.com/microsoft/onnxruntime/blob/main/docs/ReleaseManagement.md) - [Commits](https://github.com/Microsoft/onnxruntime/compare/v1.24.3...v1.25.1) --- updated-dependencies: - dependency-name: onnxruntime-node dependency-version: 1.25.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
b4f9d56f33
|
chore(deps)(deps): bump mnemonist from 0.40.3 to 0.40.4 in /gitnexus (#1239)
Some checks are pending
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
Bumps [mnemonist](https://github.com/yomguithereal/mnemonist) from 0.40.3 to 0.40.4. - [Release notes](https://github.com/yomguithereal/mnemonist/releases) - [Changelog](https://github.com/Yomguithereal/mnemonist/blob/master/CHANGELOG.md) - [Commits](https://github.com/yomguithereal/mnemonist/compare/0.40.3...0.40.4) --- updated-dependencies: - dependency-name: mnemonist dependency-version: 0.40.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
6f42253dfd
|
fix(cli): surface silent finalize-skips so analyze cannot exit 0 without persisting (#1169) (#1237)
* fix(cli): surface silent finalize-skips so analyze cannot exit 0 without persisting (#1169) Closes #1169. On Windows, `gitnexus analyze .` was observed to exit with code 0 after printing only the "GitNexus Analyzer" banner. `.gitnexus/lbug.wal` was written but `meta.json` was never persisted and the repo was not added to `~/.gitnexus/registry.json`, so `gitnexus list` / `status` reported no indexed repository. The reporter confirmed the same shape on both LadybugDB (1.6.x) and the pre-LadybugDB KuzuDB build (1.4.1), so the silent finalize-skip is upstream of the DB engine and indistinguishable from a healthy index from the user's perspective. This change makes that state a hard, actionable failure regardless of the upstream root cause. Behaviour change - New `assertAnalysisFinalized()` invariant in `repo-manager.ts` checks that meta.json exists at `<repo>/.gitnexus/meta.json` AND that the global registry has a canonical-path-matching entry. Throws `AnalysisNotFinalizedError` (kind: "AnalysisNotFinalizedError") with a diagnostic that names the missing artifact and the storage path the user should inspect. - `analyzeCommand` invokes the invariant on the rebuild path (skipped on `alreadyUpToDate`), so a future silent finalize-skip surfaces with exit code 1 and a recoverable error instead of a silent exit 0. - `analyzeCommand` installs idempotent `unhandledRejection` and `uncaughtException` handlers that bypass the progress bar's console redirection by writing to a stderr handle captured at module load. This addresses the secondary symptom where the `barLog` redirection visually erased stack traces with `\x1b[2K\r` and stripped them via `String(err)`. - The catch block also writes the failing error's full stack via the captured stderr, so failure diagnostics survive any downstream monkey-patching of `process.stdout`/`stderr`. Tests - `test/unit/repo-manager-finalize-invariant.test.ts` (4 tests): cover both `missing="meta"` and `missing="registry-entry"`, the happy path, and Windows case-insensitive registry path matching. - `test/integration/cli-e2e.test.ts` adds a regression test that runs the real CLI on a fresh repo copy, asserts exit 0, AND verifies `meta.json` plus the matching registry entry are both written — catches any future regression of the wiring. Validation - `npx tsc --noEmit` passes. - `npx vitest run --project default` passes for all my touched files (89 tests across 4 files). The full default suite reports 7188 pass with the known native LadybugDB Windows-worker flake unrelated to this change. - `npx prettier --check` clean on the diff. - `npx eslint` reports only pre-existing `any` warnings on the file; no new warnings introduced. - Live repro on the issue's two-file Python fixture reproduces a successful index after the change: meta.json present (742 B), exit 0, `gitnexus list` shows the repo. Rollback Strictly additive — the success path is unchanged when `meta.json` is written and the registry is updated. Reverting the four-file diff is safe; the previous silent-finalize behaviour returns. No persisted schema or registry shape changes. DoD - [x] Runtime wiring is complete on the affected CLI path. - [x] Requested behavior is correct and existing contracts are preserved. - [x] Smallest correct solution — one invariant, one helper, two handlers; no speculative abstraction. - [x] Tests prove the changed behavior at unit AND integration level. - [x] Required validation for `gitnexus/` was run. - [x] Repo boundaries respected; no language-specific code, no shared ingestion changes, no new injection surfaces. - [x] Diff contains only the intended change — no unrelated churn. Made-with: Cursor * fix(cli): enforce analyze finalization on fast path (#1169) Address PR review feedback by checking finalization even when analyze reports already up to date, and by making the #1169 E2E guard fail on timeout instead of passing silently. Made-with: Cursor * test(cli): fix #1169 regression coverage on CI Normalize macOS temp paths in the registry assertion and update the analyze worker timeout test mock for the new finalization invariant exports. Made-with: Cursor |
||
|
|
b5316c2df1 |
test(ci): isolate native LadybugDB and CLI e2e flakes
createFTSIndex now short-circuits on the in-process cache before issuing the native CALL CREATE_FTS_INDEX, so a prior writable session cannot trigger the macOS WAL/checkpoint duplicate-create path observed on main. The cache is also primed on the "already exists" recovery and cleared on re-init/close/drop, keeping ensureFTSIndex semantics identical for read-only fallbacks. The lbug-core-adapter close+reopen test moves to the end of the suite so its native handle churn cannot corrupt later assertions in the same fixture. skills-e2e moves into its own sequential vitest project so the heavy spawnSync-driven CLI fixtures stop competing with the parallel default project on Windows runners, fixing the C-fixture beforeAll timeout. Made-with: Cursor |
||
|
|
aa7c273093
|
fix(ingestion): index Python repos with empty __init__.py and >32 KB files (#1163)
* fix(ingestion): index Python repos with empty __init__.py and >32 KB files Two defensive fixes that let `gitnexus analyze` complete on Python codebases that previously failed. scope-extractor: synthesize an empty Module scope when the provider emits zero captures. Previously threw "no Module scope found", which fired for any 0-byte `__init__.py` package marker if the bridge's empty-source guard was bypassed. python/captures: wrap the parser.parse() and getPythonScopeQuery() .matches() calls in try/catch. node-tree-sitter throws "Invalid argument" for sources that overrun internal buffers (observed at the ~32 KB threshold on Windows). Degrade gracefully with a clear "skipping scope extraction for this file" warning instead of the opaque "Invalid argument" surfacing through the bridge. Verified by indexing whittlem/pycryptobot (which has 7 empty __init__.py and 11 Python files between 34 KB and 158 KB): 2,367 nodes / 4,973 edges, no segfault, queries resolve symbols inside the 158 KB controllers/PyCryptoBot.py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ingestion): harden Python scope extraction fallbacks Keep failed Python scope extraction on the bridge skip path and build synthetic module scopes before extractor indexes are derived. Made-with: Cursor --------- Co-authored-by: Vijay Gali <vgali@vexcelco.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
b79278705a
|
fix(hook): resolve canonical repo root + guard read-only FTS ensure (#1226)
* fix(hook): resolve canonical repo root + guard read-only FTS ensure (#1224) Two bugs in the Claude Code hook + query layer integration: 1. `findGitNexusDir` (in `gitnexus/hooks/claude/gitnexus-hook.cjs` and `gitnexus-claude-plugin/hooks/gitnexus-hook.js`) walked upward from cwd looking for a non-registry `.gitnexus/`. In linked git worktrees created via `git worktree add`, the canonical repo's `.gitnexus/` never sits above the worktree path, so the walk silently fails and neither augmentation nor staleness notifications fire. Fix: keep the cwd-walk as the fast path, then fall back to `git rev-parse --git-common-dir` to resolve the shared `.git/` directory (which lives inside the canonical repo across all linked worktrees) and walk up from its parent. Returns null cleanly when `git` isn't on PATH or cwd isn't inside any working tree. 2. `ensureFTSIndex` in the LadybugDB adapter rethrew when the active connection is read-only (e.g. the MCP query pool, which opens DBs read-only by design). Defensive callers used to surface five "Cannot execute write operations in a read-only database" warnings per query. Fix: extract `isReadOnlyDbError` (mirroring the existing `isDbBusyError` discriminator) and have `ensureFTSIndex` catch the read-only error, cache the key, and return silently. Index creation is owned by `gitnexus analyze` on a writable connection — the ensure call is safely a no-op on the read pool. Lock / busy / "already exists" / schema errors continue to propagate. Tests: - `test/unit/hooks.test.ts`: new "Linked git worktree resolution" block exercises both hooks against a real linked worktree to confirm PostToolUse stale notifications fire, plus a negative case when the canonical repo has no `.gitnexus/`. - `test/unit/lbug-readonly-error.test.ts`: new file unit-tests the `isReadOnlyDbError` discriminator (positive matches, case insensitivity, non-Error inputs, and unrelated errors that must still surface — lock contention, "already exists", schema misses). - `test/integration/lbug-core-adapter.test.ts`: extends the existing FTS coverage with an idempotency assertion for `ensureFTSIndex` to pin the read-only guard's success-path contract. Verified with `npx tsc --noEmit` and `vitest run` on the affected files (hooks + readonly + lbug-core-adapter + bm25-search + lbug-extension-loader + lbug-embedding-hashes — 136 tests pass). Build: `npm run build` succeeds. Closes #1224 * fix(local-backend): cover supported vector path Add the supported-platform regression assertion for QUERY_VECTOR_INDEX and align the unsupported VECTOR diagnostic wording with platform policy. Made-with: Cursor --------- Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
3f0c74fea0
|
fix(deps): upgrade @ladybugdb/core to 0.16.0 to resolve native segfaults (#1235)
* fix(deps): upgrade @ladybugdb/core to 0.16.0 to resolve native segfaults Resolves the SIGSEGV / access-violation (0xC0000005) / exit-139 crashes that have been reported widely since 1.6.3. The native crashes originate in @ladybugdb/core 0.15.x — primarily during FTS index creation, VECTOR extension load, and concurrent query teardown — and are reproducible on Linux, macOS and Windows. The maintainer-confirmed fix is to bump the runtime to 0.16.0, which ships nodejs async + memory-management fixes, extension ABI bump, and macOS Intel binaries. Adopting 0.16.0 cleanly required three supporting changes; without them the upgrade itself regresses other paths: 1. maxDBSize must be passed explicitly. 0.16.0 keeps the upstream JSDoc note that the default 0 is "introduced temporarily for now to get around with the default 8 TB mmap address space limit some environment". Constrained CI runners and laptops cannot reserve 8 TB and crash with "Buffer manager exception: Mmap for size 8796093022208 failed." A new gitnexus/src/core/lbug/lbug-config.ts centralises a 16 GiB default (overridable via GITNEXUS_LBUG_MAX_DB_SIZE) and every Database() construction site now passes it. 2. enableCompression default flipped from false to true in 0.16.0. Every Database() call site is updated to pass false explicitly so existing GitNexus indexes keep the same wire format. 3. Bridge DB sidecar files (.wal, .shadow). 0.16.0 enforces a database-id check on .wal / .shadow sidecars and rejects opens whose sidecars belong to a different base name. writeBridge now (a) cleans the full sidecar set when removing the tmp slot, (b) renames .wal / .shadow alongside the main file during the atomic .tmp -> .lbug swap, and (c) wraps openBridgeDbReadOnly in a bounded retry on transient Win32-Error-33 lock errors. Eager db.init() / conn.init() forces the lazy native handle to surface lock contention at the retry site. Known limitation (not a regression): on Windows the 0.16.0 native binary does not release the OS file lock until the process exits, so the close-then-reopen-same-process pattern raises Error 33 after the first close. Production paths (analyze / serve / mcp each open the DB exactly once per process) are unaffected, but eight tests that exercise the pattern are guarded with a process.platform === 'win32' skip; CI's Linux + macOS shards exercise them as before. Tracking upstream: kuzudb/kuzu#3872 / #3883 / #4730. Closes #1136 #1154 #1160 #1162 #1178 #1195 #1196 #1199 #1204 #1206 Refs #1209 (supersedes — Dependabot bump without the supporting fixes) Made-with: Cursor * fix(test): isolate LadybugDB native test state Use per-suite LadybugDB databases in integration helpers so test forks do not reopen a database created by Vitest global setup, and centralize Windows-tolerant native temp cleanup for bridge tests. * fix(lbug): avoid bridge existence reopen Reuse the built LadybugDB config in the extension installer and avoid native close/reopen cycles when checking bridge existence on Windows. Made-with: Cursor * chore(docs): exclude local lbug plan Keep the refactor planning note out of the PR while leaving the ignored local copy on disk. Made-with: Cursor * refactor(lbug): centralize database construction Route LadybugDB opens through shared helpers so native constructor defaults stay consistent across core, pool, bridge, and extension install paths. Made-with: Cursor --------- Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> |
||
|
|
883091f3e0
|
Merge pull request #1175 from ReidenXerx/fix/typescript-hof-callbacks-and-jsx-as-call
fix(typescript): capture missed CALLS edges from HOF callbacks and JSX |
||
|
|
66ad5c4980 |
fix(typescript): anchor pair-with-arrow @declaration.function on inner arrow
Addresses the medium-severity finding in @abhigyanpatwari's review of #1175: the four `pair`-with-arrow patterns in `query.ts` anchored `@declaration.function` on the outer `pair` node instead of the inner `arrow_function` / `function_expression`. For multi-action object literals like Zustand's persist((set) => ({ addItem: (item) => doA(item), removeItem: (item) => doB(item), fetchData: () => doC(), })) `pass2AttachDeclarations.atPosition(pair.startLine, pair.startCol)` resolved to the *parent* `(set) => ({...})` callback's scope (because the pair node starts at the property-key token, before the inner arrow's `@scope.function` range). All three pair-function defs landed in the same parent's `ownedDefs`, and `resolveCallerGraphId.ownedDefs.find(...)` returned the FIRST one — `addItem` — for every walk-up. Calls inside `removeItem` and `fetchData` mis-attributed to `addItem`; those two functions had zero outgoing CALLS edges in the registry-primary path. Single-pair fixtures (`bump` in `store.ts`, `queryFn` in `query-hook.ts`) masked the defect because there is no ambiguity when only one Function-like def lives in the parent's `ownedDefs` — `find()` is deterministic over a single-element set. Fix: move the `@declaration.function` anchor from the outer `pair` to the inner `arrow_function` / `function_expression`, mirroring the `lexical_declaration` patterns above (`const fn = () => {}`). The def then lands in the arrow's own scope's `ownedDefs`, the `rangesEqual(anchor.range, innermost.range)` auto-hoist promotes the binding to the parent scope (so importers + lookups still find the name in the surrounding scope), and each pair-arrow becomes an independent caller anchor in the walk. Tests: * Updated `useFeature → fetchData` expectation to `queryFn → fetchData` in `typescript-hof-callbacks.test.ts`. The new attribution is structurally correct: `fetchData()` is called from inside the named pair-arrow `queryFn: () => fetchData()`. The pre-fix expectation only worked because the pair-pattern bug rerouted the walk past the syntactic owner. * Added `multi-action-store.ts` fixture with three pair-arrows (`addItem` / `removeItem` / `fetchData`) plus three top-level call targets (`doA` / `doB` / `doC`). Four new tests pin per-action attribution: positive (each action calls its own target), negative (no sibling leakage), exact-set (the full pair set is what we expect), and the regression fingerprint (`addItem → doB` MUST be empty). Validation: * `REGISTRY_PRIMARY_TYPESCRIPT=1 vitest run` on typescript-hof-callbacks (12 tests, +4 new), typescript-jsx-as-call (7), typescript (236), typescript-finalize, typescript-cross-file-imports, call-attribution-issue-1166 (18), all scope-resolution unit suites: 886/886 pass on registry-primary AND legacy DAG paths. * Legacy DAG attribution was already correct via @abhigyanpatwari's `tsExtractFunctionName` pair-parent handling (#1179, merged into this PR earlier); this fix brings the registry-primary path to the same behavior, restoring parity for multi-action objects. * `npx prettier --check .`, `tsc --noEmit`, and `eslint` clean on the three modified/added files. Made-with: Cursor |
||
|
|
ca3d520293 |
style(typescript): fix prettier formatting in tsExtractFunctionName
Single line-length fix in `gitnexus/src/core/ingestion/languages/typescript.ts`
flagged by `quality / format` CI on commit
|
||
|
|
9cd8c3663f
|
fix(local-backend): (#1178)skip vector index query on unsupported platforms (#1181) | ||
|
|
bb6aa0c855 | Merge remote-tracking branch 'origin/fix/issue-1166-calls-edges' into fix/typescript-hof-callbacks-and-jsx-as-call | ||
|
|
851d2ab749 |
fix(typescript): address review findings — formatting + tighter test assertions
Addresses the automated review findings on PR #1175: - prettier --write the 3 files flagged by `quality / format` CI check (query.ts, typescript-hof-callbacks.test.ts, typescript-jsx-as-call.test.ts). - [medium] typescript-jsx-as-call.test.ts: tighten the combined HOF+JSX assertion from `toBeGreaterThan(0)` to `toHaveLength(1)`. A single `<Foo />` is one logical invocation; the bounds-only assertion would have masked a duplicate-CALLS-edge regression (e.g. if both `jsx_self_closing_element` and a generic call pattern matched the same site). - [medium] typescript-hof-callbacks.test.ts: replace the vacuously-true `for (c of calls) expect(...)` Zustand assertion with a structural one. Old form passed unconditionally when `calls` was empty (any change that silenced ALL CALLS edges from store.ts would have slipped through). New form asserts both: (a) at least one File-rooted edge exists (proving the `isCallerAnchorLabel` fallback fires), and (b) no edge sources from anything else (proving the fallback fires exclusively). - [low] finalize-algorithm.ts (`findExportByName`): rephrase the comment to make the language-agnostic nature of the tie-break rule explicit. The implementation was already correct for all migrated languages; only the comment overplayed the TypeScript specificity. - [low] captures.ts (arity synthesis): add a comment explaining why JSX call anchors (`jsx_self_closing_element` / `jsx_opening_element`) intentionally don't synthesize `@reference.arity`. Name-only resolution is correct for React (components aren't overloaded in the current graph model); a JSX-aware synthesizer counting jsx_attribute children would be needed if that ever changes. No production behavior change. All 8/8 HOF + 7/7 JSX + 236/236 typescript + 11/11 api-deep-flow integration tests still pass. gitnexus and gitnexus-shared typechecks clean. Made-with: Cursor |
||
|
|
93a0be2310 |
fix(ts): attribute calls inside HOF/callback patterns to the right function
Two roots in `findEnclosingFunctionId` (parse-worker) and the parallel `findEnclosingFunction` (call-processor): A. `genericFuncName` scanned `arrow_function` / `function_expression` children for the first identifier and returned it. For unparenthesized arrows like `file => processFile(file)` the first identifier is the parameter `file`, so calls inside got attributed to a phantom `Function file` ID and emitted dangling CALLS edges that never showed up in `(:Function)-[:CALLS]->()` queries. B. `tsExtractFunctionName` only named arrows whose parent was `variable_declarator`. Object-property arrows like `addItem: (item) => set(...)` (Zustand stores, TanStack queryFn, React Context providers, config objects) live under a `pair`, so they were treated as anonymous. With no named ancestor up to the file, every call inside fell back to the File and became invisible to `context()` / `impact()`. Fix: - `genericFuncName` returns null for anonymous JS/TS function-likes — the language hook is authoritative. - `tsExtractFunctionName` resolves names from `pair` parents (property_identifier / string keys; computed keys stay anonymous). - Mirror the new shape in `TYPESCRIPT_QUERIES` / `JAVASCRIPT_QUERIES` / the scope-resolution query so pair-with-arrow becomes a Function declaration node — call sourceIds resolve to a real graph node. Adds 18 unit tests pinning attribution and definition behaviour for plain helpers, `arr.map(x => fn(x))`, Promise constructor callbacks, Zustand-style nested HOFs, TanStack query factories, string-keyed pairs, and computed-key anonymity. Fixes #1166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7be595d317 |
fix(typescript): capture missed CALLS edges from HOF callbacks and JSX
Two distinct gaps in the TypeScript scope-resolution path were silently
dropping call edges in real-world React + TanStack + Zustand codebases.
On the bug reporter's repo (Sourcerer-fe, 1185 src/ functions), 504
missing Function->Function CALLS edges are now captured (+61.6%) and
the no-outgoing-CALLS orphan rate drops from 73.2% to 60.3%.
HOF / arrow-callback caller-attribution (3 cooperating fixes):
- typescript/query.ts: @declaration.function anchor moved from the
wrapping lexical_declaration to the inner arrow_function /
function_expression, so anchor.range aligns with @scope.function and
pass2AttachDeclarations lands the def on the arrow's own scope.
- finalize-algorithm.ts: findExportByName prefers callable / class-
like defs over Variable when localDefs contains both for the same
name (TS emits two defs per `const fn = () => {}`).
- graph-bridge/ids.ts: resolveCallerGraphId's walk-up class-fallback
now uses isCallerAnchorLabel restricted to Function / Method /
Constructor / Class / Interface / Struct / Enum, so module-level
calls fall through to the File node instead of mis-attributing to
sibling Variable defs (the Zustand `create()(devtools(...))`
phantom-self-loop regression).
JSX as a CALLS edge (2 cooperating fixes):
- typescript/query.ts: new TSX_JSX_QUERY_SUFFIX (TSX-grammar only)
captures jsx_self_closing_element / jsx_opening_element as
@reference.call.free / @reference.call.member. PascalCase predicate
filters native HTML elements (<div>, <span>) so they don't emit
edges to nonexistent targets.
- typescript/captures.ts: shouldEmitReadMember extended with
jsx_self_closing_element / jsx_opening_element parent cases to
suppress phantom ACCESSES edges on member-form JSX names.
Tests: 8 HOF assertions + 7 JSX assertions across two new integration
test files plus 13 minimal fixtures. typescript.test.ts (236),
api-deep-flow.test.ts (11), and scope-resolution / scope-extractor unit
tests (613) pass with no regressions.
Made-with: Cursor
|
||
|
|
2a0c97c178
|
fix: add platform-aware semantic fallback (#1150)
* fix: add platform-aware semantic fallback Make VECTOR an optional capability so Windows analysis remains stable while semantic embeddings can fall back to exact scan when native vector indexing is unavailable. Made-with: Cursor * fix: remove stale vector pool import Keep the merge with main lint-clean after VECTOR loading moved out of the read pool. |
||
|
|
1f6df5fdbb
|
fix(swift): use official prebuilt parser runtime (#1130)
* fix(swift): use official prebuilt parser runtime Vendor the official tree-sitter-swift 0.7.1 runtime package so Swift parsing works without source-building, while keeping the repo on the current tree-sitter runtime until the broader upgrade is ready. Also preserves Swift resolver correctness for overloaded owned functions and extension-backed type duplicates now that Swift is available by default. Made-with: Cursor * fix(swift): move duplicate type ordering into provider Keep Swift extension candidate ordering behind the LanguageProvider contract and cover the Swift 0.7 init scanner path so parser runtime changes do not leak language-specific logic into shared resolution. Made-with: Cursor * fix(swift): address parser runtime review Add explicit Swift prebuild checks and vendor guidance so parser runtime packaging remains observable and maintainable. |
||
|
|
86abc01445
|
fix(hooks): ignore global registry during staleness checks (#1141)
* fix(hooks): ignore global registry during staleness checks * test(hooks): cover indexed repos under global registry --------- Co-authored-by: laplace young <yangqk12@whu.edu.cn> |
||
|
|
46586a8319
|
fix(group): add configurable cross-link path exclusions to reduce false positives (#1093)
* fix(group): add configurable cross-link path exclusions to reduce false positives Add matching.exclude_links_paths and matching.exclude_links_param_only_paths to group.yaml config. These filter out noisy HTTP contracts (health checks, param-only catch-all routes) from cross-link matching while preserving them in the contract registry for documentation purposes. Defaults are empty/false for backward compatibility — no behavior change unless the operator explicitly configures exclusions. * fix(group): address review findings — filter unmatched, normalize trailing slash, add tests - Excluded contracts no longer inflate SyncResult.unmatched (isNoisy guard) - pathPart in buildNoisyContractFilter strips trailing slashes before comparison - 8 new unit tests for buildNoisyContractFilter covering all code paths - Config-parser test asserts defaults for new matching fields * fix(group): normalize configured exclusion paths and add root-path test - Strip trailing slashes from configured exclude_links_paths at Set-build time so root path '/' (which normalizes to '') matches correctly - Add test: exclude_links_paths: ['/'] suppresses http::GET::/ contracts - Add new matching fields as commented examples in fixture group.yaml (DoD §2.4) * docs(group): document exclude_links_paths and exclude_links_param_only_paths config fields Add JSDoc to MatchingConfig interface, update the microservices guide YAML example and field notes, and scaffold the new fields (commented out) in the group create template. |
||
|
|
ffa0510f9a
|
fix(lbug): prevent DuckDB extension install hangs (#1129)
Some checks are pending
CI / quality (push) Waiting to run
CI / tests (push) Waiting to run
CI / e2e (push) Waiting to run
CI / scope-parity (push) Waiting to run
CI / Save PR Metadata (push) Blocked by required conditions
CI / CI Gate (push) Blocked by required conditions
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
* fix(lbug): bound DuckDB extension install via ExtensionManager (closes #1128) `gitnexus analyze` could hang indefinitely (60% / 85% on Windows) when DuckDB's `INSTALL fts` or `INSTALL VECTOR` was unable to reach `extensions.duckdb.org`. The DuckDB driver's INSTALL is a synchronous network call, so any blocked egress would block the Node event loop forever. Replace the ad-hoc, in-process INSTALL/LOAD scattered across `lbug-adapter.ts` and `pool-adapter.ts` with a single `ExtensionManager` that owns the lifecycle of optional DuckDB extensions: * `LOAD` is always tried first — per-connection, idempotent, no network. * If `LOAD` fails and policy permits, INSTALL runs in a short-lived child Node process bounded by `GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS` (default 15s). The parent loop keeps spinning; on timeout the child is killed with SIGKILL and the capability is flagged unavailable. * Capabilities and install attempts are cached per process, so a single bounded install per extension covers every subsequent call. Install policy is now an explicit, per-context decision: * `auto` (default for analyze) — try LOAD, fall back to bounded INSTALL. * `load-only` — used by `pool-adapter` (serve / MCP read paths) so user queries never block on a network install. * `never` — operator escape hatch for offline / airgapped environments. `createFTSIndex` and `createVectorIndex` now check the boolean return value before issuing the index DDL, so missing extensions degrade BM25 and semantic search gracefully without ever throwing during analyze. Tests: - New unit suite for `ExtensionManager` covering LOAD-first behavior, all three policies, install caching, observability, and warn dedup. - Existing vector-extension integration tests pass against the new boolean return type. - Existing embedding-pipeline mocks updated to return `true`. Docs: `gitnexus/README.md` documents `GITNEXUS_LBUG_EXTENSION_INSTALL` and `GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS` with examples for offline and slow-network environments. Made-with: Cursor * fix(lbug): move DuckDB extension install child into script Keep the bounded out-of-process INSTALL behavior, but replace the inline child code with a stable packaged ESM script. This makes the child process directly runnable and gives debuggable stack traces without source-vs-dist branching or a runtime transpiler. Made-with: Cursor |
||
|
|
780ee83d52
|
fix(install): vendor tree-sitter-dart source (#1125)
Avoid remote git/SSH downloads for the Dart grammar during Docker and npm installs by resolving tree-sitter-dart from vendored source and building it during postinstall. Made-with: Cursor |
||
|
|
38ccf7ceb1
|
fix: recover worker parse stalls (#1121)
* fix(ingestion): recover worker parse stalls Made-with: Cursor * test(ingestion): cover worker timeout controls Made-with: Cursor * docs: document analyze worker timeout controls Made-with: Cursor * fix(ingestion): fail fast after worker pool hard failure Made-with: Cursor * test(ingestion): stabilize worker stall recovery tests Made-with: Cursor --------- Co-authored-by: GitNexus Maintainer <maintainer@gitnexus.local> |
||
|
|
aa7bacd48b
|
fix(search): load FTS during core DB init (#1123)
* fix(search): load FTS during core DB init Made-with: Cursor * test(lbug): rely on core init for FTS extension loading Made-with: Cursor |
||
|
|
2727a8ca2a
|
fix(mcp): project tool_map flows from handlers (#1113) | ||
|
|
77844acb6a
|
Revert "fix: correct OpenCode skills directory from 'skill' to 'skills'" (#1104)
installOpenCodeSkills() was writing to ~/.config/opencode/skill/gitnexus/ but OpenCode only discovers skills from ~/.config/opencode/skills/*/SKILL.md. Skills installed by `gitnexus setup` were silently ignored by OpenCode. - Line 590: path.join(opencodeDir, 'skill') → 'skills' - Line 587: updated JSDoc comment to match |
||
|
|
94a4365e6d
|
fix(serve): serve web UI at root path instead of 404 (#1048)
* fix(serve): serve web UI at root path instead of 404 gitnexus serve returned Cannot GET / because no route handler existed for the root path. Now serves the built gitnexus-web dist at / with SPA fallback for client-side routing. Falls back to a helpful landing page with API links when the web UI hasn't been built yet. Also updates the build script to build and copy gitnexus-web into gitnexus/web/ for the published npm package. * fix(serve): address Copilot review feedback - Use regex SPA fallback that excludes /api paths (avoids serving index.html for unknown API routes) - Add rel="noopener noreferrer" to external link (reverse-tabnabbing) - Move build "done" log after web UI step * fix(build): use npm run build for web UI, add npm install guard The build script ran `npx tsc -b && npx vite build` in gitnexus-web/, but CI only installs node_modules for gitnexus/ — not gitnexus-web/. npx then resolved the wrong `tsc` package (a trojan on npm), causing all CI jobs to fail. Fix: add an npm install guard when node_modules is missing, and use `npm run build` (which runs the local typescript) instead of npx. * feat(serve): styled fallback page, asset 404s, build script safety - Add landingPageHtml() with gitnexus-web design tokens (void bg, surface cards, accent color, terminal-style build command block). - Add resolveWebDistDir() helper with non-ENOENT error logging. - Register express.static with Cache-Control headers (no-cache HTML, immutable assets) and SPA fallback route. - Replace wildcard SPA fallback with regex that excludes /api/* AND asset-like file extensions (.js, .css, .ico, .woff2, .map, etc.). - Add ordering comment warning about SPA fallback route placement. scripts/build.js: - Change npm install to npm ci. - Add timeout: 120_000 to all execSync calls. Test coverage: - 26 new unit tests for design tokens, terminal block, external links, SPA regex acceptance/exclusion, cache headers, and fs.access edge cases. Closes #1048 (review feedback) * fix: format, lint, and add GITNEXUS_WEB_DIST env var - Remove unused fsType import from web-ui-serving.test.ts (lint error) - Run prettier on fallback-page-screenshot.html and test file - Add GITNEXUS_WEB_DIST env var as primary override in resolveWebDistDir - Add tests for env var: prefer when set, fallback when dir missing * fix: use cross-platform path matching in env var tests Path.includes('/env/dist') fails on Windows where path.join produces backslashed paths. Normalize via path.sep replacement before matching. * fix(serve): address PR #1048 review findings - Add uncaughtException/unhandledRejection crash guards to HTTP serve path - Export SPA_FALLBACK_REGEX so tests use the production constant (no drift) - Export staticCacheControlSetHeaders so tests verify the real production function - Add real Express dispatch tests for API 404 and asset 404 isolation - Delete committed debug artifact fallback-page-screenshot.html |
||
|
|
c4999b02b0
|
fix(scope-resolution): avoid variadic reference site aggregation (#1112)
Materialize finalized reference sites without spreading large arrays into push so large repositories do not overflow the JS argument stack. |
||
|
|
1e80285c47
|
fix(scope-resolution): allow same-range Module-as-parent for top-level scopes (closes #1086) (#1087)
* fix(scope-resolution): allow same-range Module-as-parent for top-level scopes (closes #1086) When a C# file consists of a single top-level `namespace_declaration` that ends exactly at EOF (no trailing newline, no leading content outside the namespace's `{}` body), tree-sitter-c-sharp 0.23.1 reports identical byte ranges for `compilation_unit` and `namespace_declaration`. Pre-fix the scope-extractor parent-finder relied on strict containment, so the Module was popped off the stack and the Namespace ended up with `parent === null` → `ScopeTreeInvariantError: non-module-requires-parent` → `extractParsedFile` swallowed the throw and the whole file was dropped from the registry-primary path. Cross-file IMPORTS / CALLS edges originating in or terminating at that file vanished. Hit on three real-world `*.Designer.cs` files in PersistentWindows (`HotKeyWindow.Designer.cs`, `LaunchProcess.Designer.cs`, `DbKeySelect.Designer.cs`) — all have the byte signature `<BOM><CRLF>namespace ... { ... }<EOF>` (last hex = `... 7D 0D 0A 7D`). The fix is a single carve-out in the parent-validity contract: a `Module` may parent a same-range non-`Module` child. The relationship stays acyclic because the carve-out is direction-asymmetric — only Module-as- outer parents a same-range non-Module, never the reverse. Two coordinated changes: * `gitnexus/src/core/ingestion/scope-extractor.ts` — `pass1BuildScopes` now consults a new `canParentScope` helper instead of `rangeStrictlyContains` directly. Sort tie-breaker added so a same- range Module always sorts before a non-Module candidate, ensuring the Module lands on the parent-stack first regardless of tree-sitter capture iteration order. * `gitnexus-shared/src/scope-resolution/scope-tree.ts` — `buildScopeTree`'s `parent-must-contain-child` check now uses the same `canParentScope` carve-out so the validator agrees with the extractor on what a well-formed parent edge looks like. Error message updated to spell out the new contract. `rangeStrictlyContains` keeps its strict semantics in both files — position-index lookups, hook-side range comparisons, and other call sites are unchanged. * `gitnexus/test/fixtures/lang-resolution/csharp-namespace-as-root-no-trailing-newline/` — minimal regression fixture mirroring the PersistentWindows shape: both `Models/User.cs` and `App/Program.cs` end exactly on the closing `}` of their namespace with no trailing newline. The trigger is shape- driven, not size-driven, so the fixture stays small (~250 bytes total). * New `csharp.test.ts` describe block: scope extraction completes for both files, and the cross-file `IMPORTS` edge resolves through the scope-resolution path with `reason: 'csharp-scope: using'`. * `scope-tree.test.ts`: replaced the prior "rejects child ranges identical to the parent" case with three new ones — non-Module parent still rejected at equal range; Module-as-parent of a same-range non- Module accepted (the #1086 carve-out); Module-as-parent of another Module still rejected (the asymmetry guard). * `npx vitest run test/unit/scope-resolution test/integration/resolvers` → 2514 passed / 77 skipped / 0 failed (52 test files). * `npx tsc --noEmit` clean in both `gitnexus/` and `gitnexus-shared/`. * End-to-end on PersistentWindows (after rebuilding the Docker image with this branch): 3 prior `scope extraction failed for *.Designer.cs` warnings → 0. Pre-fix index numbers will be re-checked here once the branch is built and indexed; the existing post-#1082 baseline is 1113 nodes / 2987 edges / 39 clusters / 97 flows. `canParentScope` is language-agnostic. Other languages whose query emits `(compilation_unit) @scope.module` plus a single same-range top-level scope can naturally hit the same byte shape on minimal files; this fix applies to all of them uniformly. Refs: #1086 (issue with full root-cause analysis + 4-case empirical repro through `extractParsedFile`). * refactor(scope-resolution): export canParentScope from gitnexus-shared Addresses #1087 review (medium): the helper was previously duplicated byte-for-byte in `scope-extractor.ts` and `scope-tree.ts`. Per DoD "single source of truth in shared", the contract piece belongs in gitnexus-shared (Ring 2 SHARED #912) and the consuming layer should import it. Eliminates the silent-drift surface where a future edit to one copy would produce extractor/validator disagreement on what a well-formed parent edge looks like. Changes: - gitnexus-shared/src/scope-resolution/scope-tree.ts: add `export` to `canParentScope`. - gitnexus-shared/src/index.ts: re-export `canParentScope`. - gitnexus/src/core/ingestion/scope-extractor.ts: remove the local `canParentScope` definition (and its now-unused local copy of `rangeStrictlyContains`), import from `gitnexus-shared`. The local `rangesEqual` stays — it's still used in capture-anchor logic at two unrelated sites. Validation (per DoD §4.4 — both CLI and web consumers verified): - npx tsc --noEmit clean in gitnexus/ and gitnexus-shared/ - cd gitnexus-web && npx tsc -b --noEmit clean - gitnexus-shared `npm run build` clean - Targeted: vitest run test/unit/scope-resolution test/integration/resolvers → 2522 passed / 0 failed / 77 skipped (54 files) - Full suite: vitest run → 7238 passed / 1 failed / 97 skipped. The single failure is `test/unit/ignore-service.test.ts > warns on EACCES but does not throw`, which cannot run when uid=0 (root bypasses POSIX permission checks). Pre-existing on this branch before the refactor; unrelated to scope-resolution. |
||
|
|
8fbbb35718
|
test(ignore-service): skip EACCES test under uid=0 (root bypasses chmod) (#1108)
The `loadIgnoreRules — error handling > warns on EACCES but does not
throw` test relies on `chmod 000` denying read access to a temporary
.gitignore file. On Linux, root bypasses POSIX read-permission checks,
so chmod 000 does NOT trigger EACCES under uid=0 — fs.readFile reads
the file anyway and loadIgnoreRules returns parsed rules instead of
the `null` the test expects.
Symptom under root: assertion fails with `Ignore { _rules: [...] }
to be null`, surfaced as a single test failure in any privileged
test environment (rootful Docker container, CI runners configured to
run tests as root, etc.).
Fix: extend the existing `skipIf(process.platform === 'win32')` guard
with `process.getuid?.() === 0`. The non-root code path still
exercises the real EACCES branch — root just can't reproduce the
failure mode the test asserts on, so skipping there is the correct
posture (matches the win32 skip's reasoning: the OS-level mechanism
the test depends on isn't available there).
Optional chaining (`getuid?.()`) keeps Windows compatibility — Node
on Windows doesn't expose `process.getuid` at all.
|
||
|
|
5c434ff313
|
fix(search): create FTS indexes during analyze (#1107)
Keep query-time LadybugDB access read-only by materializing BM25 indexes in the writable analyze phase. |
||
|
|
7c3fa5853f
|
fix(ingestion): classify Python class methods as Method (#1102)
* fix(ingestion): classify Python class methods as Method * fix(test): align Python large-buffer assertion with Method labels --------- Co-authored-by: gergo <gergo@Galahad.localdomain> |
||
|
|
09d78cadec
|
fix(ingestion): skip empty scope extraction (#1100) | ||
|
|
9e62f7c121
|
fix(ci): allow expected legacy parity failures (#1099)
Made-with: Cursor |
||
|
|
acef549791
|
test(csharp): companion fixture for #1066 frozen-bucket regression (#1085)
The csharp-large-cache-miss-resolution fixture added in #1082 reproduces the freeze contract failure via tree-sitter cache-miss reparse on >32 KB files. This adds a complementary trigger for the same root cause that does not depend on file size: a small-file pair where the importer locally declares a class with the same simple name as a sibling reached through `using`. Pre-#1082 path: scope-extractor pre-populates (and freezes) `User` in the importer's Module bindings, then populateCsharpNamespaceSiblings' namespace-import loop calls push() on the frozen array and throws "Cannot add property N, object is not extensible", aborting the whole scopeResolution phase. Post-#1082 the augmentation channel keeps both bindings visible; the local `Collision.App.User` shadows the namespace-imported one per origin precedence, so `Program.Run -> new User()` resolves to the local class. Three assertions: - scopeResolution completes (no throw on the colliding bucket). - both `User` declarations are detected across the two namespaces. - `Program.Run -> User` constructor edge points at App/Program.cs (not Models/User.cs), verifying origin:local shadows origin:namespace. Verified: full csharp.test.ts suite green (207/207). tsc --noEmit clean. Refs: #1066, #1082, #1083 (closed as superseded). |
||
|
|
98ee665889
|
fix(ingestion): two-channel binding lifecycle (closes #1066) + scope-resolution I8 hardening (#1082)
Some checks are pending
CI / quality (push) Waiting to run
CI / tests (push) Waiting to run
CI / e2e (push) Waiting to run
CI / scope-parity (push) Waiting to run
CI / Save PR Metadata (push) Blocked by required conditions
CI / CI Gate (push) Blocked by required conditions
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
* fix(csharp): adaptive tree-sitter buffer + frozen-bucket clone for cross-namespace siblings (#1066) Two coupled regressions surfaced when analyzing real-world C# repos with large source files (issue #1066): 1. Tree-sitter `parser.parse()` is hard-coded to a 32 KB buffer by default. Any file exceeding that threshold throws `Invalid argument` on the worker re-parse path of `populateCsharpNamespaceSiblings` (and the analogous Python / TypeScript captures fallbacks). 2. After the buffer fix unblocks the AST walk, the hook tries to `push()` onto the inner `BindingRef[]` array fetched from `indexes.bindings` — but `materializeBindings` froze that array via `Object.freeze(refs.slice())`. Result: `Cannot add property N, object is not extensible`. Fixes: - `csharp/captures.ts`, `python/captures.ts`, `typescript/captures.ts`: pass `bufferSize: getTreeSitterBufferSize(sourceText.length)` to `parser.parse()` on the cache-miss path so multi-MB files parse. - `csharp/namespace-siblings.ts`: introduce `cloneBindingBucket` to copy the frozen array before mutating, then `set()` the new array back. This is a working but architecturally compromised workaround (#1050 follow-up will replace it with an explicit augmentation channel — see docs/plans/2026-04-26-001 plan). Tests: - New `csharp-large-cache-miss-resolution` fixture (Models/Services/ Other layout, ~77 KB padded UserService.cs) drives the buffer-size failure end-to-end through worker mode. - `csharp.test.ts`: 4 new regression assertions covering both the parse-time buffer-size failure and the freeze workaround. - Per-language captures unit tests gain "large cache-miss file uses adaptive buffer" coverage (TS, Python, C#). - `csharp-hooks.test.ts`: in-memory freeze regression test that reproduces the `Cannot add property` crash without invoking the C# parser at all. Made-with: Cursor * refactor(scope-resolution): add bindingAugmentations channel to indexes Step 1 of the binding-augmentation-channel refactor (issue #1066 follow-up). Pure shape change — no consumers yet. Adds a new `readonly bindingAugmentations` field to `ScopeResolutionIndexes` initialized as an empty `Map` by `finalizeScopeModel`. The new channel is the dedicated post-finalize write target for hooks like `populateCsharpNamespaceSiblings`, so `indexes.bindings` can stay frozen and finalize-owned. Behavior unchanged: nothing reads or writes the new field yet. tsc and the full unit suite remain green. Plan: docs/plans/2026-04-26-001-binding-augmentation-channel.md (local only — `docs/plans/` is gitignored). Made-with: Cursor * feat(scope-resolution): add lookupBindingsAt dual-source helper Step 2 of the binding-augmentation-channel refactor. Introduces a single primitive every walker uses to read both the finalize-owned `indexes.bindings` channel and the post-finalize `indexes.bindingAugmentations` channel. Contract: - Finalized refs come first (preserves existing precedence). - Augmented refs append, deduped by `def.nodeId`. - Empty input on both channels returns a shared frozen empty array. - Single-channel hits return the bucket by reference (no allocation). No consumers are wired yet — Step 3 routes the existing walker primitives through this helper. Augmentations remain empty for every language; behavior of the full suite is unchanged. 8 unit tests pin precedence, dedup, identity for single-channel hits, and the shared-empty-frozen-array sentinel. Made-with: Cursor * refactor(scope-resolution): route binding lookups through lookupBindingsAt Step 3 of the binding-augmentation-channel refactor. Every direct `indexes.bindings.get(...)` consumer in the post-finalize phase is now routed through `lookupBindingsAt` (per-name) or `namesAtScope` + `lookupBindingsAt` (bulk iteration). Routed sites: - `findClassBindingInScope` (walkers.ts) — class-receiver lookups. - `findCallableBindingInScope` (walkers.ts) — free-call lookups. - `findExportedDefByName` (walkers.ts) — module-scope-fallback callable lookups. - `propagateImportedReturnTypes` (passes/imported-return-types.ts) — bulk iteration over an importer's binding entries; switched to `namesAtScope` + per-name `lookupBindingsAt` so post-finalize augmentations are visible to import-derived typeBinding mirrors. Behavior unchanged: augmentations are empty across the suite (Step 4 populates them for C# `populateNamespaceSiblings`). 587 scope-resolution unit tests + 50 integration resolver suites green (4 pre-existing Swift method-implements failures unrelated to this work). Adds `namesAtScope` companion helper for the bulk-iteration callers. Made-with: Cursor * refactor(csharp): write namespace siblings to bindingAugmentations channel Step 4 of the binding-augmentation-channel refactor. The C# `populateNamespaceSiblings` hook is the only consumer that needed to inject cross-file bindings post-finalize, and prior to this change it cloned the (frozen) finalized `BindingRef[]` arrays through a `cloneBindingBucket` helper, then `set()`-back the new array — a workaround for the `Object.freeze` applied by `finalize-algorithm.ts` (issue #1066 root cause). Architecturally that violated `ScopeResolver` Invariant I8 (which permits post-finalize modifications but not in-place mutation of finalized buckets). It also forced read-side consumers to be aware of the workaround. This change: * Switches the three C# write sites to append into `indexes.bindingAugmentations` via `getAugmentationBucket`. The augmentation channel was added in Step 1 and is mutable by contract: inner `BindingRef[]` arrays here are NEVER frozen. * Deletes `cloneBindingBucket` and `getMutableScopeBindings` (workaround helpers no longer needed). * `lookupBindingsAt` (Step 2) merges the two channels transparently for every walker (Step 3), so behavior is unchanged for callers. * Updates the unit test to assert against both channels: finalized bucket stays frozen and untouched, cross-file siblings show up in augmentations only. Renamed the test accordingly. Validation: * `npx tsc --noEmit` clean. * csharp hooks unit + walkers-augmentations unit + csharp integration resolver suite all green (236/236). * Wider `test/unit/scope-resolution test/integration/resolvers` suite: 2507 pass, only 4 pre-existing Swift METHOD_IMPLEMENTS failures remain (unrelated to this work, present on baseline). Refs: issue #1066, ADR-pending binding-augmentation-channel. Made-with: Cursor * feat(scope-resolution): tighten I8 + add validateBindingsImmutability dev guard Step 5 of the binding-augmentation-channel refactor. Captures the new two-channel binding lifecycle in the contract docs and adds a dev-mode runtime validator so a future hook cannot silently drift back into mutating `indexes.bindings`. Contract changes: * `contract/scope-resolver.ts` — rewrote Invariant I8 to describe the two channels (`indexes.bindings` is finalize-output and immutable post-finalize; `indexes.bindingAugmentations` is the append-only post-finalize channel populated by hooks like `populateNamespaceSiblings`). Documented `lookupBindingsAt` as the read-side merger and pointed at the new validator as the enforcement mechanism. * `gitnexus-shared/src/scope-resolution/types.ts` — extended the module-header lifecycle contract to call out `bindingAugmentations` alongside `ReferenceIndex` as the two structures populated after the freeze. Validator: * New `pipeline/validate-bindings-immutability.ts` mirrors the shape of `validateOwnershipParity` (#909): runs only when `NODE_ENV !== 'production' && VALIDATE_SEMANTIC_MODEL !== '0'`, emits via `onWarn`, never throws. Asserts (a) every inner `BindingRef[]` in `indexes.bindings` is `Object.isFrozen`, and (b) every inner array in `indexes.bindingAugmentations` is NOT frozen. * Wired into `pipeline/run.ts` after both `populateNamespaceSiblings` and `propagateImportedReturnTypes`, before `resolveReferenceSites`. One sweep covers the full post-finalize surface. Tests: * `validate-bindings-immutability.test.ts` — 6 cases pinning happy path, both drift directions, multi-violation accumulation, and both production no-op gates. All scope-resolution + csharp resolver tests green (242/242 in the focused run; matches the wider Step 4 baseline). Made-with: Cursor * fix(ingestion): size tree-sitter buffers from UTF-8 bytes Tree-sitter buffer sizing is byte-based, so computing adaptive buffers from JavaScript string length under-sized UTF-8-heavy files. Make getTreeSitterBufferSize accept source text directly and compute Buffer.byteLength internally, then update all parse call sites and max-buffer skip checks to use byte length. Add multibyte cache-miss and cap regressions for C#, Python, TypeScript, and the C# namespace-sibling fallback parse path. Made-with: Cursor * test(scope-resolution): pin augmentation read paths Add focused unit coverage for augmented-only binding reads across the routed walker helpers and imported-return-type propagation path. Clarify I8 wording around lexical Scope.bindings versus post-finalize index channels, and document the intentional local-only behavior of findExportedDef. Also switch the immutability validator tests to Vitest env stubs, document one intentional validator blind spot, and split C# namespace-sibling tests so UTF-8 parsing and augmentation-channel behavior are asserted independently. Made-with: Cursor * test(scope-resolution): avoid slow parser stress fixtures Replace high-cardinality large-file capture fixtures with large padding plus a trailing declaration. This still proves adaptive tree-sitter buffers parse beyond large ASCII and UTF-8-heavy input, without making query matching process thousands of declarations and risking timeouts. Made-with: Cursor * test(scope-resolution): add python and typescript cache-miss resolver regressions Add worker-mode resolver integration coverage mirroring the C# #1066 scenario for Python and TypeScript. Each test builds a temp fixture with large ASCII and UTF-8-heavy source padding, then asserts trailing declarations and call edges still resolve after scope-resolution cache-miss reparsing. Made-with: Cursor * refactor(scope-resolution): gate I8 validator and fast-path namesAtScope Addresses SPARC reviewer feedback on the binding-augmentation channel: - Validator gate is now opt-in outside development. Extract isSemanticModelValidatorEnabled() in utils/env.ts as the single predicate; both validateBindingsImmutability and phase.ts's warn handler share it. Default CLI runs no longer pay the O(binding-buckets) scan, and explicit VALIDATE_SEMANTIC_MODEL=1 now emits warnings even when NODE_ENV is unset. - namesAtScope returns Iterable<string> and zero-allocates when at most one channel is populated (returns Map.keys() directly), only materializing a Set when both channels carry names. The caller-side branching and EMPTY_NAMES escape hatch in propagateImportedReturnTypes are gone -- both helpers handle the empty-augmentation case internally. - C# namespace-siblings header/JSDoc, model JSDoc, I8 contract prose, and the #1066 integration-test header rewritten to say post-finalize fanout appends only to bindingAugmentations; finalized refs come first and win duplicate def.nodeId metadata; local lexical Scope.bindings remains the first-tier shadowing channel. Validator unit-test setup deduplicated via beforeEach and extended with default-CLI no-op + explicit-opt-in cases. Made-with: Cursor |
||
|
|
ab077b4c29
|
feat(ingestion): TypeScript registry-primary scope resolution (Ring 3) (#1050)
* feat(ingestion): TypeScript registry-primary scope resolution (Ring 3) - Add TypeScript ScopeResolver stack (query/captures/interpret, import decomposition, hooks, arity, merge, receiver binding) and register in SCOPE_RESOLVERS. - Harden shared compound receiver and receiver-bound CALLS pass for map for-of tuple bindings, dotted typeRef shapes, and callable-alias fallbacks. - Flip TypeScript into MIGRATED_LANGUAGES; refresh AGENTS.md and type-resolution-system.md. - Shared finalize-algorithm updates for cross-file scope parity. - Tests: TS scope-resolution unit suite; legacy call-processor suite forces REGISTRY_PRIMARY_TYPESCRIPT=0; registry-primary flag test opts out TS in override scenario. Made-with: Cursor * fix(ingestion): SCC-ordered cross-file return-type propagation + multi-hop re-export resolution Fix CI failures on PR #1050 (TypeScript registry-primary migration) by making `propagateImportedReturnTypes` deterministic via reverse- topological SCC ordering and updating the multi-hop re-export contract to match `followReexportChain` behavior. Why: the legacy pass mirrored an intermediate ref instead of the terminal type when an importer was processed before its source module had its own typeBindings chain-followed (4-file alias chain regression in `ts-simple` fixture: `models.User -> service.user -> app.user` collapsed to `getUser` instead of `User`). Reverse-topological walk of `indexes.sccs` (leaves first) lets every importer see the source's already-followed terminal type in a single pass. Changes: - `imported-return-types.ts`: rewrite to walk SCCs leaves-first, chain- follow the source module's typeBindings BEFORE mirroring, and chain- follow the importer's typeBindings AFTER mirroring. Cyclic SCCs reach a partial fixpoint (no convergence guarantee, ts-circular only asserts no-throw). - `finalize-algorithm.ts`: docstring update on `FinalizeFile.localDefs` to reflect that `followReexportChain` resolves multi-hop re-exports through barrels even when intermediates do not surface the name - surfacing is now a static optimization, not a correctness requirement. - `contract/scope-resolver.ts` Invariant I3: explicitly document the SCC ordering requirement. - `pipeline/run.ts`: split PROF timer into `finalize` and `propagate` so the pass's cost is observable independently. - `ARCHITECTURE.md` Performance notes: describe SCC-ordered propagation. - `imported-return-types.ts`: expand chain-depth comment (2x effective depth from pre/post follow), add multi-ref break rationale, add `ts-simple` motivating-fixture pointer. Tests: - `finalize-algorithm.test.ts`: add 4 cases (3-hop chain, cyclic re-export visited-set guard, wildcard re-export fall-through, multi-source first-match-wins); fix misleading shared nodeId in the thick variant; rename and update the multi-hop test for the new contract (transitiveVia assertion on the thin variant). - `imported-return-types.test.ts` (NEW): unit tests for the SCC pass pinning topological collapse, local-annotation guard, missing-source skip, and cyclic-SCC no-throw. - `cross-file-binding.test.ts` + `ts-deep-alias-chain` fixture (NEW): 5-file integration regression guard for SCC-ordered propagation through 4 module boundaries. Validation: 865 scope-resolution + cross-file tests pass on Windows; typecheck clean across both packages; only pre-existing Swift overload failures remain (verified on PR base commit, environmental). Made-with: Cursor * fix(ingestion): address PR #1050 review findings — side-effect imports, resolve-cache perf, adapter signature Three independent fixes surfaced by the production-readiness review of the TypeScript registry-primary scope-resolution migration (RFC #909 Ring 3). All three pass under both REGISTRY_PRIMARY_TYPESCRIPT=0 and =1. 1. Side-effect imports were silently dropped (correctness regression). The legacy DAG emitted IMPORTS edges for `import './polyfill'` because its tree-sitter query matches `(import_statement source: (string))` regardless of clause. The new registry-primary path returned `[]` from `splitImportStatement()` for clause-less imports, so no ParsedImport / ImportEdge was ever produced — silent file-level edge loss. Add a generic 'side-effect' variant to `ParsedImport` and `ImportEdge['kind']` in `gitnexus-shared`; finalize resolves the target file and pre-finalizes the edge (no `targetDefId`, no `BindingRef`) so the SCC fixpoint loop skips it. The TypeScript provider now emits + interprets the new kind end-to-end. The variant is intentionally generic so other languages (Rust `use foo as _`, Python module-init) can adopt it. 2. Per-import re-derivation in `resolveImportTarget` (perf regression). The TS adapter built `new Set(allFilePaths)` on every call and let `resolveTsImportTarget` re-derive `allFileList` / `normalizedFileList` and discard the `resolveCache`. For a workspace with N files and M imports that's O(N × M) work per pass. Wrap the adapter in a closure that memoizes all five derived values keyed on the orchestrator's `ReadonlySet` identity; reset only when the set reference changes (start of new pass). New cost: O(N + M). 3. Misleading fake `ParsedImport` in the adapter (architecture). The adapter constructed `{ kind: 'named', localName: '_', importedName: '_', targetRaw }` to call `resolveTsImportTarget`, even though only `targetRaw` and the structural-typed context are read. Extract `resolveTsTarget(targetRaw, ctx)` so the adapter has an honest signature; `resolveTsImportTarget` still works for other callers. Also extract `narrowTsContext` for the type narrowing. Tests: - New 4-file fixture `typescript-side-effect-imports` with two side-effect imports + one named import. - New "TypeScript side-effect imports" describe in `test/integration/resolvers/typescript.test.ts` (parity-gated by `ci-scope-parity.yml` — runs under both flag states). - Updated 2 unit tests to expect 1 side-effect ParsedImport and 4 `@import.statement` matches (was 0 / 3). - 785 / 785 TS scope-resolution tests pass under both REGISTRY_PRIMARY_TYPESCRIPT=0 and =1. Made-with: Cursor * fix(scope): address Codex adversarial review findings on PR #1050 Four findings from the Codex adversarial review broke registry-primary TypeScript resolution for common patterns. All four now have unit and integration regression coverage that pass under both `REGISTRY_PRIMARY_TYPESCRIPT=0` (legacy DAG) and the default registry-primary path. [high] tsconfig path aliases dropped: Threaded `tsconfigPaths` through ScopeResolver via a new opaque `resolutionConfig` parameter and a `loadResolutionConfig(repoPath)` hook. The orchestrator (`scopeResolutionPhase` + `runScopeResolution`) loads it once per workspace pass and forwards into every `resolveImportTarget` call. TypeScript resolver now resolves `@/services/user` style imports through the standard resolver's alias branch. [high] TSX parsed with the wrong grammar: `emitTsScopeCaptures` now picks the parser/query by `filePath` (`.tsx` -> TSX grammar) and validates cached trees against the expected grammar via the new exported `tsCachedTreeMatchesGrammar` helper. Stale TS-grammar trees for `.tsx` files no longer leak through the scope query. [medium] Literal dynamic imports never linked: Added `kind: 'dynamic-resolved'` to `ParsedImport` and `ImportEdge`. The decomposer emits a synthetic `@import.literal` capture for string-literal dynamic imports; the interpreter maps that to `dynamic-resolved`; finalize pre-finalizes it as a file-level terminal (same shape as `side-effect`). `import('./feature')` now produces a real IMPORTS edge under the registry-primary path. Legacy DAG keeps its existing behavior — the new integration assertion is gated behind the flag. [medium] Namespace re-exports invisible from barrels: The decomposer now emits TWO captures for `export * as ns from './m'` — the existing `reexport-namespace` import draft AND a synthetic `@declaration.namespace` capture (via `buildNamespaceDeclarationMatch`). The latter creates a Namespace `SymbolDefinition` in the barrel's `localDefs`, so downstream `import { ns } from './barrel'` resolves through `findExportByName`. Regression fixtures under `gitnexus/test/fixtures/lang-resolution/`: - typescript-tsconfig-aliases (`@/` alias) - typescript-tsx-jsx (Button.tsx + App.tsx with JSX) - typescript-dynamic-import (`await import('./feature')`) - typescript-reexport-namespace (`export * as Models from './base'`) Validation: - gitnexus-shared builds clean - gitnexus typecheck clean - 385/385 TS scope-resolution tests pass under both `REGISTRY_PRIMARY_TYPESCRIPT=0` and default Made-with: Cursor * perf(scope): O(1) defById lookup + bounded re-export depth (PR #1050 round 3) Addresses the round-3 PR #1050 reviews (Claude adversarial + xkonjin): both flagged the existing O(N²) `findDefById` linear scan in `materializeBindings` and the unbounded recursion in `followReexportChain` as production-readiness blockers for TypeScript monorepos. Both fixes land alongside their regression tests under both `REGISTRY_PRIMARY_TYPESCRIPT=0` and the default registry-primary path. [high] materializeBindings O(N_files × N_defs × N_edges) → O(N_defs + N_edges): Build a `nodeId → SymbolDefinition` index map once at the top of `materializeBindings` (one O(N_defs) pass), then replace the per-edge `findDefById(files, edge.targetDefId)` linear scan with an O(1) `defById.get(edge.targetDefId)` lookup. Also drop the now-unused `findDefById` helper. At realistic TypeScript monorepo scale (~5k files × ~50 defs/file × ~100k linked import edges) this is the difference between ~25 s and a few ms inside finalize. Regression test in `finalize-algorithm.test.ts` builds 200 leaf files + 1 consumer importing one symbol from each, asserts every binding materializes correctly. [medium] followReexportChain unbounded recursion: The existing `visited` set caps depth at `O(N_files)` but allows recursion proportional to barrel-chain depth, mismatching the explicit "Iterative DFS to avoid stack overflow" policy in `tarjanSccs`. Added a `MAX_REEXPORT_DEPTH = 100` constant and a `depth` parameter to `followReexportChain` (defaults to 0); each recursive call passes `depth + 1` and the function returns `null` when the cap is exceeded. 100 is comfortably above any realistic hand-authored barrel chain (typical depth 1-5; auto-generated barrels rarely exceed 20) while staying well below JS engine call stack limits. Regression test wires a 200-link reexport chain and verifies the crawl terminates cleanly with `linkStatus: 'unresolved'` (no terminal def reachable within the budget). [low] synthesizeInstanceofNarrowings bare-identifier-only limitation: xkonjin's review #4 noted that the LHS narrowing only handles bare identifiers (`if (x instanceof Foo)`), not member expressions (`if (user.address instanceof Address)`). Added a JSDoc note explaining the constraint and pointing readers at field-type resolution as the workaround for member-chain receivers. Validation: - gitnexus-shared builds clean - gitnexus typecheck clean - 413/413 tests pass under both flag states for finalize-algorithm + TS unit + TS integration suites - 972/972 tests pass across full scope-resolution + Python + C# integration smoke (no cross-language regression) Made-with: Cursor * refactor(finalize): replace recursive followReexportChain with SCC-condensed iterative closure The legacy `followReexportChain` walked re-export drafts via mutual recursion guarded by a per-call visited set + a `MAX_REEXPORT_DEPTH` ceiling. Recursion is fragile (call-stack ceiling, no bound on depth that's actually meaningful), so this replaces it with a structurally better algorithm: a precomputed per-file re-export closure built by running Tarjan SCC over the re-export sub-graph and propagating names in reverse-topological order with a bounded intra-SCC fixpoint. Algorithm (`buildReexportClosures` in finalize-algorithm.ts): 1. Sub-graph: build the directed graph of `reexport` + `wildcard` drafts only (regular/namespace/dynamic imports do not contribute). 2. SCC condensation: run the same iterative `tarjanSccs` already used for the file-level import graph; output is in reverse-topo order so out-of-SCC neighbors are always already-finalized. 3. Per-SCC propagation: - Acyclic singleton: one pass populates from neighbors' closures. - Cyclic SCC: bounded fixpoint capped at |SCC|+1 iterations. With first-wins precedence the closure map is monotone, so each name needs at most |SCC| hops to traverse the cycle. Precedence (preserved from the recursive crawl): - Named re-exports take precedence over wildcards. - Within each kind, declaration order wins. Lookup at finalize time becomes O(1) (`lookupReexportedName`), down from O(chain_depth × drafts) per consult and recursive at that. Properties vs the legacy implementation: - Stack-safe by construction; no `MAX_REEXPORT_DEPTH` guard needed. - 1000-hop barrel chains now resolve in full (legacy capped at 100 and surfaced anything deeper as `unresolved`). - Cycles handled structurally via SCC, not via per-call visited set. - Same observable semantics: every existing test passes unchanged. Tests: - Replace the obsolete `MAX_REEXPORT_DEPTH (200-hop chain stops cleanly without stack overflow)` test (which asserted the OLD bug — that deep chains failed to resolve) with a positive 1000-hop test that asserts full resolution + accurate `transitiveVia`. Proves both the recursion is gone AND the closure correctly inherits the leaf def across all hops. - Update commentary on adjacent re-export tests to reference the closure mechanism. - Update `FinalizeFile.localDefs` JSDoc + import-decomposer.ts inline doc to point at `buildReexportClosures` instead of the removed function name. Validation: - gitnexus-shared builds cleanly. - gitnexus typechecks cleanly. - 28/28 finalize-algorithm.test.ts tests pass (incl. new 1000-hop). - 801/801 TypeScript scope-resolution tests pass under default (registry-primary) AND `REGISTRY_PRIMARY_TYPESCRIPT=0` (legacy DAG). - 404/404 Python + C# integration tests pass — no regression in cross-language consumers of the shared `finalize`. Made-with: Cursor * fix(scope): remove non-null assertions from scope resolution Made-with: Cursor * fix(scope): address TypeScript review follow-ups Made-with: Cursor * fix(scope): address TypeScript import review follow-ups Add regression coverage for non-binding import edges and circular TypeScript bindings so PR #1050 review concerns stay visible without changing runtime semantics. Made-with: Cursor |
||
|
|
13abf192e0
|
fix: use os.homedir() instead of process.env.HOME for HF cache dir (#1078)
On Windows, HOME env is often unset, causing cache to be written to './undefined/'. Using os.homedir() ensures cross-platform compatibility while preserving HF_HOME priority. Fixes #1068 |