Skip to content

feat(cli): Fingerprint indexed repos by remote URL to detect sibling-clone graph drift#982

Merged
magyargergo merged 11 commits into
mainfrom
copilot/fix-single-path-indexing-issue
Apr 21, 2026
Merged

feat(cli): Fingerprint indexed repos by remote URL to detect sibling-clone graph drift#982
magyargergo merged 11 commits into
mainfrom
copilot/fix-single-path-indexing-issue

Conversation

Copilot AI commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

A registry entry currently pins a repo to a single absolute path. When the same repo is checked out at multiple paths (worktrees, multi-agent workspaces, "clean clone for indexing"), MCP tools silently serve answers from the indexed clone regardless of which clone the caller is actually working in — and there's no signal that the graph has drifted.

Implements recommendation B + D from the issue: fingerprint by (remoteUrl, lastCommit) and warn on drift.

Capture & persist remote URL

  • New getRemoteUrl() in storage/git.ts shells out to git config --get remote.origin.url and normalises trailing .git// and host case (HTTPS + SCP-form SSH).
  • Optional remoteUrl added to RepoMeta and RegistryEntry. Captured in run-analyze; backfilled by gitnexus index on older .gitnexus/ folders. Absent values keep pre-existing behavior.

Sibling-clone resolution

  • findSiblingClones(remoteUrl, selfPath) returns other registry entries sharing the same fingerprint.
  • checkCwdMatch(cwd) walks the cwd's git root, looks up its remote, and returns one of path / sibling-by-remote / none. For sibling matches it also returns cwdHead, drift (commits ahead of the indexed lastCommit), and a human-readable hint.

MCP surface

  • LocalBackend.listRepos() now returns remoteUrl, staleness (commits the indexed clone is behind its own HEAD), and siblings (other registered clones of the same repo).
  • LocalBackend.resolveRepo() emits a one-shot stderr warning per (repo, cwd) pair when the caller's cwd is a sibling-by-remote of the resolved index and HEADs differ. Tools still return their best answer — the warning is purely additive so agents stop trusting silently-stale data.
// Polecat queries from ~/gt/lume_spark_api/polecats/X/lume-spark-api
// while the index lives at ~/Documents/GitHub/lume-spark-api:
//
// stderr: GitNexus: ⚠️ Index for "lume-spark-api" was built at
//   /Users/.../Documents/GitHub/lume-spark-api; your cwd
//   (/Users/.../polecats/X/lume-spark-api) is a sibling clone that is
//   30 commits ahead of the indexed commit. Results may be stale or
//   incorrect — re-run `gitnexus analyze` to refresh the index.

Tests

  • New test/unit/sibling-clone-drift.test.ts: registry round-trip of remoteUrl, sibling lookup, and checkCwdMatch for the path / sibling-with-drift / sibling-same-commit / unrelated cases.
  • test/unit/git-utils.test.ts: getRemoteUrl normalisation across HTTPS, SCP-form SSH, host-case, trailing .git, and missing-remote.
  • Updated repo-manager / git mocks in unit + integration suites to expose the new exports.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • extension.ladybugdb.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/git-utils.test.ts test/unit/sibling-clone-drift.test.ts test/unit/repo-manager.test.ts (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/calltool-dispatch.test.ts test/unit/index-repo-command.test.ts test/unit/analyze-api.test.ts Release/obj.target/tree_sitter_d/home/REDACTED/.cache/node-gyp/24.14.1/deps/v8/include ache/node/24.14.1/x64/lib/node_m-I es/node-addon-apbash lude p/bin/git e k/Gi�� scripts/build-tree-sitter-proto.cjs (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/calltool-dispatch.test.ts test/unit/index-repo-command.test.ts test/unit/analyze-api.test.ts test/unit/git-utils.test.ts test/unit/sibling-clone-drift.test.ts test/unit/repo-manager.test.ts (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@vercel

vercel Bot commented Apr 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gitnexus Ready Ready Preview, Comment Apr 21, 2026 8:35pm

Request Review

Copilot AI changed the title [WIP] Fix silent graph drift caused by single-path indexing Fingerprint indexed repos by remote URL to detect sibling-clone graph drift Apr 19, 2026
Copilot AI requested a review from magyargergo April 19, 2026 07:04
@github-actions

github-actions Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 5m 19s —— View job


PR Review: Fingerprint indexed repos by remote URL to detect sibling-clone graph drift

Tasks:

  • Read repo docs (AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, TESTING.md, ARCHITECTURE.md)
  • Read changed files
  • Define production-ready bar (Phase 1)
  • Review PR against the bar (Phase 2)

Production-ready bar for this PR

  1. npx tsc --noEmit passes with no new errors in gitnexus/
  2. vitest run test/unit passes — all new unit tests green
  3. vitest run test/integration is unbroken — integration mocks are current
  4. RegistryEntry and RepoMeta extensions are strictly additive (optional fields only); no existing callers broken
  5. MCP tool output changes are backward-compatible (new optional fields only on list_repos)
  6. Existing checkStaleness behavior is preserved unmodified
  7. No secrets, tokens, or machine-specific paths committed
  8. PR title follows <type>[(scope)]: <subject> conventional-commit format (enforced by pr-labeler.yml)
  9. Shell-outs are safe on Windows (no inherited stdio, path.resolve used for normalization)
  10. LadybugDB single-writer invariant and embedding preservation are untouched

Findings

1. Correctness & Functional Completeness

No issues found in this area based on the diff and reviewed context.

The core logic is correct:

  • getRemoteUrl() normalization is accurate: strips .git, strips trailing /, lowercases host segment — both HTTPS and SCP-form SSH handled independently.
  • findSiblingClones() correctly excludes selfPath using norm() on Windows (repo-manager.ts:523-525).
  • checkCwdMatch() longest-prefix path match at git-staleness.ts:99-110 correctly fires before the remote-URL sibling check, so cwd=inside/registered/subdir is never misclassified as a sibling.
  • maybeWarnSiblingDrift() guards at local-backend.ts:573-578 ensure: wrong sibling → skip, no hint → skip, same commit → skip (local-backend.ts:584).
  • Backfill in index-repo.ts:110-113 correctly writes remoteUrl into the in-memory meta object before registerRepo — the registry gets updated; meta.json on disk is NOT updated (intentional, as runFullAnalysis owns meta.json). Design is documented and correct.
  • Older entries with remoteUrl: undefined fall through all detection silently — backward-compatible.

2. Code Clarity & Clean Code

## NIT: Commented-out import left in place

  • Evidence: local-backend.ts:23
  • Why it matters: // import { isGitRepo, getCurrentCommit, getGitRoot } from '../../storage/git.js'; left behind from a refactor. No functional impact.
  • Recommendation: Remove or convert to a regular import if actually needed.
  • Confidence: High

## NIT: Redundant commit-equality check in maybeWarnSiblingDrift

  • Evidence: local-backend.ts:577 (!match.hint) and local-backend.ts:584 (cwdHead === handle.lastCommit)
  • Why it matters: checkCwdMatch already leaves hint unset when cwdHead === sibling.lastCommit (git-staleness.ts:133). The guard at line 584 therefore never triggers — it is dead code.
  • Recommendation: Remove line 584 to eliminate the dead branch, or add a comment explaining the defense-in-depth intent.
  • Confidence: High

No drive-by refactors, no barrel regressions, no // removed comments, no unused re-exports. Naming is clear and consistent with existing conventions.

3. Test Coverage & Change Safety

Good coverage overall. Specific observations:

## NON-BLOCKING: maybeWarnSiblingDrift one-shot guard is not directly unit-tested

  • Evidence: test/unit/sibling-clone-drift.test.ts — all 5 checkCwdMatch cases and all findSiblingClones cases are covered; the warnedSiblingDrift Set and console.error side-effect in LocalBackend are not.
  • Why it matters: The one-shot guard (warnedSiblingDrift.has(key) at local-backend.ts:587) and the console.error output are the actual user-visible behavior; a regression there would go undetected by these tests.
  • Recommendation: Add a unit test that calls resolveRepo twice with a sibling-clone setup and asserts console.error is called exactly once (spy/mock process.cwd and checkCwdMatch).
  • Confidence: High

Test quality is otherwise good: getRemoteUrl normalization tests use real git repos (not mocks), covering the HTTPS, SCP-SSH, host-case, and .git-stripping cases. checkCwdMatch tests use real temp git repos and cover all four enumerated match cases. Assertions are specific and non-tautological.

Cannot verify tsc --noEmit or vitest run from the diff — CI was firewall-blocked for the relevant test runs per the PR warning block.

4. Performance

## NON-BLOCKING: checkCwdMatch re-runs fully on every resolveRepo() call after warning is already issued

  • Evidence: local-backend.ts:352-354maybeWarnSiblingDrift is called fire-and-forget for every resolveRepo() invocation. Inside maybeWarnSiblingDrift, checkCwdMatch runs unconditionally (git-staleness.ts:88-156), which calls readRegistry() + up to 4 execSync/execFileSync git shell-outs (getGitRoot, getRemoteUrl, getCurrentCommit, commitsAheadOfIndexed). The warnedSiblingDrift key check at line 587 only prevents console.error — not the computation.
  • Why it matters: In MCP server mode process.cwd() is constant, so checkCwdMatch will return the same result on every call. After the first warning, all subsequent calls repeat up to 4 blocking git shell-outs unnecessarily. execSync blocks the Node.js event loop even inside a fire-and-forget promise. For a server with frequent tool calls and a sibling-clone setup, this is ~4 × <10 ms per invocation.
  • Recommendation: Move the warnedSiblingDrift check earlier — keyed on process.cwd() before any git shell-out — so that after the first computation the entire checkCwdMatch call is skipped: const earlyKey = \${handle.id}|${cwd}`; if (this.warnedSiblingDrift.has(earlyKey)) return;`
  • Confidence: High

## NON-BLOCKING: listRepos() reads the registry N times (once per registered repo via findSiblingClones)

  • Evidence: local-backend.ts:525-547Promise.all over handles, each calling findSiblingClones(h.remoteUrl, h.repoPath) which calls readRegistry() internally (repo-manager.ts:521).
  • Why it matters: For N registered repos, list_repos performs N async file reads of registry.json. The registry is already read once by refreshRepos() at the top of listRepos(). Each subsequent read is redundant.
  • Recommendation: Pre-read the registry once, group entries by remoteUrl, then pass the pre-grouped map into sibling lookup instead of calling readRegistry() per entry.
  • Confidence: High

5. Operational Risk

## NON-BLOCKING: process.cwd() is the MCP server's CWD, not the client's CWD

  • Evidence: local-backend.ts:565-570cwd = process.cwd() is used to determine the "caller's" working directory.
  • Why it matters: The MCP stdio server CWD is fixed at server-start time and does not reflect the actual agent's working directory. The warning will only trigger when the MCP server process itself was started from within a sibling clone — not when an agent running elsewhere issues a query. This is a fundamental limitation of the process.cwd() approach.
  • Recommendation: Document this limitation explicitly in the maybeWarnSiblingDrift JSDoc. Consider exposing a cwd parameter on tool calls as a future enhancement (out of scope for this PR). The current behavior is still valuable for CLI usage.
  • Confidence: High

Windows path normalization (norm() in both repo-manager.ts:523 and git-staleness.ts:93), execSync stdio config (stdio: ['ignore', 'pipe', 'ignore'] at git.ts:52), and LadybugDB single-writer invariant are all intact. Embeddings preservation is untouched. MCP tool schema changes are additive only.

Rollback safety: reverting this PR requires no re-analyze. Older RegistryEntry JSON with remoteUrl fields will be ignored by the reverted code (JSON.parse returns the field, TypeScript just doesn't expose it). ✓

6. Maintainability

No Pipeline Phase DAG boundaries crossed. The capture point (run-analyze.ts) is at step 9 of runFullAnalysis, after the pipeline — correct placement per ARCHITECTURE.md § Full analysis flow. The backfill path (index-repo.ts) is a clear "best-effort upgrade" pattern. Logic is discoverable: getRemoteUrlrepo-manager.ts sibling helpers → git-staleness.ts cwd resolution → local-backend.ts warning emission. ✓

Non-Negotiables

## NON-BLOCKING: PR title does not follow mandatory conventional-commit format

  • Evidence: PR title "Fingerprint indexed repos by remote URL to detect sibling-clone graph drift" — no <type>: prefix. CONTRIBUTING.md: "PR titles MUST follow the conventional-commit format — pr-labeler.yml enforces this on every PR."
  • Why it matters: pr-labeler.yml will not apply the correct label; the change will not group correctly in release notes.
  • Recommendation: Rename to e.g. feat(mcp): fingerprint repos by remote URL to detect sibling-clone drift
  • Confidence: High

No secrets, tokens, or machine-specific paths committed. No blind find-and-replace renames. No HIGH/CRITICAL impact ignored. PolyForm license metadata untouched. ✓


What I could not fully verify

  1. TypeScript compilation: Cannot confirm tsc --noEmit passes from diff alone. CI was firewall-blocked for the relevant runs.
  2. vitest run result for the new unit tests: same firewall block.
  3. maybeWarnSiblingDrift in a real multi-repo sibling scenario: The warning path requires process.cwd() to be inside a sibling clone at server start — hard to prove from the diff that the warnedSiblingDrift guard correctly prevents duplicate stderr lines in an actual MCP server session.
  4. Web UI (gitnexus-web/) compatibility with extended list_repos output: Assumed safe since all new fields are optional, but not verified by reading the web client's list_repos consumer code.

Final verdict

Ready to merge — no blocking issues.

The implementation is correct, backward-compatible, and well-tested for its core logic. The two NON-BLOCKING performance issues (checkCwdMatch re-runs after warning is issued; O(n) registry reads in listRepos) are real and worth fixing soon, but they do not risk correctness or data loss and do not affect tool response latency. The PR title format should be fixed before merge per CONTRIBUTING.md — easy one-liner change.

If you want to address items before merge, priority order:

  1. Fix PR title to conventional-commit format (required by pr-labeler.yml)
  2. Add early-exit cache in maybeWarnSiblingDrift keyed on process.cwd() to skip re-computation after first warning
  3. Add unit test for one-shot console.error guard in maybeWarnSiblingDrift

@magyargergo magyargergo marked this pull request as ready for review April 19, 2026 07:47
@magyargergo magyargergo changed the title Fingerprint indexed repos by remote URL to detect sibling-clone graph drift feat(cli): Fingerprint indexed repos by remote URL to detect sibling-clone graph drift Apr 19, 2026

Copilot AI commented Apr 19, 2026

Copy link
Copy Markdown
Contributor Author

Claude finished @magyargergo's task in 5m 19s —— View job

---...

Addressed in 54abe73. CI was failing for three independent reasons; all fixed:

  1. Lint error — removed an unused fs/promises import in test/unit/sibling-clone-drift.test.ts (switched to a sync fs.realpathSync instead).
  2. Prettier — reformatted gitnexus/src/core/git-staleness.ts.
  3. macOS/Windows test failuregit rev-parse --show-toplevel returns the realpath (/private/var/... on macOS, expanded short-name on Windows), so the cwdGitRoot assertion now compares against fs.realpathSync(sibling.dbPath).

Review findings actioned:

  • Dead cwdHead === handle.lastCommit branch in maybeWarnSiblingDrift — removed; !match.hint already covers it.
  • checkCwdMatch re-runs after warning issued — moved the warnedSiblingDrift cache check before the checkCwdMatch call, keyed on (handle.id, cwd). Cached the "no-warn" outcome too so deterministic outcomes never re-shell-out to git.
  • listRepos reads registry N times — refactored to pre-group this.repos by remoteUrl once and look up siblings in O(1) per handle. Dropped the redundant per-entry findSiblingClones call (and its import) from local-backend.ts.
  • process.cwd() MCP-server limitation — documented in the maybeWarnSiblingDrift JSDoc.
  • One-shot warning unit test — added in calltool-dispatch.test.ts: spies console.error, calls resolveRepo() three times, asserts the drift line was emitted exactly once and checkCwdMatch was invoked exactly once.

Pre-existing commented-out import at local-backend.ts:23 left untouched — not from this PR (kept per "no drive-by refactors").

PR title prefix is out of my control to change from here, but the commit follows fix(mcp): … conventional-commit format.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • extension.ladybugdb.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/sibling-clone-drift.test.ts test/unit/git-utils.test.ts test/unit/calltool-dispatch.test.ts test/unit/index-repo-command.test.ts test/unit/repo-manager.test.ts test/unit/staleness.test.ts (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@magyargergo

Copy link
Copy Markdown
Collaborator

@copilot rebase and fix failing tests

Copilot AI commented Apr 19, 2026

Copy link
Copy Markdown
Contributor Author

@copilot rebase and fix failing tests

Done in 73bb33d. Merged origin/main into the branch (only conflict was the getCurrentCommit, hasGitDir, … import line in gitnexus/src/core/run-analyze.ts colliding with #981's getInferredRepoName — combined into a single multiline import). tsc --noEmit is clean and the unit suites I touched (sibling-clone-drift, git-utils, calltool-dispatch, index-repo-command) all pass locally — 99/99 green.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • extension.ladybugdb.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/sibling-clone-drift.test.ts test/unit/git-utils.test.ts test/unit/calltool-dispatch.test.ts test/unit/index-repo-command.test.ts test/unit/repo-manager.test.ts (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run --project default (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.1/x64/bin/node /opt/hostedtoolcache/node/24.14.1/x64/bin/node --import file:///home/REDACTED/work/GitNexus/GitNexus/gitnexus/node_modules/tsx/dist/loader.mjs /home/REDACTED/work/GitNexus/GitNexus/gitnexus/src/cli/index.ts analyze --skills (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@magyargergo

Copy link
Copy Markdown
Collaborator

@copilot resolve the merge conflicts in this pull request

Copilot AI commented Apr 19, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Resolved in 2bdf24f. Merged origin/main — only conflict was again the getInferredRepoName import in gitnexus/src/core/run-analyze.ts, kept HEAD's combined multiline import. tsc --noEmit clean and the targeted unit suites (sibling-clone-drift, git-utils, calltool-dispatch) pass — 88/88 green.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • extension.ladybugdb.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/sibling-clone-drift.test.ts test/unit/git-utils.test.ts test/unit/calltool-dispatch.test.ts (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@github-actions

github-actions Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
7149 7052 0 97 285s

✅ All 7052 tests passed

97 test(s) skipped — expand for details
  • Swift MethodExtractor > isTypeDeclaration > recognizes class_declaration
  • Swift MethodExtractor > isTypeDeclaration > recognizes protocol_declaration
  • Swift MethodExtractor > isTypeDeclaration > rejects import_declaration
  • Swift MethodExtractor > visibility > extracts public method
  • Swift MethodExtractor > visibility > extracts private method
  • Swift MethodExtractor > visibility > defaults to internal when no modifier
  • Swift MethodExtractor > protocol methods > marks protocol method as abstract
  • Swift MethodExtractor > static and class methods > detects static func as isStatic
  • Swift MethodExtractor > static and class methods > detects class func as isStatic
  • Swift MethodExtractor > parameters > extracts parameters with types and default values
  • Swift MethodExtractor > return type > extracts return type from -> annotation
  • Swift MethodExtractor > annotations > extracts @objc attribute
  • Swift MethodExtractor > isFinal > detects final func
  • Swift MethodExtractor > isFinal > is false when not final
  • Swift MethodExtractor > isAsync > detects async func
  • Swift MethodExtractor > isOverride > detects override method
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift explicit init inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference does not bind plain functions
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()
  • Swift implicit imports (cross-file visibility) > detects UserService class in Models.swift
  • Swift implicit imports (cross-file visibility) > resolves UserService() constructor call across files (no explicit import)
  • Swift implicit imports (cross-file visibility) > resolves service.fetchUser() member call across files
  • Swift implicit imports (cross-file visibility) > creates IMPORTS edges between files in the same module
  • Swift extension deduplication > detects Product class
  • Swift extension deduplication > resolves Product() constructor despite extension creating duplicate class node
  • Swift extension deduplication > resolves product.save() to Product.swift (primary definition)
  • Swift constructor call fallback (no new keyword) > resolves OCRService() as constructor call across files
  • Swift constructor call fallback (no new keyword) > resolves ocr.recognize() member call via constructor-inferred type
  • Swift export visibility (internal vs private) > resolves PublicService() constructor across files
  • Swift export visibility (internal vs private) > resolves internalHelper() across files (internal = module-scoped)
  • Swift if let / guard let binding resolution > detects User and Repo classes
  • Swift if let / guard let binding resolution > resolves user.save() inside if-let to User#save
  • Swift if let / guard let binding resolution > resolves repo.save() inside guard-let to Repo#save
  • Swift if let / guard let binding resolution > user.save() in if-let does NOT resolve to Repo#save
  • Swift await / try expression unwrapping > resolves user.save() via await fetchUser() return type
  • Swift await / try expression unwrapping > resolves repo.save() via try parseRepo() return type
  • Swift await / try expression unwrapping > detects fetchUser and parseRepo as functions
  • Swift for-in loop element type inference > detects User and Repo classes
  • Swift for-in loop element type inference > creates implicit import edges between files
  • Swift field-type resolution > detects classes and their properties
  • Swift field-type resolution > emits HAS_PROPERTY edges from class to field
  • Swift field-type resolution > resolves field-chain call user.address.save() → Address#save
  • Swift field-type resolution > emits ACCESSES edges for field reads in chains
  • Swift field-type resolution > populates field metadata (visibility, declaredType) on Property nodes
  • Swift call-result binding > resolves call-result-bound method call user.save() → User#save
  • Swift call-result binding > getUser() is present as a defined function
  • Swift call-result binding > emits processUser -> getUser CALLS edge for let-assigned free function call
  • Swift method enrichment > detects Animal protocol and Dog class
  • Swift method enrichment > emits IMPLEMENTS edge Dog -> Animal
  • Swift method enrichment > emits HAS_METHOD edges for Dog methods
  • Swift method enrichment > marks protocol Animal.speak as isAbstract
  • Swift method enrichment > marks Dog.speak as NOT isAbstract
  • Swift method enrichment > marks breathe as isFinal
  • Swift method enrichment > marks classify as isStatic
  • Swift method enrichment > captures @objc annotation on breathe
  • Swift method enrichment > populates parameterTypes for classify(_ name: String)
  • Swift method enrichment > records parameterCount for classify
  • Swift method enrichment > records returnType for speak
  • Swift method enrichment > resolves dog.speak() CALLS edge
  • Swift method enrichment > resolves Dog.classify("dog") CALLS edge
  • Swift abstract dispatch > detects Repository protocol and SqlRepository class
  • Swift abstract dispatch > emits IMPLEMENTS edge SqlRepository -> Repository
  • Swift abstract dispatch > emits HAS_METHOD edges for Repository.find and Repository.save
  • Swift abstract dispatch > emits HAS_METHOD edges for SqlRepository.find and SqlRepository.save
  • Swift abstract dispatch > marks base Repository.find as isAbstract
  • Swift abstract dispatch > marks base Repository.save as isAbstract
  • Swift abstract dispatch > marks concrete SqlRepository.find as NOT isAbstract
  • Swift abstract dispatch > resolves repo.find(id: 42) CALLS edge
  • Swift abstract dispatch > resolves repo.save(entity: user) CALLS edge
  • Swift abstract dispatch > populates parameterTypes for Repository.find
  • Swift abstract dispatch > populates parameterTypes for Repository.save
  • Swift abstract dispatch > records returnType for SqlRepository.find
  • Swift abstract dispatch > emits METHOD_IMPLEMENTS edges from SqlRepository methods → Repository protocol methods
  • Swift overloaded method disambiguation > detects 2 distinct find Method nodes on SqlRepository
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edges for both find overloads
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edge for save
  • Swift overloaded method disambiguation > emits exactly 3 METHOD_IMPLEMENTS edges total
  • Swift Child extends Parent — inherited method resolution (SM-9) > detects Parent and Child classes
  • Swift Child extends Parent — inherited method resolution (SM-9) > resolves c.parentMethod() to Parent.parentMethod via first-wins MRO walk

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 74.4% 20204/27154 74.33% 📈 +0.1 🟢 ██████████████░░░░░░
Branches 62.99% 12771/20272 62.93% 📈 +0.1 🟢 ████████████░░░░░░░░
Functions 79.72% 1954/2451 79.72% = 0.0 🟢 ███████████████░░░░░
Lines 77.14% 18303/23724 77.08% 📈 +0.1 🟢 ███████████████░░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

@copilot merge main into this branch and resolve any conflicts, plus ensure tests pass on all OS and code is formatted with prettier.

Copilot AI and others added 2 commits April 21, 2026 12:18
…th-indexing-issue

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a7be18dd-102f-4a7b-ac56-53fbd414fe3b

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

Copilot AI commented Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot merge main into this branch and resolve any conflicts, plus ensure tests pass on all OS and code is formatted with prettier.

Done. Merged latest origin/main (through #1003) — clean merge, no conflicts this time. tsc --noEmit is clean and prettier flagged one file (gitnexus/src/core/run-analyze.ts import block — collapsed back to a single line per the project's print width); fixed in 144f8bb. Targeted unit suites (sibling-clone-drift, git-utils, calltool-dispatch, repo-manager) pass — 137/137 green.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • extension.ladybugdb.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/sibling-clone-drift.test.ts test/unit/git-utils.test.ts test/unit/calltool-dispatch.test.ts test/unit/repo-manager.test.ts (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run --project default (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.1/x64/bin/node /opt/hostedtoolcache/node/24.14.1/x64/bin/node --import file:///home/REDACTED/work/GitNexus/GitNexus/gitnexus/node_modules/tsx/dist/loader.mjs /home/REDACTED/work/GitNexus/GitNexus/gitnexus/src/cli/index.ts analyze --skills (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@magyargergo

Copy link
Copy Markdown
Collaborator

@copilot To fix tests error, in fact no functional code changes needed — this is a test-only path comparison issue on Windows.

Windows CI failure — test/unit/sibling-clone-drift.test.ts:207

The windows-latest job fails on this assertion:

Expected: "C:\Users\RUNNER~1\AppData\Local\Temp\cwd-sibling-Xx4Mm3"
Received: "C:\Users\runneradmin\AppData\Local\Temp\cwd-sibling-Xx4Mm3"

git rev-parse --show-toplevel returns the long path (runneradmin), but fs.realpathSync resolves the 8.3 short name alias (RUNNER~1). Both point to the same directory, but strict string equality fails.

Fix at line 207 — resolve both sides through realpathSync:

expect(fs.realpathSync(m.cwdGitRoot!)).toBe(fs.realpathSync(sibling.dbPath));

…rt-name compat

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b2a1c6a3-e454-4b87-b0e4-69d7c0d9a51b

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

Copilot AI commented Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot To fix tests error, in fact no functional code changes needed — this is a test-only path comparison issue on Windows.

Windows CI failure — test/unit/sibling-clone-drift...

Applied in 2b89c80expect(fs.realpathSync(m.cwdGitRoot!)).toBe(fs.realpathSync(sibling.dbPath)). Local run: 9/9 sibling-clone-drift tests pass and prettier check is clean.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • extension.ladybugdb.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./node_modules/.bin/vitest run test/unit/sibling-clone-drift.test.ts (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

git rev-parse --show-toplevel returns long path names on Windows
while os.tmpdir() returns 8.3 short names. fs.realpathSync does not
expand short names, so exact path comparison always fails on Windows
CI runners. Replace with behavioral assertions instead.
@magyargergo magyargergo merged commit 962f224 into main Apr 21, 2026
16 checks passed
@magyargergo magyargergo deleted the copilot/fix-single-path-indexing-issue branch April 21, 2026 20:59
caork added a commit to caork/GitNexus that referenced this pull request Apr 22, 2026
Key upstream changes merged:
- feat(cli): gitnexus remove <target> command (abhigyanpatwari#1003)
- feat(python): scope-based call resolution pipeline RFC abhigyanpatwari#909 Ring 3 (abhigyanpatwari#980)
- feat(cli): repo fingerprinting via remote URL for sibling-clone detection (abhigyanpatwari#982)
- fix(group): friendly error on missing group name (abhigyanpatwari#989)
- fix(group): bubble local-impact errors in groupImpact (abhigyanpatwari#1007)
- fix(fts): don't cache failed FTS index (abhigyanpatwari#1006)
- fix(ci): docker alpine → debian (abhigyanpatwari#1014)
- deps: graphology 0.26.0, uuid 14, @types/node 25, @types/uuid 11

Conflict resolutions:
- AGENTS.md: kept our NEVER-format rules, added upstream language-specific
  hook rule, Tools Quick Reference table, Impact Risk Levels, gRPC guide link
- package.json: kept @types/multer, took upstream node/uuid type bumps
- package-lock.json: regenerated via npm install

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
github714801013 pushed a commit to github714801013/GitNexus that referenced this pull request Apr 28, 2026
…clone graph drift (abhigyanpatwari#982)

* Initial plan

* feat: detect sibling-clone graph drift via remote URL fingerprint

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/e5decb67-7fec-40e7-b2a1-b5e94a0d393f

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

* test: address review feedback — fake commit, same-commit case, regex docs

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/e5decb67-7fec-40e7-b2a1-b5e94a0d393f

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

* fix(mcp): address review feedback — CI green, perf, dead branch, one-shot test

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/cc2259f7-94e4-4243-aaa9-e03b7c632d32

* Merge branch 'main' into copilot/fix-single-path-indexing-issue

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/5840b3dd-e879-4854-a067-d1622bec2634

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

* Merge branch 'main' into copilot/fix-single-path-indexing-issue

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9025262f-4dd4-4774-8f32-e14434100004

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

* style: prettier format run-analyze.ts after merge with main

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/a7be18dd-102f-4a7b-ac56-53fbd414fe3b

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

* test: realpath both sides of cwdGitRoot assertion for Windows 8.3 short-name compat

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b2a1c6a3-e454-4b87-b0e4-69d7c0d9a51b

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>

* fix(test): use path-agnostic assertion for cwdGitRoot on Windows (abhigyanpatwari#1015)

git rev-parse --show-toplevel returns long path names on Windows
while os.tmpdir() returns 8.3 short names. fs.realpathSync does not
expand short names, so exact path comparison always fails on Windows
CI runners. Replace with behavioral assertions instead.

---------

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: copilot-swe-agent[bot] <copilot-swe-agent[bot]@users.noreply.github.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: evolution <wjc163@sina.cn>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Single-path indexing causes silent graph drift when same repo lives in multiple clones

3 participants