Skip to content

Keep repository detection off the main registration thread - #26760

Merged
nwparker merged 15 commits into
mainfrom
nwparker/win-issues-ipc-audit-local-1008
Oct 10, 2026
Merged

nwparker merged 15 commits into
mainfrom
nwparker/win-issues-ipc-audit-local-1008

Conversation

@nwparker

@nwparker nwparker commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 7 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​475 $\color{#cf222e}{\Huge{\mathbf{−}}}$​3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​472
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​175 $\color{#cf222e}{\Huge{\mathbf{−}}}$​25 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​150

ELI5

Adding a Git project lets unrelated Orca requests continue while Git identifies the repository.

What Changed

Local and paired-runtime registration await the existing Git runner. A single decision tree serves synchronous compatibility callers and async callers, preserving repository roots, linked worktree identity, bare repositories and metadata fallback. The existing Docker SSH lane includes registration coverage. Nested import and folder upgrades remain separate PRs.

Why

Sharing the classifier avoids maintaining parallel detection rules. The runner retains host environment preparation, WSL routing, admission and the same 15-second per-command limit. Earlier Windows timings (39.6–48.7 ms blocked IPC versus 2.5–5.8 ms response) belong to the original pre-split source; they are not final-head measurements.

Linked Issue

Related to #20497. Merge this before #26790 and #26791; the registration dedup fix #26789 is already on main.

Visual Proof

No layout change. Hidden-renderer captures below explain the previous CI failure: one populated host suppresses headers; the upstream fixture correction adds a real local project and retains the managed header assertion.

Controlled settled catalog before the upstream fixture correction

Final catalog recovery with both host sections

Fresh hidden Windows combined-stack run: actual WSL registration retains the literal dollar sign and space. Source-manifest and lockfile qualifications are recorded below.

Windows WSL registration with literal path characters

Testing

Final head 1fdc56bf558cd5bb9156124ae02e9e536f31f814, refreshed onto main b3aa719a57ca.

  • 242 focused tests pass, including 29 actual Git 2.25.1 cases; two platform cases skip. Nine additional cases cover unusual filenames, symlink identity, each rejected classifier stage, and generator failure handling. Independent review also compared against the pre-change synchronous classifier.
  • Fresh macOS hidden Electron: sequential and concurrent registration, real Docker SSH registration, and catalog-failure recovery pass (4); WSL skips here. Fresh main/renderer, CLI, relay and Docker orcad builds are used.
  • Full pnpm tc and changed-code quality pass. Fresh Windows combined validation merge 18159658119f1000e339200feaa8b3ad49f028e4 matches the expected tracked-file manifest; fresh install, CLI/Electron builds and four shipped hidden tests pass: native/concurrent registration, real WSL literal-dollar/space paths, and folder promotion. Two auxiliary checks also pass: native path/classifier parity with actual unavailable-Git metadata fallback, and linked routing with a WSL hint. Docker SSH skips on Windows. The banner capture utility is separate from production login-shell fallback.
  • CI failure 37902354341/job113731869253 is accounted for by upstream test(e2e): keep two host sections in the catalog-retry reconnect test #26838, imported through the forward main merge. The unchanged Mac test passed transiently; settling catalogs deterministically reproduced header count zero, then the same check passed with only the upstream local-project seed. No assertion weakening or retries were added.
  • Hidden app flows manually inspected
  • Automated tests added

Review

Low incremental code merge risk after independent review and path/failure parity checks. Metadata fallback still performs synchronous filesystem reads. The paired-runtime icon-discovery duplicate-registration race predates this change and remains separate. Real WSL path registration and the captured-shell utility test are distinct; production login-shell fallback has mocked runner coverage, not a claimed real-host fallback run. Final-head CI passes, including Windows packaging. The auxiliary Windows fixture later reported lockfile drift; its diff was unavailable, so exact post-install dependency reproducibility is unverified.

Agent skill upstream boundary

  • Not applicable; no upstream skill-installer source copied.

Checklist

  • Focused scope, mechanism and alternatives explained
  • Cross-platform, SSH, folder and wire compatibility considered
  • Focused tests, full typecheck, changed quality and fresh builds pass

Share repository discovery decisions across synchronous compatibility callers and the existing asynchronous Git runner. Await discovery in project registration, nested imports, and folder upgrades, and recheck folder ownership after discovery completes.
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

Repository detection now runs through shared command generators, with synchronous wrappers and new asynchronous APIs. Local and runtime registration use asynchronous Git checks. Tests cover repository layouts, command failures, linked worktrees, and local, SSH, and WSL registration.

















Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 1fdc5

The registration test does not confirm that the project appears in the sidebar. Add that assertion; no production failure is established.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 18.52% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 27 functions across 21 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly summarizes the main change: moving repository detection off the main registration thread through asynchronous execution.
Description check Passed The description is detailed and covers the required explanation, changes, rationale, issue reference, testing, review notes, compatibility considerations, and checklist. The visual-proof section does …

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR











  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nwparker nwparker changed the title Keep project discovery responsive while Git runs Keep repository detection off the main registration thread Oct 9, 2026
@nwparker
nwparker changed the base branch from main to nwparker/repository-registration-dedup-task1cc89eeb October 9, 2026 07:33
@nwparker
nwparker changed the base branch from nwparker/repository-registration-dedup-task1cc89eeb to main October 9, 2026 08:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔇 Additional comments (7)
src/main/ipc/repos/local-repo-registration.ts (1)

27-27: Concurrent registrations of the same path can both pass the dedupe check.

The code now awaits inspectGitRepoForRegistrationAsync and getGitRepoRootAsync before the store.getRepos() dedupe checks. Two concurrent calls for the same path both yield at these awaits. The existing raceWinner check after detectRepoIconAndUpstream runs synchronously up to store.addRepo. This keeps the race window closed, so no duplicate is added.

No change is required.

Also applies to: 59-61

src/main/git/repo-detection.ts (1)

240-266: LGTM!

src/main/git/repo.ts (1)

14-18: LGTM!

tests/e2e/repo-registration-async.spec.ts (1)

1-180: LGTM!

config/scripts/run-ssh-docker-e2e.mjs (1)

57-57: LGTM!

src/main/git/repo-detection-async.test.ts (1)

76-76: 🎯 Functional Correctness

The fallback test and runGitRepoCommands use the same ./runner module export. The proposed module-resolution mismatch does not exist.

src/main/ipc/repos-add-linked-worktree.test.ts (1)

47-54: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Make the async mocks return Promises.

The real isGitRepoAsync, inspectGitRepoForRegistrationAsync, and getGitRepoRootAsync return Promises. The mocks return plain values. await hides the mismatch. Code that calls .then on the result would break only in production.

Use mockResolvedValue-style wrappers to preserve the real signature.

Based on learnings: mock functions must preserve the sync/async signature of the real implementation they replace.

Proposed change
-  isGitRepoAsync: isGitRepoMock,
-  inspectGitRepoForRegistrationAsync: vi.fn((path: string) => ({
+  isGitRepoAsync: vi.fn(async (path: string) => isGitRepoMock(path)),
+  inspectGitRepoForRegistrationAsync: vi.fn(async (path: string) => ({
     isRepo: isGitRepoMock(path),
     rootPath: getGitRepoRootMock(path),
     mainRepoPath: getLinkedWorktreeMainRepoRootMock(path)
   })),
   getGitRepoRoot: getGitRepoRootMock,
-  getGitRepoRootAsync: getGitRepoRootMock,
+  getGitRepoRootAsync: vi.fn(async (path: string) => getGitRepoRootMock(path)),

Source: Learnings


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 464512fd-42c2-481a-a69f-0411cbfb85c9
📥 Commits

Reviewing files that changed from the base of the PR and between 72208aa and 09fbb2b.

📒 Files selected for processing (7)
  • config/scripts/run-ssh-docker-e2e.mjs
  • src/main/git/repo-detection-async.test.ts
  • src/main/git/repo-detection.ts
  • src/main/git/repo.ts
  • src/main/ipc/repos-add-linked-worktree.test.ts
  • src/main/ipc/repos/local-repo-registration.ts
  • tests/e2e/repo-registration-async.spec.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 529a6a3f-3b99-48b5-988f-5444f60c8a79
📥 Commits

Reviewing files that changed from the base of the PR and between 09fbb2b and 1fdc56b.

📒 Files selected for processing (4)
  • src/main/git/repo-detection-async.test.ts
  • src/main/git/repo-detection-command.test.ts
  • src/main/git/repo-detection-failure-parity.test.ts
  • tests/e2e/repo-registration-async.spec.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Comment on lines +80 to +82
expect(
await orcaPage.evaluate(async () => (await window.api.repos.list()).map((repo) => repo.id))
).toEqual(result.persistedIds)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '1,205p' tests/e2e/repo-registration-async.spec.ts
rg -n 'E2E Assertions Must Target|thing a spec finally|project row|sidebar' tests/e2e/AGENTS.md tests/e2e/*registration* tests/e2e/helpers | head -95

Repository: stablyai/orca

Length of output: 10453


🏁 Script executed:

set -e
printf '%s\n' '--- sidebar/project DOM references ---'
rg -n -F --glob '*.{ts,tsx}' -- 'Worktrees' src tests/e2e || test "$?" -eq 1
rg -n --glob '*.{ts,tsx}' 'role="(option|listbox|treeitem)"|getByRole\(.{0,80}(option|listbox|treeitem)|data-(project|worktree|sidebar)' src tests/e2e || test "$?" -eq 1
printf '%s\n' '--- comparable registration/project DOM assertions ---'
rg -n --glob 'tests/e2e/**/*.ts' 'repos\.add|addRemote|project.*row|sidebar.*project|getByRole\(' tests/e2e || test "$?" -eq 1
printf '%s\n' '--- relevant package/source file names ---'
rg --files src tests/e2e | rg '(sidebar|project|worktree|repo-registration)' | head -120

Repository: stablyai/orca

Length of output: 45665


🏁 Script executed:

set -e
printf '%s\n' '--- exact sidebar selectors in renderer ---'
rg -n -F -- 'data-worktree-sidebar' src/renderer/src tests/e2e || test "$?" -eq 1
rg -n -F -- 'data-project-group-header-id' src/renderer/src tests/e2e || test "$?" -eq 1
printf '%s\n' '--- project-group assertion context ---'
sed -n '1,215p' tests/e2e/project-group-manual-sort.spec.ts
printf '%s\n' '--- candidate sidebar component declarations ---'
rg -n --glob '*.tsx' 'ProjectGroup|project group|worktree-sidebar|role=.option.|aria-label' src/renderer/src/components src/renderer/src | head -180

Repository: stablyai/orca

Length of output: 41215


🏁 Script executed:

set -e
printf '%s\n' '--- catalog settlement and registration test references ---'
rg -n -F -- 'awaitLocalRepoCatalogSettlement' src tests/e2e
rg -n -F -- 'fetchRepos' src/renderer/src/store src/renderer/src/components/sidebar | head -80
printf '%s\n' '--- sidebar row declarations ---'
sed -n '1,280p' src/renderer/src/components/sidebar/worktree-list/rows/SectionHeader.tsx
sed -n '280,390p' src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx
printf '%s\n' '--- repo-header and option rendering ---'
rg -n --glob '*.tsx' 'data-repo-header-id|role="option"|data-worktree-id' src/renderer/src/components/sidebar/worktree-list

Repository: stablyai/orca

Length of output: 28281


🏁 Script executed:

set -e
printf '%s\n' '--- default sidebar grouping ---'
rg -n --glob '*.{ts,tsx}' 'groupBy\s*:\s*['"'"'"'"'"']repo|groupBy\s*=\s*['"'"'"'"'"']repo|default.*groupBy|groupBy.*default|setGroupBy' src/renderer/src/store src/renderer/src/app-shell src/renderer/src/components/sidebar | head -140
printf '%s\n' '--- catalog settlement implementation ---'
sed -n '70,185p' src/renderer/src/store/repos/repo-catalog-actions.ts
printf '%s\n' '--- comparable visibility test setup/assertion ---'
sed -n '1,155p' tests/e2e/project-group-creation-visibility.spec.ts

Repository: stablyai/orca

Length of output: 17368


Assert the registered project in the sidebar.

The E2E rule requires the final expect() to target user-visible DOM. The registration test currently ends with window.api.repos.list() and has no DOM assertion. No exception applies.

Keep the API checks as supporting assertions, then assert the single repository header in the sidebar. Use the repository-header selector instead of counting generic role="option" elements, which represent worktree rows.

🐛 Suggested fix
--- "a/tests/e2e/repo-registration-async.spec.ts"
+++ "b/tests/e2e/repo-registration-async.spec.ts"
@@ -77,10 +77,11 @@
     join(root, 'missing')
   )
   expect('error' in missing).toBe(true)
   expect(
     await orcaPage.evaluate(async () => (await window.api.repos.list()).map((repo) => repo.id))
   ).toEqual(result.persistedIds)
+  await expect(orcaPage.locator('[data-worktree-sidebar] [data-repo-header-id]')).toHaveCount(1)
 })
 
 test('SSH registration reads the repository on its execution host', async ({
   orcaPage
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(
await orcaPage.evaluate(async () => (await window.api.repos.list()).map((repo) => repo.id))
).toEqual(result.persistedIds)
expect(
await orcaPage.evaluate(async () => (await window.api.repos.list()).map((repo) => repo.id))
).toEqual(result.persistedIds)
await expect(orcaPage.locator('[data-worktree-sidebar] [data-repo-header-id]')).toHaveCount(1)

@nwparker
nwparker merged commit b63d39e into main Oct 10, 2026
42 checks passed
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.

1 participant