mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-06 08:16:02 +00:00
648 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cb3e653aee
|
Merge branch 'main' into copilot/fix-gitnexus-wiki-timeout-error | ||
|
|
dfbe68ad24
|
fix(lbug): issue #1647, detect WAL corruption in schema init and surface recovery (#1650) | ||
|
|
b83c9ccbb9
|
fix: address latest wiki timeout review follow-ups
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/d5d9ae3e-75fa-48ab-8709-3ade04f4827c |
||
|
|
ce17a63961
|
fix: match timeout-like wiki errors robustly
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/557eb110-9ffd-4771-a7a8-782b1934b3d2 |
||
|
|
523f3a06a4
|
fix: harden wiki timeout error detection
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/557eb110-9ffd-4771-a7a8-782b1934b3d2 |
||
|
|
e4fbfe7559
|
test: cover wiki timeout ms messaging
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/557eb110-9ffd-4771-a7a8-782b1934b3d2 |
||
|
|
fae3dbade5
|
fix: surface engaged wiki timeout errors
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/557eb110-9ffd-4771-a7a8-782b1934b3d2 |
||
|
|
d74c5dd25b
|
fix: reject overflowing wiki timeout values
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2f7def72-828c-419c-a5db-1bf1e2f10203 |
||
|
|
e85de16bd6
|
test: reject fractional wiki timeout values
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2f7def72-828c-419c-a5db-1bf1e2f10203 |
||
|
|
24ad0048a9
|
test: add wiki timeout validation edge cases
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2f7def72-828c-419c-a5db-1bf1e2f10203 |
||
|
|
66522c48c5
|
test: cover wiki timeout option mapping
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2f7def72-828c-419c-a5db-1bf1e2f10203 |
||
|
|
f8981ed4ff
|
fix: validate invalid wiki timeout values
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2f7def72-828c-419c-a5db-1bf1e2f10203 |
||
|
|
d05602112e
|
fix: remove default wiki llm timeout ceiling
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/af129945-1e0e-4d94-8676-877470c4574c |
||
|
|
2376912ca7
|
feat(ingestion): Add C++ parameter type class sidecar (#1642)
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
a4dfebd073
|
feat(cpp): sfinae filter (#1623)
* feat(cpp): SFINAE-aware overload filter — drops candidates whose enable_if_t / requires constraints fail (#1579) * fix(cpp): SFINAE follow-ups for is_integral_v/is_arithmetic_v bool and char support, an unqualified F1 test fixture, and parameter-lookup gap documentation (#1579) -> claude feedback * revert: reverting all changes to .md files |
||
|
|
42d4fcaf6f
|
chore: release v1.6.5 (#1645) | ||
|
|
a26ac55fb0
|
fix(lbug): Recover gitnexus analyze from orphan LadybugDB sidecars when main DB file is missing (#1622)
* Initial plan * fix: recover from orphan lbug sidecars on init Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/6e8ea6e8-f9ab-46ff-9c1b-4d2c73a6452c * test: strengthen orphan sidecar recovery coverage Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/6e8ea6e8-f9ab-46ff-9c1b-4d2c73a6452c * fix(lbug): only clean orphan sidecars when DB is missing Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a34217b5-0e98-4949-bae1-2a50933f291e * test(lbug): cover no-cleanup path when db file exists Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a34217b5-0e98-4949-bae1-2a50933f291e * test(lbug): use errno-shaped ENOENT mocks for sidecar recovery Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a34217b5-0e98-4949-bae1-2a50933f291e * test(lbug): cover partial sidecar and unlink-failure recovery cases Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a34217b5-0e98-4949-bae1-2a50933f291e * refactor(lbug): tighten ENOENT detection and test naming Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a34217b5-0e98-4949-bae1-2a50933f291e * test(lbug): normalize errno mock helpers across sidecar tests Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a34217b5-0e98-4949-bae1-2a50933f291e * docs(lbug): annotate orphan `.wal.checkpoint` cleanup provenance Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/7edf5156-43e0-412d-87a4-bf4b2934deac * test(lbug): clarify unlink-failure path test intent Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/7edf5156-43e0-412d-87a4-bf4b2934deac * fix(lbug): handle orphan-sidecar cleanup error paths explicitly Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/93294b2c-57f6-459c-8eb2-86e3b8920fb0 * refactor(lbug): extract errno and error-summary helpers Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/93294b2c-57f6-459c-8eb2-86e3b8920fb0 * test(lbug): expand non-ENOENT lstat coverage and remove magic number Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/93294b2c-57f6-459c-8eb2-86e3b8920fb0 * test(lbug): add native integration test for orphan sidecar recovery Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2dd28264-4604-430a-a249-af52afd29245 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(lbug): annotate best-effort catch in integration test cleanup Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2dd28264-4604-430a-a249-af52afd29245 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(lbug): add cross-process init lock for orphan sidecar cleanup with integration tests Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/e4cbcfec-a252-449d-8d65-2f3570a253f8 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * refactor(lbug): use INIT_LOCK_STALE_MS in stale lock detection and address review feedback Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/e4cbcfec-a252-449d-8d65-2f3570a253f8 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * style(lbug): fix Prettier line-length violation in acquireInitLock fs.open call Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a140b567-0e9b-4ec9-a158-9fe6b8685ec2 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(lbug): ensure parent directory exists before creating init lock file acquireInitLock tried to create `${dbPath}.init.lock` using O_CREAT | O_EXCL, but on a fresh repo the parent directory (`.gitnexus/`) doesn't exist yet — the mkdir call was inside the locked section. This caused ENOENT failures on all platforms (Windows, macOS, Ubuntu) during `gitnexus analyze`. Move mkdir to before the lock file creation attempt. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/6883dc3c-36eb-4907-bcd8-61d23e2c641a Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(lbug): verify acquireInitLock succeeds when parent directory does not exist Adds an integration test proving the fix from the previous commit: acquireInitLock now creates the parent directory before attempting to create the lock file, preventing ENOENT on fresh repos. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/6883dc3c-36eb-4907-bcd8-61d23e2c641a Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
467c14caa2
|
feat(cpp): standard-conversion-sequence ranking for overload resolution (#1606)
* feat(cpp): add standard-conversion-sequence ranking to overload resolution (#1578) Introduce `ConversionRankFn` abstraction and `cppConversionRank` implementation to disambiguate C++ overloaded calls by argument-to-parameter conversion cost. Exact type match (rank 0) beats standard arithmetic conversion (rank 2), which beats non-viable mismatch (Infinity). Thread the rank function through `narrowOverloadCandidates`, `pickImplicitThisOverload`, `pickOverload`, and `pickUniqueGlobalCallable` via the `ScopeResolver.conversionRankFn` contract. Add `findAllCallableBindingsInScope` scope walker for collecting all overloads at the first binding scope. Guard against false ambiguity suppression when candidates span different files (local-shadows-import preservation). * fix: address Claude review findings on conversion-rank PR Finding 1 (HIGH): add tests that exercise the conversion ranker. - p('a') with p(int)/p(double): char→int promotion (rank 1) beats char→double conversion (rank 2), forcing step 4b in narrowOverloadCandidates. Exact-type filter misses both overloads. - h(42, 2.5) with h(int,int)/h(double,double): multi-arg tied total score forces the ranker, both candidates score 2 → suppressed. Finding 2 (HIGH): unify multi-candidate suppression across all paths. - Non-ADL free-call: suppress when narrowed.length > 1 (same-file guard), mirroring ADL merged-candidate behavior. - ADL ordinary-only: same pattern. - pickOverload: return OVERLOAD_AMBIGUOUS when candidates.length > 1 after normalized-ambiguity check. - Case 0.5 (this receiver): set ambiguous=true when narrowed > 1. Finding 3+4 (MEDIUM): implement rank-1 integral promotions. - char→int and bool→int now return rank 1 (ISO C++ [conv.prom]). - Updated comment to remove misleading ISO table header; document only the post-normalization ranking that is actually implemented. - Updated ConversionRankFn JSDoc in overload-narrowing.ts. 218/218 C++ tests pass (registry-primary). Legacy: 186+32. * fix: implement pairwise dominance comparison for overload ranking Replace the summed per-slot conversion cost with ISO C++-aligned pairwise dominance comparison ([over.ics.rank]). F1 is better than F2 only when F1 is not worse for every argument and strictly better for at least one. Non-dominated candidates are returned; if multiple remain they are genuinely ambiguous. This fixes false CALLS edges for asymmetric multi-arg overloads: h('a', 2.5) against h(int,int) / h(double,double) — the old summed cost picked h(double,double) (cost 2 < 3), but ISO C++ considers the call ambiguous because h(int,int) is better at arg 0 via char promotion. The pairwise check correctly finds neither dominates. Add h('a', 2.5) test case asserting zero CALLS edges alongside the existing h(42, 2.5) symmetric-tie test. 218/218 C++ tests pass (registry-primary). Legacy: 186+32. * docs: update step 4b JSDoc to reflect pairwise dominance --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
fa06c5610b
|
fix: resolve cross-file type propagation stall on large repos (#1626)
* Initial plan * fix: add time-based deadline to cross-file type propagation to prevent stalling on large repos Adds a 2-minute wall-clock time limit (DEFAULT_CROSS_FILE_ELAPSED_MS) to runCrossFileBindingPropagation. When exceeded, the phase gracefully stops and logs a warning. Users can override via GITNEXUS_CROSS_FILE_TIMEOUT_MS env var. This prevents the analyze command from stalling for hours on very large repositories where per-file re-resolution is expensive. Fixes the reported issue where gitnexus analyze stalls at "Cross-file type propagation" for several hours on repos with 15000+ files. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b8341947-557c-4111-a3a8-991ba455ab01 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: root cause - cache tree-sitter queries across files, add live progress reporting Root cause: cross-file propagation called processCalls() with 1 file at a time, causing Parser.Query to be recompiled from the query string for every single file (O(N) compilations vs O(1) for the whole phase). Additionally, progress was only reported once at the start, making the phase appear completely frozen. Fixes: - Add optional `compiledQueryCache` parameter to `processCalls` so callers that invoke it with single-file batches can share compiled query objects across calls. The cross-file phase now compiles each language's query string exactly once and reuses it for all files of that language (e.g. 1 TypeScript compile for 595+ files). - Pre-count candidate files and emit onProgress every 25 files showing "Cross-file type propagation (N/M files)..." so the UI shows real movement instead of a frozen bar. - Keep the wall-clock deadline (GITNEXUS_CROSS_FILE_TIMEOUT_MS) as a safety net for pathological inputs. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/f5028cc8-4bc9-4309-8ffb-798fe2bd7a0a Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address code review - use SupportedLanguages key type, rename queryCache to compiledQueryCache Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/f5028cc8-4bc9-4309-8ffb-798fe2bd7a0a Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(cross-file): remove wall-clock timeout from type propagation The query compilation cache and live progress reporting address the original stall; the 2-minute deadline could truncate cross-file work on large repos. MAX_CROSS_FILE_REPROCESS (2000) remains as the only cap. * test(cross-file): verify compiledQueryCache is shared across all processCalls invocations Finding 1: O(N) query recompilation was fixed by sharing a compiledQueryCache Map across all processCalls invocations in runCrossFileBindingPropagation. This test verifies the fix is correctly wired: the same Map instance is passed as the 12th argument to every call, proving queries are compiled once per language, not once per file. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/3ab768d9-3993-4882-9d8f-17f7fcbd086e Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cross-file): verify live progress events are emitted with N/M format Finding 2: frozen progress display was fixed by emitting onProgress every 25 files with "Cross-file type propagation (N/M files)..." messages instead of calling it once at phase start. This test verifies the fix with 50 candidate files: expects onProgress called 3 times (1 initial + at 25 + at 50) with correct N/M counters. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/3ab768d9-3993-4882-9d8f-17f7fcbd086e Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(cross-file): skip registry-primary language files before readFileContents Finding 3 (from comment 4466231612): cross-file-impl was calling processCalls for every candidate file even when that file's language is registry-primary (TypeScript, C++, Python, Go, C#, PHP, C — since AGENTS.md v1.7.0). processCalls would immediately skip those files via its own isRegistryPrimary guard, but cross-file-impl still paid the full cost: readFileContents I/O, buildImportedReturnTypes, buildImportedRawReturnTypes, and Map allocation — all discarded. Fix: check isRegistryPrimary(lang) in both the totalCandidates pre-count loop and the levelCandidates builder, before any file I/O or map building. This eliminates 595+ no-op processCalls invocations on large TypeScript repos. Test: mocks isRegistryPrimary to always return true and verifies that processCalls is never invoked and result is 0. The mock also defaults to false in beforeEach so existing tests using .ts files are unaffected. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/3ab768d9-3993-4882-9d8f-17f7fcbd086e Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * refactor(test): address code review - simplify mock factory, name the arg index constant Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/3ab768d9-3993-4882-9d8f-17f7fcbd086e Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
263ca353a6
|
fix: shard parse cache persistence on large repos (#1580)
* fix: shard parse cache persistence on large repos * fix(parse-cache): validate shard keys, docs, and sharded-cache tests - Reject non-sha256-hex keys from index.json before path.join (path traversal). - saveParseCache: skip invalid keys defensively; try/catch per-shard JSON.stringify. - Clarify save comment (tmp dir + rename vs atomic). - Tests: hex keys throughout, traversal keys, multi-shard, version-mismatch+legacy, second save, legacy removal. - AGENTS.md / GUARDRAILS.md: document .gitnexus/parse-cache/ vs legacy parse-cache.json. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(autofix): apply prettier + eslint fixes via /autofix command --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
8500f18e5f
|
fix(cpp): detect same-name ambiguity across inline namespace children (#1564) (#1600)
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Release Candidate / Check if release candidate should run (push) Has been cancelled
Release Candidate / ci (push) Has been cancelled
Release Candidate / Publish release candidate to npm (push) Has been cancelled
Release Candidate / Build & Push RC Docker images (push) Has been cancelled
|
||
|
|
aed370b931
|
feat: C++ ADL V2: merge ordinary and ADL free-call candidates before overload selection (#1599)
* Initial plan * Merge C++ ADL and ordinary free-call candidate sets Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/1aea3511-3471-4ec2-9819-0fb27ac40b89 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * Address review feedback on merged ADL ambiguity suppression Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/1aea3511-3471-4ec2-9819-0fb27ac40b89 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: apply prettier to C++ ADL resolver fallback files Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9b9c1494-bc69-4db5-a89d-69eb816bab82 * docs: update ADL ambiguity comments to merged narrowing flow Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9b9c1494-bc69-4db5-a89d-69eb816bab82 * fix: suppress global fallback when merged ADL narrowing yields zero candidates Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9b9c1494-bc69-4db5-a89d-69eb816bab82 * docs: clarify free-call fallback comment for ADL merged path Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9b9c1494-bc69-4db5-a89d-69eb816bab82 * feat: ADL Gap 2 — enum-typed arguments contribute enclosing namespace ISO C++ [basic.lookup.argdep] §2: "If T is an enumeration type, its associated namespace is the namespace in which it is defined." - Add Enum to findCppClassDefBySimpleName type filter - Map Enum defs to enclosing namespace in populateCppAssociatedNamespaces - Add test fixture cpp-adl-enum-arg with color::Channel enum Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ca8987b6-365e-4034-af56-ca3f9b439902 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * feat: ADL Gap 6 — inline namespace expansion in associated set ISO C++ inline namespaces are transparent for ADL: if a namespace is in the associated set, candidates declared in its inline-namespace children are also reachable. - Expand pickCppAdlCandidates to scan inline-namespace children of associated namespaces (via isCppInlineNamespaceScope predicate) - Add test fixture cpp-adl-inline-ns-expansion: Event in outer audit, record in inline v1, other::record(int) forces arity disambiguation Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ca8987b6-365e-4034-af56-ca3f9b439902 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * feat: ADL Gap 1 — hidden friend functions visible via ADL ISO C++ [basic.lookup.argdep] §2: friend functions declared inside a class body are visible via ADL when the class is an associated class. - Exempt friend_declaration from cppLabelOverride's class-body function suppression (c-cpp.ts) so friend function defs are captured - Scan Function scopes that are direct children of associated Class scopes in pickCppAdlCandidates (adl.ts) to find hidden friends - Add test fixture cpp-adl-hidden-friend: `friend void process(Foo&)` declared inside lib::Foo, resolved via ADL from app::run() Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ca8987b6-365e-4034-af56-ca3f9b439902 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * feat: ADL Gap 3 — non-function ordinary lookup suppresses ADL ISO C++ [basic.lookup.unqual] §7: if ordinary unqualified lookup finds a name that is not a function or function template, ADL is not performed. - Add hasNonCallableBindingInScope walker in walkers.ts - In free-call-fallback, check for non-callable binding before invoking ADL; when found, bypass resolveAdlCandidates entirely - Add test fixture cpp-adl-non-function-blocks: variable `int record` shadows the function name, blocking ADL from finding audit::record Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ca8987b6-365e-4034-af56-ca3f9b439902 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: use nearest-scope semantics for ADL non-callable blocker check Finding 1: `hasNonCallableBindingInScope` walked the entire scope chain, which could incorrectly suppress ADL when an inner scope had a callable and an outer scope had a non-callable for the same name. Per ISO C++ `[basic.lookup.unqual]` §7, ADL is blocked only when ordinary lookup itself finds a non-function — if ordinary lookup stops at an inner scope where only callables exist, ADL should still fire. Replace the separate `hasNonCallableBindingInScope` + `findAllCallable BindingsInScope` calls with a combined `findCallableBindingsAndAdlBlocker` walker that stops at the first scope with ANY binding for the name and returns both `{ callables, nonCallableFound }`. One pass, one stop. Fixture: cpp-adl-inner-callable-outer-noncallable — inner scope has callable `swap(int,int)`, outer scope has `int swap = 0`. ADL fires and resolves to `data::swap(Pair&,Pair&)` via argTypes narrowing. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a2f97daf-17fd-4891-8b10-a81e44d32808 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: block-scope function declaration suppresses ADL Finding 2: ISO C++ [basic.lookup.argdep] lists three ADL blockers: 1. class member declaration (handled by pickImplicitThisOverload) 2. block-scope function declaration NOT a using-declaration (NEW) 3. non-function/non-template declaration (handled by nonCallableFound) Extend `findCallableBindingsAndAdlBlocker` to return `blockScopeDeclFound` when a callable is found at a Function or Block scope — indicating a local forward declaration that should suppress ADL per standard. `free-call-fallback.ts` now checks both `nonCallableFound` and `blockScopeDeclFound` to determine ADL suppression. Fixture: cpp-adl-block-scope-decl-blocks — `void record(int);` declared inside function body prevents ADL from discovering audit::record. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a2f97daf-17fd-4891-8b10-a81e44d32808 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * docs: update stale ADL_AMBIGUOUS comment in unqualified-ref-collision fixture Finding 3: The `ADL_AMBIGUOUS` sentinel was removed by this PR (replaced by `isOverloadAmbiguousAfterNormalization` in merged-narrowing). Update the fixture comment to reference the current mechanism. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a2f97daf-17fd-4891-8b10-a81e44d32808 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test: add legacy-parity expected failures for ADL blocker tests The new ADL nearest-scope blocker and block-scope function declaration tests rely on scope-resolution-only mechanisms not present in the legacy DAG path. Register them in LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a2f97daf-17fd-4891-8b10-a81e44d32808 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore: revert unrelated prettier-plugin-tailwindcss devDep addition The `prettier-plugin-tailwindcss` dependency was accidentally added while running local prettier; it is not needed for the C++ ADL changes. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a2f97daf-17fd-4891-8b10-a81e44d32808 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> |
||
|
|
99b8c7b03b
|
feat: C++ ADL V2: free-function reference args contribute enclosing namespace (#1598)
* Initial plan * cpp ADL V2: free-function reference args contribute enclosing namespace Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/24805583-c0c4-4ef8-978f-b874bd917947 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * merge: resolve conflicts with origin/main and fix overloaded fixture app.cpp Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/136aeffe-45da-47e2-95dd-e3883e85fad7 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(finding-1): replace ISO C++ [basic.lookup.argdep] misstatement with GitNexus-approximation label Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fa37c0dd-65f9-4dd4-9811-617227a37073 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(finding-2): verify Function/Method exists in namespace before contributing via qualified_identifier arg Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fa37c0dd-65f9-4dd4-9811-617227a37073 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(finding-3): function parameters in parameter_list no longer misclassified as free-function refs Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fa37c0dd-65f9-4dd4-9811-617227a37073 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * doc(finding-4): document typedef/using-aliased function-pointer limitation in lookupAdlIdentifierType Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fa37c0dd-65f9-4dd4-9811-617227a37073 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(finding-5): add negative fixtures for local-fp shadowing free-func and unqualified namespace collision Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/fa37c0dd-65f9-4dd4-9811-617227a37073 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(legacy-parity): skip two new negative-fixture tests from legacy DAG parity run Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/64ffbaf1-f442-4a2b-8542-4afa500d9182 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
813acd7ec5
|
feat: C++ ADL V2: include base-class associated namespaces via MRO (#1597)
* Initial plan * fix(cpp): include base-class namespaces in ADL candidate selection Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/7df6f692-1af9-43e6-82de-099ed43a60cb Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): clarify ADL base-namespace test names Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/7df6f692-1af9-43e6-82de-099ed43a60cb Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): remove stale legacy parity expected-failure entry Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/1a55a5e8-ae91-44bc-9b21-9324cdfea3de Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): assert base-namespace ADL tests are not parity skips Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b3726b70-e797-4f37-955d-7d61fd28d338 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(cpp): avoid MRO amplification on ambiguous class-name ADL lookup Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b3726b70-e797-4f37-955d-7d61fd28d338 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): strengthen ADL base-namespace target identity assertions Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b3726b70-e797-4f37-955d-7d61fd28d338 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): add ADL negative cases for anonymous and unresolved bases Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b3726b70-e797-4f37-955d-7d61fd28d338 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): fix anonymous-base parity expectation and formatting Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/6a2e3cf9-beea-435c-8494-6a7a00af0f1e * fix(cpp): propagate unnamed-namespace members through #include in registry-primary resolver Anonymous-namespace contents in a header (e.g. `namespace { void f(); }`) are reachable by unqualified lookup in any TU that #includes the header per ISO C++ [basic.namespace.anon]/1 (the unnamed namespace behaves as if a `using namespace unique;` is inserted into the enclosing scope, with per-TU `unique`). The registry-primary path was filtering these defs out of `expandCppWildcardNames` via both the structural Namespace-owner check and the `isFileLocal` mark, so `hidden_probe(d)` from a TU including the header resolved to nothing while the legacy DAG returned the correct edge. Track anonymous-`namespace_definition` source ranges at capture time, resolve them to ScopeIds in `populateOwners` (parallels inline-namespace handling), and exempt those scopes from the two wildcard-expansion filters plus the `populateCppNonGloballyVisible` structural set. `markFileLocal` is preserved so the global free-call fallback still blocks cross-TU leaks for files that do NOT #include the declaring file (cpp-anon-ns-cross-file guard still passes). --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
7fbf302018
|
feat: C++ ADL V2: include template-specialization associated namespaces (with nested template args) (#1596)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
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
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
|
||
|
|
5ee1122330
|
chore(deps)(deps-dev): bump vitest from 4.1.5 to 4.1.6 in /gitnexus (#1605) | ||
|
|
cdac8a691a
|
feat: C++ ADL V2: include class-typed reference args (incl. rvalue refs) in associated-namespace lookup (#1595) | ||
|
|
b00ba2ab47
|
feat(cpp): resolve template-body this-> + using ns::name calls in scope resolver (#1590)
* Initial plan * fix(cpp): resolve this-> and using-name calls in template bodies Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/d9d91945-f19c-4fd2-9b52-b0ebc9aa34b6 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(cpp): treat duplicate using-name hits as ambiguous Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/d9d91945-f19c-4fd2-9b52-b0ebc9aa34b6 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(cpp): gate this-receiver path and harden overload semantics Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/030a1842-c698-460d-ae2a-95037e6def73 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): add positive this-> overload case and document field shadowing Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/030a1842-c698-460d-ae2a-95037e6def73 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(cpp): skip new template-this assertions in legacy parity lane Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/27002f6e-6331-41e3-8175-9d9e4691927c Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
c2193318b5
|
feat(cpp): Enable C++ ADL for class pointer arguments and exclude function pointers (#1592)
* Initial plan * fix: unwrap cpp adl pointer argument types Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2e9c8549-e062-410c-9ce3-66ba0a181590 * chore: tighten cpp adl function-pointer guard Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2e9c8549-e062-410c-9ce3-66ba0a181590 * docs: clarify cpp adl implementation comments Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2e9c8549-e062-410c-9ce3-66ba0a181590 * fix: avoid aborting cpp adl declaration scan Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/e54f1d4b-9aac-407c-9b5e-b5f3ea0534ea --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
8b2d8018bc
|
fix(cli): tolerate read-only workspace in ensureGitNexusIgnored (#1549) (#1550)
* fix(cli): tolerate read-only workspace in ensureGitNexusIgnored The documented Docker workflow mounts the host workspace at /workspace:ro and runs `gitnexus index /workspace/<repo>` against an index produced by a prior host-side `analyze`. Since PR #1248 ("keep GitNexus ignores inside .gitnexus") the index command has called `ensureGitNexusIgnored`, which unconditionally writes `<repo>/.gitnexus/.gitignore` and `<repo>/.git/info/exclude` — both fail with EROFS on the :ro bind mount even though the host already wrote the correct file during `analyze`. Two complementary changes: 1. Idempotent fast path. Read the existing .gitnexus/.gitignore content first; if it already matches the desired value (`*\n`), skip the write entirely. This is the common case for the Docker workflow and avoids touching the FS at all. 2. EROFS/EACCES tolerance. When a write is genuinely needed but the FS refuses it, log a structured warning via the existing pino logger and continue. `registerRepo` runs before `ensureGitNexusIgnored` in `indexCommand`, so the global-registry write is already committed when we get here — letting the gitignore-write failure propagate leaves the user with a registered-but-error-exited command. Three new unit tests pin the behaviour: - idempotent re-call leaves mtime untouched - ENOENT-then-correct path on a writable parent succeeds - :ro parent (simulated via chmod 0o555) does not throw, on the already-correct fast path and on the cold-create path Existing tests (61) still pass. Closes #1549. * test(storage): cover read-only ignore paths and tolerate EPERM (#1550) - Add isReadOnlyFilesystemError helper including EPERM alongside EROFS/EACCES for ensureGitNexusIgnored and ensureGitInfoExclude (Windows parity with lbug-config / bridge-db patterns). - Skip chmod-based read-only tests on win32 and uid 0; assert logger.warn on POSIX chmod denial for missing .gitignore. - Add repo-manager-ensure-ignore-readonly.test.ts with vi.mock fs/promises delegating writeFile so EROFS/EACCES/EPERM rejections are asserted with structured log path and message for both .gitignore and .git/info/exclude. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(autofix): apply prettier + eslint fixes via /autofix command --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
89c03b2ebb
|
fix: skip Claude augment hook when GitNexus server owns DB (#1493)
* fix(claude): skip augment hook when server owns db * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(hooks): cross-platform DB lock probe for MCP owner guard Extract hook-db-lock-probe.cjs with a single hasGitNexusDbLockedByGitNexusServer entry point used by both Claude hooks: - Linux: scan /proc/<pid>/fd via dev+inode (no lsof required), optional lsof fallback; GITNEXUS_HOOK_LINUX_PROC_BUDGET_MS caps scan time - macOS and other Unix: trusted lsof + ps (absolute paths / env overrides) - Windows: Restart Manager + Win32_Process via win-rm-list-json.ps1 and GITNEXUS_HOOK_POWERSHELL_PATH Update hooks.test.ts source coverage for the probe module. Co-authored-by: Cursor <cursoragent@cursor.com> * Update gitnexus/hooks/claude/win-rm-list-json.ps1 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestion from @github-actions[bot] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(gitnexus): repair package.json JSON after malformed engines edit Co-authored-by: Cursor <cursoragent@cursor.com> * Update Node.js engine version requirement to 22.0.0 * Update Node.js engine version to >=22.0.0 * fix(hooks): address ce-code-review findings on PR #1493 P0: - Replace malformed `RM_UNIQUE_PROCESS` block in `gitnexus/hooks/claude/win-rm-list-json.ps1` (duplicate struct decl + duplicate `ProcessStartTime` + unbalanced braces) with a single well-formed `[StructLayout(LayoutKind.Sequential, Pack = 4)]` struct, so PowerShell `Add-Type` actually compiles and the Windows DB-lock probe stops fail-open on every machine. - `gitnexus/src/cli/setup.ts` now copies `hook-db-lock-probe.cjs` and `win-rm-list-json.ps1` into the user's `~/.claude/hooks/gitnexus/` alongside `hook-lock.cjs`, preventing the `MODULE_NOT_FOUND` thrown by `gitnexus-hook.cjs:18`'s top-level require on every fresh install. `gitnexus/test/unit/setup.test.ts` extended to assert both new copy destinations. - Four fail-open hook tests (`ENOENT lsof`, `npx parent line`, `non-GitNexus ps line`, `ps ENOENT`) now seed `createHookToolDir` with a valid `[GitNexus]` stderr line so `expect(parseHookOutput).not.toBeNull()` actually holds on CI. P1: - Plugin copy of `win-rm-list-json.ps1` gains `Pack = 4` so its CLR struct matches the 12-byte native `RM_UNIQUE_PROCESS` layout (multi-blocker `RmGetList` no longer reads mangled `dwProcessId`). - `GITNEXUS_HOOK_CLI_PATH = ''` now falls through to the resolution chain in `gitnexus-hook.cjs`, matching the plugin copy and removing the twin-file divergence on empty-string envs. - Lock-warning suppression test seeds `gitnexusMarkerPath` and asserts the augment subprocess actually ran, plus `GITNEXUS_DEBUG=1` preserves the full discarded prefix. - MCP-owner skip branch in both hook copies now emits `[GitNexus] augment skipped: MCP server owns DB` on stderr, so agents can distinguish intentional skip from silent failure. P2: - `ps` loop in `hook-db-lock-probe.cjs` fails-closed on `ETIMEDOUT` to mirror the `lsof` handling (symmetric subprocess-probe contract). - `RmStartSession` return value captured in both `.ps1` copies; exits early with `[]` on non-zero so subsequent RM API calls don't operate on an invalid handle. - Windows RM-list `.ps1` encoded cache distinguishes uninitialized (`undefined`) from load-failed (`null`) with a one-shot `GITNEXUS_DEBUG` warning instead of silently caching empty string. - `createHookToolDir` helper accepts `lsofOutputLines` and `psOutputByPid`; the multi-PID test uses them instead of duplicating the fake-binary construction inline. - All five skip-path tests now assert `result.status === 0` and the new skip-signal stderr line. - `AGENTS.md` documents the seven hook configuration env vars (`GITNEXUS_HOOK_CLI_PATH`, `_LSOF_PATH`, `_PS_PATH`, `_POWERSHELL_PATH`, `_LINUX_PROC_BUDGET_MS`, `_RM_TARGET`, `GITNEXUS_DEBUG`). - `GITNEXUS_DEBUG` path in `gitnexus-hook.cjs`/`.js` writes the full discarded stderr prefix instead of a 180-char preview. - Inline comment in `hook-db-lock-probe.cjs` explains the intentional Windows ETIMEDOUT fail-closed semantics. - Removed the unnecessary `as WriteFileOptions` cast and orphaned `import type { WriteFileOptions }` in `hooks.test.ts`. P3: - `isGitNexusServerCommand` unexported from `hook-db-lock-probe.cjs` (kept as private helper). - Env-path overrides (`GITNEXUS_HOOK_CLI_PATH`, `_POWERSHELL_PATH`, `_LSOF_PATH`, `_PS_PATH`) require `fs.existsSync` before being returned, so typos / stale config fall through to the standard resolution chain. Misc: - `gitnexus/package.json` engines.node back to `>=22.0.0` (matches origin/main and the original PR reviewer's earlier request). Twin-tree parity / CI sync mechanism tracked separately at abhigyanpatwari/GitNexus#1591. Test plan: vitest run test/unit/hooks.test.ts → 113 passed, 18 Unix-only skipped; setup.test.ts → 14 passed. * chore(autofix): apply prettier + eslint fixes via /autofix command * trigger --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
911a2ee1e6
|
fix: apply ESM .js extension fallback to tsconfig path alias resolution (#1530)
* fix: apply ESM .js extension fallback to tsconfig path alias resolution Path alias imports (e.g. `@/utils.js` via tsconfig paths) now correctly strip JS-family extensions and retry with TS equivalents when the literal .js file does not exist. This applies the same stripJsExtension fallback already used for relative imports to the alias resolution branch. Fixes #1528 * chore(autofix): apply prettier + eslint fixes via /autofix command * test(esm): cover .mjs/.cjs path-alias extension resolution Co-authored-by: Cursor <cursoragent@cursor.com> * test(esm): use Map for path aliases in resolveWithAlias helper Matches TsconfigPaths.aliases from language-config. CI cannot run tsc -p tsconfig.test.json yet: the project has hundreds of pre-existing errors under test/ (fixtures + unit/integration); enable that step after backlog cleanup. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
586dbf7aa1
|
feat(cpp): disambiguate template specializations in class graph IDs and receiver routing (#1587)
* Initial plan * fix(cpp): disambiguate template specializations in class graph IDs Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c929edb2-f2e9-41c6-a2e9-2092b967f603 * fix(cpp): guard template-specialization class lookup fallback Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/c929edb2-f2e9-41c6-a2e9-2092b967f603 * fix(cpp): address github-actions inline review findings Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/68d8fbac-4ff4-47f7-b732-eaf2c2f94043 * fix(cpp): cover template-type receiver binding for specialization routing Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9505dfcd-3fb6-4bc2-a134-f60fe0dc8cd9 * chore(cpp): clarify specialization-binding fallback assumptions Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9505dfcd-3fb6-4bc2-a134-f60fe0dc8cd9 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
c901ee4666
|
fix(cpp): workspace-wide dependent-base name resolution for cross-file templates (#1586)
* Initial plan * fix(cpp): workspace-wide dependent-base name resolution (cross-file support) - Replace per-file `populateCppDependentBases(parsed)` with a workspace-wide `populateCppDependentBases(parsedFiles)` that builds a cross-file class index - Use qualified-name prefix for namespace disambiguation when multiple classes share a simple name (e.g. `Box` in two namespaces) - Move the call from `populateOwners` (per-file) to the new `populateWorkspaceOwners` hook so all files are processed before resolution runs - Add `cpp-two-phase-dependent-base-ns` fixture: Base<T> in a namespace in a separate file from Derived<T>, plus a namespace-free function with the same name — exercises the path where the class-owned filter does not apply - Add two integration tests for the new fixture" Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/d78fae8b-cd32-45d8-a815-2b27d7d89e62 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(cpp): clarify V1 conservative exact-prefix namespace match in two-phase-lookup Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/d78fae8b-cd32-45d8-a815-2b27d7d89e62 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> |
||
|
|
75cb49477e
|
feat(cpp): emit EXTENDS edges for template and qualified template bases (#1581)
* Initial plan * fix: emit cpp extends edges for template bases Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/eaddb1ac-7b57-4f44-94ba-a07a578d078d Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore: address final review notes Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/eaddb1ac-7b57-4f44-94ba-a07a578d078d Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: keep cpp extends edges class-owned Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b10bbb4d-6746-46fa-9b82-5c0962cd8b3f Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test: address cpp follow-up review findings Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/be67e437-055f-4a71-a24e-d3bfb87ad0cd Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> |
||
|
|
e01f0912bc
|
feat(cpp): migrate C++ to scope-based resolution model (#938) (#1520)
* fix(cpp): complete scope-resolution parity * fix(ci): resolve formatting, lint errors for PR #1520 - prettier: format arity-metadata.ts, captures.ts, index.ts - eslint: rename unused HEADER_GLOB to _HEADER_GLOB - eslint: replace unsafe parser.parse() with parseSourceSafe() - eslint: suppress intentional console.warn/log in sync.ts - eslint: remove unused _it import alias in cpp.test.ts * fix(ci): complete formatting, lint, and typecheck fixes - prettier: format call-processor.ts, imported-return-types.ts, include-extractor.test.ts, cpp-captures.test.ts, cpp-imports.test.ts - eslint: suppress intentional console.warn in manifest-extractor.ts - typecheck: restore 'thrift' in ContractType union (was accidentally removed) and add thrift case to exhaustive switch in manifest-extractor * fix(ci): revert unintended group module changes that broke tests Restore types.ts, config-parser.ts, matching.ts, sync.ts, and manifest-extractor.ts to upstream/main versions. The original commit accidentally removed fields (thrift, workspace_deps, exclude_links_paths, exclude_links_param_only_paths) from DetectConfig/MatchingConfig/ContractType which are still referenced by matching.test.ts, config-parser.test.ts, sync.test.ts and other integration tests. This PR's scope is C++ scope-resolution parity only — group module type definitions and logic should remain unchanged. * fix(codeql): address security and quality alerts - arity-metadata.ts, interpret.ts: replace single-pass template strip regex (/<[^>]*>/g) with a while-loop to fully handle nested templates like Map<List<int>> — resolves 'Incomplete multi-character sanitization' - cpp.test.ts: remove unused vitest 'it' import since the file defines its own 'it' via createResolverParityIt — resolves 'Assignment to constant' - include-extractor.test.ts: use fs.mkdtempSync() instead of predictable os.tmpdir()+Date.now() paths — resolves 'Insecure temporary file' - interpret.ts: remove redundant 'name !== undefined' check (already guaranteed by early return) — resolves 'Comparison between inconvertible types' * review: address Claude review findings on PR #1520 - Findings 1-3 (BLOCKERS): restore include-extractor.ts and its test to the main baseline. Block-comment fallback regression, suffix-resolve false-positive suppression, and the four deleted regression tests (#3-#6) are now back. These changes were unrelated to C++ scope parity and should not have been in this PR. - Finding 4 (MAJOR, partial): revert COMPOUND_RECEIVER_MAX_DEPTH 6 to 4. No C++ test exercises depth > 4 (cpp-chain-call uses a 2-hop chain), so the bump risked silent regressions on other migrated languages without justification. The wildcard-origin propagation in imported-return-types.ts is retained — C++ #include and using namespace both emit wildcard-origin bindings (cpp/import-decomposer .ts:40,90), so wildcard propagation is causal to C++ parity. - Finding 6: tighten write-access dedup test with exact per-field counts (nameWrites = 2, addrWrites = 1) instead of total-count + sub string containment, so a regression in one of the two name writes can no longer be masked. - Finding 8: skipped. Box-drawing characters in cpp/query.ts comments match the established convention used in csharp/java/php query files. Finding 5 (int/long normalization tie-breaker) left as documented follow-up — proper fix requires resolver-level tie-breaker logic and risks regressing other arity-matching tests. * fix(cpp): stop #include from leaking class methods and namespace members (U1) The C++ registry-primary resolver was emitting impossible CALLS edges for ordinary headers: an including file's unqualified save() resolved to User::save and unqualified foo() resolved to ns::foo. Two leak paths converged on localDefs: 1. expandCppWildcardNames (file-local-linkage.ts) iterated the flattened localDefs and exported every simple tail, including class-owned methods and namespace-contained symbols. Replaced with a scope-aware filter: build nodeId -> owning Scope from Scope.ownedDefs and skip defs whose owning scope is Namespace or Class. 2. The shared global free-call fallback's pickUniqueGlobalCallable walks the workspace registry by simple name and would still hit class methods / namespace members even with wildcard expansion fixed. Plugged the gap via the existing isFileLocalDef hook — semantically 'logically invisible cross-file' — by tracking per- file non-globally-visible nodeIds (populateCppNonGloballyVisible, called from populateOwners) and adding an ownerId !== undefined fast-path for class-owned defs. Side fix in shared finalize-algorithm.ts: when wildcard expansion resolves to a real target but produces zero propagating names, the edge was dropped, taking the file-level IMPORTS edge with it. Preserve the original wildcard edge so #include dependencies survive even when the header exposes no unqualified bindings. Tests: cpp-include-no-class-leak, cpp-include-no-namespace-leak, and cpp-anon-ns-same-file-visible fixtures. Negative tests mode-gated to REGISTRY_PRIMARY_CPP=1 via the expected-failures registry — legacy DAG has no scope-aware filtering on the global fallback; backporting is out of scope. All 2104 resolver integration tests pass under registry-primary mode. * fix(cpp): suppress receiver-bound CALLS when integer-width overloads collide (U2) C++ arity-metadata normalizes int, long, short, unsigned, size_t to 'int' so single-candidate flows like 'process(42L)' match a 'long'- typed parameter via loose matching. But when both 'process(int)' and 'process(long)' coexist as method overloads, they both end up with parameterTypes=['int'] in the registry, and pickOverload's narrowing returns 2 candidates with no way to disambiguate. The previous code picked candidates[0] arbitrarily, emitting a CALLS edge to the wrong overload roughly half the time. Fix: - Add isOverloadAmbiguousAfterNormalization in overload-narrowing.ts that detects >1 candidate sharing identical parameterTypes sequences. - Have pickOverload return a new OVERLOAD_AMBIGUOUS sentinel when this fires. - In the receiver-bound-calls loop, when pickOverload signals ambiguity, suppress the edge AND add the site to handledSites so the late-stage emitReferencesViaLookup pass does not re-emit the pre-resolved reference. Without the handled-mark, the reference index still carries a toDef and emits the same wrong edge. Graph schema has no ambiguous-target edge model, so emitting two edges (one per candidate) would require a separate schema change. Zero-edge is the only safe outcome. Other languages: the ambiguity check is a precondition gate, not a behavior change for normal narrowing. Languages whose normalizers do not collapse distinct types into a single token (verified by grep over *-arity-metadata.ts) will never produce >1 candidate with identical parameterTypes from genuinely distinct declarations, so the branch is effectively C++-only in practice. Test: cpp-overload-int-long fixture asserts exactly .toBe(0) CALLS edges. Count=1 = arbitrary pick (the bug); count>1 = unsupported ambiguous-edge model. Mode-gated to REGISTRY_PRIMARY_CPP=1 — legacy DAG has no OVERLOAD_AMBIGUOUS wiring; backporting is out of scope. All 2105 resolver integration tests pass under registry-primary; all 139 cpp tests pass under both modes (3 negative tests skipped in legacy as documented). * test(cpp): add integration coverage for anonymous-namespace, using-namespace conflict, and std-shim leakage (U3+U4+U5) Three new end-to-end fixtures exercise the resolver pipeline against scenarios that previously had only unit-level coverage or no coverage at all (Claude review Finding 7): U3 — cpp-anon-ns-cross-file: helper.cpp declares 'namespace { void worker(); }' and calls it internally. caller.cpp declares a separate 'void worker()' and calls it. Asserts (a) the cross-file CALLS edge from caller's run() does not target helper.cpp's anonymous-namespace worker, and (b) the same-file edge from helper_entry() to its own worker still resolves (positive guard against a 'no edges at all' regression making the negative check vacuously pass). Includes a state-isolation guard that re-runs the same fixture and asserts identical results, proving clearFileLocalNames() is called by the pipeline entry. U4 — cpp-using-namespace-conflict: Two headers each declaring 'namespace a { foo() }' and 'namespace b { foo() }' respectively, plus a caller doing 'using namespace a; using namespace b; foo()'. Asserts exactly zero CALLS edges. One edge = arbitrary pick (the bug); two edges would require an ambiguous-target edge model GitNexus does not have. Depends on U1 — without scope-aware filtering, both foo()s would already be in the importer's wildcard binding set as simple 'foo', so the test would pass for the wrong reason. U5 — cpp-using-namespace-std-smoke: Fixture-local 'namespace std { void cout_write(); void println(); }' shim rather than real <iostream> — captures the wildcard-leak shape deterministically without depending on system-header modeling stability (out of scope per plan). Asserts (a) the project-local call resolves correctly, (b) no leak to shim STL symbols, and (c) no CALLS/ACCESSES edges from the caller into std-shim.h at all. Negative tests for U2/U4 mode-gated to REGISTRY_PRIMARY_CPP=1 via the expected-failures registry; legacy DAG lacks the OVERLOAD_AMBIGUOUS suppression and the namespace-aware filtering, so the leaks persist there. All 2112 resolver integration tests pass under registry-primary; all 146 cpp tests pass under both modes (4 negative tests skipped in legacy as documented). * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(cpp): scope-aware isSuperReceiver classification (U1) The C++ isSuperReceiver hook used a regex `/^[A-Z]\w*::/` that misclassified any uppercase-qualified call as a super-receiver call. Singleton::getInstance(), std::Foo::bar(), and PascalCase namespace calls all entered the super branch, where the absence of an enclosing class (or wrong MRO context) dropped the resolution entirely. Fix: - New optional ScopeResolver hook isSuperReceiverInContext(text, callerScope, scopes). Languages where super classification depends on caller context define it; receiver-bound-calls.ts prefers it when defined and falls back to the simple isSuperReceiver(text) otherwise. Other migrated languages (Python, Java, C#, PHP, Go, TypeScript) are unchanged. - C++ implementation: parse the LHS of '::' from the receiver text, resolve via findClassBindingInScope, and return true only when the LHS is a class-like def in the caller's enclosing class's MRO. Returns false for namespace LHS, unresolved LHS, self-class LHS (qualified self-calls aren't super), and any non-'::' form. - Extended the C++ tree-sitter query to capture the LHS of qualified_identifier as @reference.receiver so qualified static member calls (Singleton::getInstance()) reach the receiver-bound Case 2 (class-name receiver) path. Without the receiver capture, qualified calls had no explicit receiver and could not resolve through any receiver-bound branch. Test: cpp-namespace-qualified-not-super fixture. Singleton::getInstance() from a free function asserts exactly 1 CALLS edge through the qualified-call path. Passes under both REGISTRY_PRIMARY_CPP=1 and =0. All 2113 resolver integration tests pass; all 147 cpp tests pass under both modes. * fix(cpp): suppress receiver-bound CALLS when default-arg overloads collide (U4) ISO C++ rejects 's.f(1)' as ambiguous when both 'void f(int)' and 'void f(int, int = 0)' are declared on S. The previous resolver returned the first viable candidate via pickOverload's fallback. Extended isOverloadAmbiguousAfterNormalization to take an optional argCount: when provided, the predicate compares only the first argCount slots of each candidate's parameterTypes. Candidates whose declared-prefix matches up to argCount are treated as ambiguous because default arguments make all of them equally viable for the call. Without argCount, behavior is unchanged (the original int/long normalization-collapse contract, full-length equality required). pickOverload now passes site.arity so default-arg ambiguity fires. Test: cpp-overload-default-arg-ambiguous fixture. s.f(1) where S has f(int) and f(int, int = 0) asserts exactly .toBe(0) CALLS edges. Passes under both REGISTRY_PRIMARY_CPP=1 and =0. All 2114 resolver integration tests pass; all 148 cpp tests pass under both modes. * fix(cpp): two-phase template lookup suppresses dependent-base members (U3) ISO C++ two-phase name lookup: inside a class template body, unqualified calls MUST NOT bind to members of a dependent base class. Only this->name or Base<T>::name forms make the lookup dependent. GCC and Clang both reject the unqualified form with 'declaration of f must be available'. Before this fix, GitNexus's global free-call fallback walked the workspace registry by simple name and bound unqualified calls inside template bodies to dependent-base members, producing CALLS edges the compiler would reject. Implementation: - New languages/cpp/two-phase-lookup.ts module: per-pipeline state recording (className, dependentBaseName) pairs at capture time and resolving them to nodeId sets during populateOwners. - captures.ts detectCppDependentBases walks the AST once finding every template_declaration containing a class/struct definition. For each, it collects template-parameter names (typename T, class T, non-type int N, template-template parameters) and walks each base in the base_class_clause checking whether any inner type_identifier matches a template parameter. Conservative bias: typename T::U, decltype, and template-template-parameter shapes also classified as dependent. - Extended scope-resolution contract's isCallableVisibleFromCaller hook with optional callerScope and scopes fields. C++ implements the hook to consult isCppDependentBaseMember: when the candidate is a member of a dependent base of the caller's enclosing class, the hook returns false and pickUniqueGlobalCallable skips the candidate. - clearFileLocalNames also clears the dependent-base state per pipeline run. Fixtures: - cpp-two-phase-dependent-base: Derived<T> deriving from Base<T>, unqualified f() and i inside Derived's body. Asserts zero CALLS edges and zero ACCESSES edges respectively. - cpp-two-phase-this-qualified, cpp-two-phase-non-dependent-base, cpp-two-phase-namespace-free-call-inside-template: positive fixtures left as documented gaps (this-> and qualified-name resolution inside template bodies are pre-existing resolver weaknesses independent of U3). Tracked separately. Negative test mode-gated to REGISTRY_PRIMARY_CPP=1 via the expected- failures registry; legacy DAG has no two-phase lookup. All 2116 resolver integration tests pass under registry-primary; all 150 cpp tests pass under both modes (5 negative tests skipped in legacy as documented). * fix(cpp): implement V1 ADL (Koenig lookup) for free-function calls (U2) Plan 2026-05-13-001 U2. Adds argument-dependent lookup as a new candidate-generating tier in `emitFreeCallFallback`: when ordinary unqualified lookup is empty, ADL surfaces candidates from each value-class-typed argument's enclosing namespace. V1 boundary (locked by cpp-adl-pointer-arg-boundary fixture): - only direct enclosing-namespace closure - only directly-named class-type values (pointer / reference / template- spec args excluded; closure rules deferred to V2) - ADL fires ONLY when ordinary lookup is empty (no union-and-resolve) Parenthesized name `(f)(s)` suppresses ADL per ISO C++ [basic.lookup.argdep]/3.1. Multi-candidate ambiguity (e.g. `process(int)` vs `process(long)` after C++ int-width normalization) returns the ADL_AMBIGUOUS sentinel — caller suppresses entirely, mirroring the OVERLOAD_AMBIGUOUS contract from plan 2026-05-12-002 U2. Implementation: - `cpp/adl.ts` — new module: per-pipeline argInfoBySite + noAdlSites Maps populated at capture time, classToNamespaceQualifiedName Map populated during populateOwners; `pickCppAdlCandidates` returns SymbolDefinition | ADL_AMBIGUOUS | undefined - `scope-resolution/contract/scope-resolver.ts` — adds optional `resolveAdlCandidates` hook - `scope-resolution/passes/free-call-fallback.ts` — invokes ADL hook between `findCallableBindingInScope` and `pickUniqueGlobalCallable`; marks site handled on `'ambiguous'` so emit-references doesn't retry - `cpp/captures.ts` — detects `parenthesized_expression` function wrap; per-arg classification (pointer/reference/value class) preserving the shape info the existing arity-narrowing normalizer strips - `cpp/scope-resolver.ts` — registers hook, populates associated namespaces, clears state in loadResolutionConfig Negative tests (parens, pointer-boundary, ambiguous) gated under LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.cpp — legacy DAG has no V1/V2 ADL boundary or ADL_AMBIGUOUS suppression. 154/154 cpp integration tests pass under REGISTRY_PRIMARY_CPP=1; 147 pass + 7 skipped under =0 (legacy parity baseline). * fix(cpp): inline namespace transitive walking + qualified namespace resolution (U5) Plan 2026-05-13-001 U5. Two ISO C++ inline-namespace semantics: 1. Unqualified-lookup transitive visibility: inline-namespace members reach the enclosing namespace's scope as if declared there. The `populateCppNonGloballyVisible` exemption keeps them globally visible so cross-file unqualified lookup finds them. 2. Qualified-receiver transitive visibility: `outer::foo()` resolves to `outer::v1::foo()` when `v1` is inline (and through arbitrarily-deep nesting like `outer::v1::experimental::foo`, matching libc++ `__1` / libstdc++ `__cxx11`). The second behavior required a new resolver case in `receiver-bound-calls.ts` (Case 1.5: language-specific qualified-receiver member lookup) because C++ qualified-namespace member calls had no prior resolution path — receiver-bound Case 1 only handled `ParsedImport.kind === 'namespace'` (Python/JS-style) and Case 2 handles class receivers, neither of which fired for `outer::foo()`. The new hook `resolveQualifiedReceiverMember` is opt-in; languages without C++-style qualified-name semantics omit it. Implementation: - `cpp/inline-namespaces.ts` — new module: per-pipeline `inlineNamespaceRangesByFile` + `inlineNamespaceScopeIds` Sets; `markCppInlineNamespaceRange` at capture time; `populateCppInlineNamespaceScopes` resolves ranges → scope IDs; `resolveCppQualifiedNamespaceMember` walks namespace scopes by simple name and descends transitively through inline children only. - `scope-resolution/contract/scope-resolver.ts` — adds optional `resolveQualifiedReceiverMember` hook to the contract. - `scope-resolution/passes/receiver-bound-calls.ts` — Case 1.5 invokes the hook between Case 1 (namespace imports) and Case 2 (class-name receiver). Returns undefined for non-namespace receivers so Case 2 still resolves class-qualified calls. - `cpp/captures.ts` — detects `inline` keyword child on `namespace_definition`; records 1-based range to match Scope.range. - `cpp/file-local-linkage.ts` — `populateCppNonGloballyVisible` exempts inline-namespace scopes so cross-file unqualified lookup keeps their members visible. - `cpp/scope-resolver.ts` — wires `populateCppInlineNamespaceScopes` into populateOwners (BEFORE `populateCppNonGloballyVisible` so the exemption sees populated state); registers `resolveQualifiedReceiverMember` hook. 4 fixtures: `cpp-inline-namespace-unqualified`, `-versioned`, `-nested` (two transitive inline hops, STL `__1` shape), and `-adl-participation` (composes with U2 — ADL surfaces records declared inside inline child namespaces). All 4 assert exactly 1 CALLS edge with correct target file. Versioned fixture gated under LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.cpp — legacy DAG can't disambiguate two same-name foos without inline awareness. Other 3 coincidentally resolve in legacy. 158/158 cpp integration tests pass under REGISTRY_PRIMARY_CPP=1; 150 pass + 8 skipped under =0 (legacy parity baseline). * test(cpp): Phase 5 cross-unit composition tests for U1/U2/U3/U5 Plan 2026-05-13-001 Phase 5. Locks in correct behavior at the intersections between the previously-shipped scope-resolver units. Enhancement to U1: `isSuperReceiverInContext` strips template-argument lists (`Base<T>` → `Base`) and namespace prefixes (`outer::v1::Base` → `Base`) before resolving the receiver in the caller's scope chain. This makes the super-receiver classification work for template-class heritage shapes like `Base<T>::method()` and `outer::v1::Base<T>::f()`. Three fixtures + four tests: - `cpp-phase5-u1-u3-qualified-base-call`: `template<class T> struct Derived : Base<T>` with `Base<T>::method()` inside a template body. Asserts NO mis-routing (count = 0) — documents the V1 gap that template-class inheritance isn't captured as EXTENDS by the legacy DAG, so MRO walks are empty and the super branch can't dispatch. The composition still works correctly: U1's template-arg-stripping classifies `Base<T>` as a super candidate, but the empty-MRO terminates without false edges. - `cpp-phase5-u2-u3-adl-from-derived`: `Derived : Base<T>` where `Base::record` shadows `audit::record`. Unqualified `record(e)` inside the template body should resolve via ADL to `audit::record` (because U3 + the `isFileLocalDef` class- owned filter suppress `Base::record`). Asserts 1 edge to audit.h and 0 edges to base.h. - `cpp-phase5-u3-u5-inline-base`: `template<class T> struct Derived : outer::v1::Base<T>` where `v1` is inline. Unqualified `f()` inside `Derived<T>::g()` should NOT bind to Base::f (dependent-base suppression even across inline namespace prefix). Asserts count = 0. Phase 5 tests asserting no-false-positives are gated under LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.cpp — legacy DAG over- resolves without the template-arg-stripping qualified-receiver path and without two-phase dependent-base suppression. 162/162 cpp integration tests pass under REGISTRY_PRIMARY_CPP=1; 152 pass + 10 skipped under =0 (legacy parity baseline). --------- Co-authored-by: HuangWenjie <zhoudeng.hwj@alibaba-inc.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
e9349ce66a
|
fix(markdown): handle CRLF line endings in section heading parser (#1469)
* fix(markdown): handle CRLF line endings in section heading parser
split('\n') on CRLF content leaves a trailing \r on each line, and the
heading regex /^(#{1,6})\s+(.+)$/ (anchored with $) fails to match
'## Heading\r' because $ matches before end-of-string, not before \r.
Result: Windows-authored markdown silently produces zero Section nodes.
Use split(/\r\n|\r|\n/) to normalize all line-ending conventions.
Pure additive — LF-only files produce identical output. CR-only (Mac OS
Classic) becomes tolerated as a side benefit at zero risk.
Adds integration test markdown-processor-crlf.test.ts covering LF
baseline, CRLF (the regression), CR-only, mixed, and startLine/endLine
correctness.
* test(markdown): strengthen CRLF integration tests + clarify split comment
- Assert section names, levels, line spans, and CONTAINS hierarchy (not only counts)
- Document trailing-newline effect on endLine via exact toEqual expectations
- Reword markdown-processor comment: \$ only at end-of-string vs .+ before \\r
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: empty commit
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
a3eef48ce3
|
fix(cli): make --no-stats actually omit volatile counts (#1477) (#1478)
* fix(cli): make --no-stats actually omit volatile counts (#1477) Closes #1477. The `--no-stats` flag on `gitnexus analyze` was advertised as "Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md" but had no effect: every reindex still rewrote the markdown with fresh count phrases, producing chore-commit churn on every run — the exact problem the flag was added to solve in #704. Root cause is commander.js negation-flag semantics. `.option( '--no-stats', ...)` registers the option under the accessor `stats` (boolean, default `true`; `false` when the flag is passed), NOT `noStats`. The two action-handler reads in `analyze.ts` (lines 414 and 500 pre-fix) read `options?.noStats`, which is always `undefined`, so the `noStats` payload always reached `runFullAnalysis` / `generateAIContextFiles` as `undefined`/falsy and the count branch in the template always fired. Fixed by replacing `options?.noStats` with `options?.stats === false` at both reads. The strict `=== false` check (rather than `!options?.stats`) means absent options or absent `.stats` field fall through as no-stats=false, preserving the documented default-on behaviour. Also updated the `AnalyzeOptions` interface to declare `stats?: boolean` (matching commander's actual output) with a JSDoc explaining the negation, since the prior `noStats?: boolean` shape was a static-type misrepresentation of what commander provides at runtime. Internal call sites that re-pack `{ noStats: ... }` for downstream consumers (`run-analyze.ts`, `ai-context.ts`) keep their existing field name — those interfaces are not commander- shaped, so `noStats` is the correct name there. ## Regression tests Two new unit tests in `test/unit/ai-context.test.ts`: * `omits volatile counts when noStats option is set (#1477)` — asserts the count parenthetical is absent from both CLAUDE.md and AGENTS.md when `noStats: true` is passed. * `preserves volatile counts when noStats is not set (default)` — documents the default-on path so a future refactor can't silently flip the default. Both call `generateAIContextFiles` directly with distinctive numbers that would unmistakably leak through if the omit branch is broken. ## Manual verification * `vitest run test/unit/ai-context.test.ts` → 13/13 pass (11 prior + 2 new). * Verified before-fix behaviour by checking out main, running `npx gitnexus analyze --no-stats` against an indexed repo, and observing the count phrase still present. Re-running on the fix branch with the same flag strips the phrase as documented. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(cli): resolve merge conflict markers in analyze.ts (PR #1478) Remove leftover conflict hunks from main merge; keep commander stats shape (stats?: boolean), wire noStats: options?.stats === false into runFullAnalysis and generateAIContextFiles, and retain indexOnly / skipSkills / skipAgentsMd wiring from main. Co-authored-by: Cursor <cursoragent@cursor.com> * test(cli): cover analyzeCommand → runFullAnalysis noStats bridge (#1477) Assert commander-shaped options.stats maps to the internal noStats payload (including explicit true/false and skipAgentsMd combination) so the CLI bridge cannot regress without failing tests. Co-authored-by: Cursor <cursoragent@cursor.com> * test(cli): cover AGENTS.md default stats + skills noStats bridge (#1478) - Assert volatile stats phrase in both CLAUDE.md and AGENTS.md when noStats is omitted - Add bridge test for --skills regeneration path with stats:false → generateAIContextFiles noStats - Note shared noStats expression beside skills-path call; stub process.exit for full analyze path Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
6229417bd5
|
feat: gitnexus:keep marker preserves custom context sections (resubmit of #605) (#1508)
* feat: gitnexus:keep marker preserves custom context sections When <!-- gitnexus:keep --> is present inside the gitnexus block, analyze only updates the stats line instead of replacing the entire section with the verbose template. Lets users maintain lean custom context without it being overwritten on every reindex. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: improve gitnexus:keep marker to reliably preserve custom sections The `<!-- gitnexus:keep -->` marker inside a GitNexus block tells `analyze` to only update the stats line (node/edge/flow counts) while preserving the user's custom layout. This lets teams trim the verbose default template to a lean format without having it overwritten on every reindex. Changes: - Broaden stats-line regex to match both "Indexed as" and "indexed by GitNexus as" formats - Improve stats extraction from generated content (prefer structured match over greedy parentheses) - If keep marker is present but no stats line found, preserve the section as-is instead of falling through to full replace - Add tests for keep preservation and no-keep replacement Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR #1508 review findings (F1-F5) Refactor the keep-marker stats-update path and close the test-coverage gaps surfaced by the production-readiness review. ## Findings 2 + 3 (high) — fragile extraction → silent corruption Stop re-extracting `newName` (first `**bold**`) and `newStats` (first `(...)`, with fallback) from generated content. Both are structurally fragile: - F2: newName silently picks the wrong value if the template ever emits bold text before the project-name line (no current bug; an unstated contract with no enforcement) - F3: newStats fallback `\(([^)]+)\)` matches `({target: "symbolName", direction: "upstream"})` from the Always-Do bullet when `noStats: true` suppresses the canonical stats line, silently corrupting the stats output Fix: pass `projectName: string` and `stats: RepoStats` directly into `upsertGitNexusSection`. Build the stats line from those values. Both callers in `generateAIContextFiles` already have them in scope. ## Finding 1 (high) — misleading return value When a keep marker is present but no stats line matches the pattern, the function previously returned `'updated'` without writing, producing `CLAUDE.md (updated)` in CLI output for a file that was not touched. Add a distinct `'preserved'` return variant; CLI now reports `CLAUDE.md (preserved)` honestly. ## Finding 4 (medium) — unanchored stats regex `/(?:Indexed as|...) \*\*[^*]+\*\* \([^)]+\)/` could match prose embedded mid-paragraph in user content (e.g. "you'll see it Indexed as **Foo** (note: ...)"). Anchor with `^...$` plus the `m` flag so only standalone stats lines match. ## Finding 5 — test coverage gaps Seven new tests, each cross-referenced to the review finding: - keep marker OUTSIDE the GitNexus section has no effect - AGENTS.md keep path preserves custom layout (parity with CLAUDE.md) - idempotent: second run produces byte-identical output - CRLF file with keep marker: stats line updates correctly - noStats + keep marker: not corrupted by Always-Do tuple text (F3 regression guard) - returns 'preserved' (not 'updated') when no stats line matches (F1 regression guard) - project name with markdown punctuation (hyphens/slash/dot) lands intact All 23 ai-context tests pass; typecheck, prettier, eslint clean. * docs(ai-context): address PR #1508 review findings on keep-marker path - Clarify that noStats affects generated template only, not keep-section stats updates - Fix stats-line regex comment to match behavior (no end anchor; trailing suffix kept) - Assert '. MCP tools.' survives stats replacement in preserve-custom-section test - Document LF normalization when rewriting CRLF seed in keep-marker CRLF test Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: dp-web4 <dp@web4.ai> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
88d3df77cc
|
feat:(wiki) added --timeout and --retries flags for large module pages to mitigate timeout aborts (#1543)
* feat:(wiki) added --timeout and --retries flags for large module pages to mitigate timeout aborts * docs(wiki): document --timeout and --retries options * docs(wiki): document --timeout and --retries in SKILL.md --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
4cc4e9c84b
|
fix(build): use platform-aware tsc command for win32 (#1531) | ||
|
|
48cd55a120
|
fix(search): guard against undefined bm25Results when FTS unavailable (#1489) (#1540)
* fix(search): guard against undefined bm25Results when FTS unavailable (#1489) When the FTS extension is unavailable in the MCP process, searchFTSFromLbug can return an unexpected shape or throw, leaving bm25Results undefined. The for-loop then crashes with "bm25Results is not iterable". - mergeWithRRF: default both inputs via ?? [] so undefined never reaches the iteration loops - hybridSearch: wrap searchFTSFromLbug in try/catch and fall back to semantic-only search instead of crashing - local-backend query handler: guard bm25SearchResult?.results and semanticResults with ?? [] - bm25Search: wrap the dynamic import in try/catch for sandboxed MCP contexts; guard ftsResponse?.results Adds 6 regression tests covering undefined inputs and FTS failure fallback. Fixes #1489 * fix(search): address review findings on #1489 crash guards - Guard ftsResponse.results with ?? [] in hybridSearch (Finding 1) - Add logger.warn on bm25-index.js import failure (Finding 3) - Add unit test for callTool query FTS throw path (Finding 2) --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
e8c8ddec8a
|
fix(wiki): sanitize generated mermaid diagrams (#1539)
* fix(wiki): sanitize generated mermaid diagrams * fix(wiki): address mermaid sanitizer review --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
ec4624af87
|
fix(hooks): cap concurrent augment subprocesses (#1486) (#1510)
* fix(hooks): cap concurrent augment subprocesses to prevent runaway process spawn (#1486) When Claude Code fires PreToolUse hooks for parallel Grep/Glob/Bash tool calls, each invocation spawned its own `gitnexus augment` subprocess — a Node + LadybugDB cold start that holds resources for several seconds. Under heavy parallel search load (issue #1486: 180+ piled-up processes, load avg > 100), these accumulated faster than they completed because nothing capped concurrent in-flight augments. Add a lockfile-based concurrency guard under `<.gitnexus>/.hook-locks/`: each running hook claims a `<pid>.lock`, the guard counts live PIDs and prunes stale entries (>30s mtime or pid no longer alive), and bails silently when MAX_INFLIGHT (3) is reached. Augment is best-effort enrichment — missing a few fires under burst load is preferable to melting the system. Applied to all three hook variants that spawn augment: - gitnexus/hooks/claude/gitnexus-hook.cjs (npm-installed Claude hook) - gitnexus-claude-plugin/hooks/gitnexus-hook.js (plugin Claude hook) - gitnexus-cursor-integration/hooks/gitnexus-hook.cjs (Cursor hook) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hooks): make augment concurrency cap a hard cap via atomic slot files Address Claude's review of #1510. The original count-then-claim guard had a TOCTOU window: N hooks could each read `active < MAX_INFLIGHT` between readdirSync and the per-pid `wx` write and all proceed, briefly exceeding the cap. The PR title's "cap" language overstated this. Replace with fixed-name `slot-0.lock` ... `slot-N.lock` under `.hook-locks/`. `O_CREAT|O_EXCL` on a fixed path is OS-atomic — exactly one process wins each slot, so the cap is hard regardless of burst arrival timing. Each slot file contains the owning PID so stale-takeover still works when a hook crashes without releasing. PID liveness is checked before age (Claude's Finding 3): a slow-but-alive hook is never wrongly evicted. The 30s age window only kicks in to defend against PID reuse on a long-abandoned slot, well above the 7s augment timeout so a healthy run never hits it. Also adds the missing concurrency-guard tests to cursor-hook.test.ts (Claude's Finding 2): source-level wiring + dead-PID reclaim + 3-slots-full bail. Previously only the CJS and Plugin variants had test coverage for the guard; the Cursor variant was validated only by code inspection. Tests: 5726 passing, +9 from baseline (1 hard-cap burst test + 4 source regressions in hooks.test.ts; 3 source + 2 integration in cursor-hook.test.ts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hooks): inspect slot mtime + content via single fd (codeql TOCTOU) CodeQL flagged the stale-takeover path in acquireHookSlot as a potential filesystem race (js/file-system-race): statSync(slotPath) followed by readFileSync(slotPath) gives a TOCTOU window where the file could be swapped between the metadata check and the content read. Replace the two separate path-based calls with a single openSync + fstatSync + readSync + closeSync sequence. Both mtime and owner PID now come from the same file descriptor, so the operations are atomic on one inode. No behavioral change beyond closing the race. Applied to all three hook variants (CJS, Plugin, Cursor). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hooks): distinguish EPERM from ESRCH in PID liveness check Cursor Bugbot caught a contradiction with the stated design: the bare `catch` after `process.kill(owner, 0)` was treating EPERM (process exists but owned by another user) the same as ESRCH (process gone), which would evict a live slot whenever the lock dir straddled user boundaries. Inspect the error code: ESRCH → dead, evict; EPERM → still alive, keep the slot; anything else → assume alive (be conservative under unexpected failure rather than over-evict). Applied to all three hook variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hooks): fail closed when lock dir cannot be created Previously the mkdirSync catch in acquireHookSlot returned `() => {}` (a truthy no-op). The caller checks `if (!release) return;` to skip augment when the guard can't be established — but a truthy no-op slipped through that check and let augment spawn unguarded. On a cross-user shared `.gitnexus/` or read-only filesystem, N concurrent hooks would each take that branch and reintroduce the #1486 fan-out the guard exists to prevent. Return `null` instead so the caller's `if (!release) return;` skips augment cleanly. Augment is best-effort enrichment — skipping it when the guard fails is strictly safer than running unguarded. Also clarify the stale-slot comment: PID-liveness wins for slots younger than HOOK_LOCK_STALE_MS, but age is the final arbiter beyond 30s (PID-reuse defense). The previous wording said "PID-liveness wins over age" without qualifying it, which contradicted the >30s branch. Add source-level regression tests in hooks.test.ts and cursor-hook.test.ts asserting acquireHookSlot returns null (not () => {}) on lock-dir failure. Note in the Cursor test file that the 10-spawner burst test is not duplicated because the algorithm is byte-for-byte identical to the CJS hook and already covered there. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(hooks): extract lock guard into helper modules Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/04dd20c5-28fd-433a-83cf-ad83fd03fb32 --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
6a23616873
|
chore(deps)(deps): bump protobufjs from 7.5.5 to 7.5.8 in /gitnexus (#1536) | ||
|
|
7637bd1c83
|
chore(deps)(deps): bump @protobufjs/utf8 in /gitnexus (#1535)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
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
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
|
||
|
|
8083c39f6d
|
feat(php): migrate PHP to scope-based resolution model (#938) [supersedes #1124] (#1497) | ||
|
|
a2f1b07700
|
fix: resolve TypeScript ESM .js extension imports to .ts source files (#1525)
* fix: resolve TypeScript ESM .js extension imports to .ts source files TypeScript ESM requires imports to use .js extensions even when source files are .ts (moduleResolution: node16/bundler). The import resolver now strips JS-family extensions (.js/.jsx/.mjs/.cjs) and retries with TS equivalents (.ts/.tsx/.mts/.cts) when the literal .js file does not exist. This fallback only applies to TypeScript/JavaScript languages. Also adds .mts/.cts to the EXTENSIONS list for completeness. Fixes #1503 * fix: address review findings — normalization, edge-case tests, integration test - Fix makeCtx to use production normalization (.replace backslash) instead of .toLowerCase() (Finding 3) - Add tests for .mjs/.cjs with competing .ts/.mts siblings (Finding 1) - Add tests for ./dir.js → dir/index.ts boundary (Finding 2) - Add integration test verifying full pipeline CALLS edges for ESM .js imports (Finding 4) - Document path alias .js limitation as known follow-up (Finding 5) * chore(autofix): apply prettier + eslint fixes via /autofix command * chore: retrigger CI after bot-only tip commit Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
0daae93701
|
fix(lbug): drain checkpoint result before close (#1506)
* fix(lbug): drain checkpoint result before close * test(lbug): cover checkpoint drain lifecycle * fix(lbug): close query results after reads * fix(lbug): close all stream query results * fix(lbug): harden query result cleanup --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
4fa40e9881
|
feat(analyze): incremental indexing (parse cache + DB writeback + scope-res short-circuit) (#1479)
* docs: incremental indexing design spec Captures the design agreed in brainstorming on 2026-05-10: - Transitive importer closure with public-surface-change optimization - Git-only change detection (non-git repos: full rebuild as today) - New default behavior; --force opts out - New hydratePhase + loadGraphFromLbug primitive - Iterative closure expansion with parseCache reuse - incrementalInProgress dirty flag for crash recovery Prior art: PR #592 (zenprocess), PR #533 (davidbeesley), PR #1146 (azeemshaik025) — referenced and credited. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(communities): seed Leiden RNG for deterministic community detection The vendored Leiden algorithm defaults to Math.random for tie-breaking and randomized walks, which produces non-deterministic community assignments and modularity values across runs on the same graph. Pass a seeded mulberry32 RNG (LEIDEN_SEED=0xC0DE) so: - The same graph always produces the same partition - Modularity values are reproducible - Equivalence tests for incremental indexing can compare community assignments byte-for-byte This is foundational for the upcoming incremental-indexing feature (see docs/superpowers/specs/2026-05-10-incremental-indexing-design.md) where the correctness contract is incremental output ≡ full rebuild output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(incremental): change-detection, surface signatures, closure expansion Three new modules supporting the incremental-indexing pipeline: * core/incremental/git-diff.ts — getChangedFilesSinceCommit() unions 'git diff lastCommit HEAD' (committed) with 'git status --porcelain' (dirty tree). Renames flattened to delete(orig) + add(new). Throws LastCommitMissingError when lastCommit is gone (caller falls back to full rebuild). * core/incremental/surface.ts — extractSurfaceSignature() produces a stable hash of a file's publicly-visible symbols (functions, classes, methods, interfaces, types, heritage). Body-only edits → same hash. Signature/heritage changes → different hash. Drives the closure scoping optimization. * core/incremental/closure.ts — computeImporterClosure() iterative fixpoint: parse each closure file, extract surface, query DB importers, expand. Uses a parseCache so each file is parsed once. Generic over TParseResult so closure logic is decoupled from the pipeline's parse representation. 32 unit tests across the three modules. Tests cover edge cases: clean tree, dirty-only, mixed, renames, deletes, multi-hop cascade, cycle termination, surface invariance, etc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(lbug): loadGraphFromLbug, queryImporters, deleteAllCommunitiesAndProcesses Three new primitives in lbug-adapter.ts to support incremental indexing: * loadGraphFromLbug(graph, unchangedFilePaths) — streams all nodes for files in the set across every hydratable node table (excludes Community/Process — graph-wide, regenerated downstream). Then loads edges where both endpoints belong to loaded nodes, excluding MEMBER_OF / STEP_IN_PROCESS edges (also graph-wide). FilePaths chunked at 200 per query to keep statement size bounded on huge repos. Endpoint-level join filters by source-side filePath in the query, target-side checked JS-side via the loadedNodeIds set. * queryImporters(targetFilePath) — returns DISTINCT a.filePath where a -[IMPORTS]-> b and b.filePath = target. Powers closure expansion: when a changed file's surface signature changes, all its importers must be re-parsed. * deleteAllCommunitiesAndProcesses() — drops Community/Process nodes (and their edges via DETACH DELETE) at the start of each incremental run so the communities/processes phases regenerate them from the fully-merged graph. Required for the 'Leiden runs on full graph' correctness invariant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(pipeline): hydrate phase + parse-filter for incremental indexing Wires the incremental-indexing infrastructure into the phase-based pipeline. Three coordinated changes: * New hydratePhase (deps: structure) — loads node/edge state for files OUTSIDE ctx.options.filesToParse from the existing LadybugDB index. Runs before parse so the parse phase can produce a partial graph while downstream phases (mro, communities, processes) still see the full graph. No-op in full-rebuild mode (filesToParse unset). * PipelineOptions.filesToParse: optional ReadonlySet<string>. When set, parse phase filters scanned files to this set; hydrate fills the complement. Set by runFullAnalysis when it detects an eligible incremental run; never set by callers directly. * gitnexus-shared PipelinePhase enum: 'hydrate' added so progress callbacks can report the new phase distinctly from 'structure'. Phase order: scan → structure → hydrate → markdown,cobol → parse → routes,tools,orm → crossFile → scopeResolution → mro → communities → processes. Communities (Leiden) still runs on the full graph, satisfying the correctness invariant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(analyze): incremental orchestrator branch + meta schema Wires incremental indexing into runFullAnalysis. Highlights: * RepoMeta schema extended: schemaVersion, surfaceSignatures, and incrementalInProgress fields. INCREMENTAL_SCHEMA_VERSION = 1. * core/incremental/file-hash.ts — v1 surface signature: SHA-256 of file content. v2 will switch to a true surface-only signature (defined in surface.ts) so body-only edits don't expand the closure. The plumbing is signature-agnostic so the swap is local. * core/incremental/orchestrator.ts — eligibility check, closure computation (uses file-hash as the surface signal), dirty-flag management, subgraph extraction, signature merge. * run-analyze.ts adds: - hasDirtyTree() check on the existing 'lastCommit==HEAD' early-exit so an uncommitted edit triggers re-index (was a coarse equality check before). - incremental branch: try incremental first; fall through to full rebuild on any setup failure or eligibility miss. - runIncrementalBranch() — opens existing DB, deletes closure-file rows + Community/Process, runs pipeline with filesToParse, writes only the changed-subgraph back, refreshes FTS, updates meta with new surfaceSignatures and clears the dirty flag. - Full-rebuild path now populates surfaceSignatures + schemaVersion in meta.json so the next run is eligible for incremental. Crash recovery: incrementalInProgress is set BEFORE any DB mutation and cleared on success by overwriting meta.json. A crash anywhere in between leaves the flag set, and the next analyze run forces a full rebuild (cheapest path back to a known-good index). v1 limitation documented: body-only edits trigger 1-hop closure expansion (content-hash signal). True surface-only optimization is deferred to v2 — see design doc for the integration path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(incremental): drop invalid --no-renames=false from git diff The flag --no-renames=false isn't valid git syntax (it's parsed as a file path). Git's default rename detection is on; removing the flag keeps that behavior. Caught while running an end-to-end smoke test against a small fixture repo: incremental setup failed with 'Command failed: git diff --name-status -z --no-renames=false ...'. After the fix, the incremental path runs cleanly: closure is computed, hydrate phase loads unchanged-file state from DB, parse phase only re-parses files in closure, and the writeback updates only changed nodes/edges. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert v1 incremental indexing (5 commits) Reverts the v1 design that parsed only closure files into a fresh graph and tried to hydrate the rest from DB. Real-repo equivalence test failed: cross-file resolution operates on partial parse data (closure files only), so CALLS edges that resolve through unchanged files silently fall off. Diff against full rebuild on the same edited state: -50 nodes, -425 edges, -5 communities, -48 processes. Architecture pivot: switch to PR #533-style content-addressed parse cache. Pipeline parses every file (cache-served when possible), giving cross-file resolution full data, with DB writeback then restricted to changed-file rows. Reverts: |