Skip to content

fix: make worktree scan failures actionable - #21291

Merged
Jinwoo-H merged 2 commits into
mainfrom
pencilfish
Sep 18, 2026
Merged

Jinwoo-H merged 2 commits into
mainfrom
pencilfish

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​28 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​28
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​161 $\color{#cf222e}{\Huge{\mathbf{−}}}$​42 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​119

ELI5

When Git cannot scan a repository, the sidebar now explains the likely host problem and gives a safe next step instead of showing only the raw error.

What Changed

  • Added shared classification for Xcode license, missing Apple developer tools, architecture mismatch, and unknown Git startup failures.
  • Updated the worktree warning tooltip with a plain-language explanation.
  • Added Copy command for the two validated Apple setup commands.
  • Added Copy diagnostics for every failure.
  • Kept architecture guidance conditional instead of assuming Rosetta is always the fix.

Why

The same host-level Git failure can affect every repository, but the old tooltip exposed only raw stderr and required users to discover the repair themselves. The classifier keeps known guidance specific and avoids prescribing a misleading architecture fix when the host evidence is incomplete.

Linked Issue

Fixes #

Visual Proof

After screenshot (fresh background Electron renderer; tooltip footer actions are left-aligned):

Worktree scan diagnostic with copy actions

Testing

  • Focused renderer and classifier tests: 6 passed.

  • oxlint passed for changed files.

  • Manually validated the tooltip in a background Orca Electron instance through CDP on macOS.

  • SSH/Windows behavior is unchanged; classification is based only on returned Git diagnostics.

  • I manually tested these changes locally

  • Automated tests added/updated

Review

Please review whether the classifier wording matches the desired support guidance before broadening it to structured executable-path diagnostics.

Agent skill upstream boundary

  • Not applicable

Notes

The implementation does not automatically select an alternate Git executable. It reports actionable guidance while preserving existing PATH selection behavior.

@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

Follow-up accuracy review addressed before merge:

  • Suppressed platform-specific repair commands for remote execution hosts.
  • Removed the broad xcode-select: error: classification; only the explicit missing-tools message receives install guidance.
  • Made architecture wording platform-neutral until host and executable metadata are available.
  • Kept copied diagnostics local-only for paths/platform details; remote diagnostics identify the execution host without claiming local context.

Focused validation now passes 7 tests and oxlint.

@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 0c28420e-33fe-4885-ad12-3dd12ba21eeb

📥 Commits

Reviewing files that changed from the base of the PR and between 1736da8 and fa00f83.

📒 Files selected for processing (4)
  • src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts
  • src/shared/worktree-scan-failure.ts

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


📝 Walkthrough

Walkthrough

The change adds structured worktree scan failure kinds for Xcode license, developer tools, architecture mismatch, and unknown errors. Main-process metadata fallbacks now include the classified kind. Renderer validation and result comparisons account for failureKind. The unavailable indicator displays kind-specific messages, applicable fix commands, and copyable diagnostics. Tests cover all supported classifications and unknown cases.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to fa00f

Some unrelated Git startup failures may receive incorrect architecture-mismatch guidance. This does not block scanning or recovery, but the classifier should be narrowed before merge if accurate remediation is required.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change, motivation, testing, platform scope, and user-facing behavior. However, the required Linked Issue field is incomplete because it contains only Fixes #, and the r… Add the complete linked issue number after Fixes #. Add a before screenshot or video alongside the existing after screenshot. Complete the required checklist items or state why they do not apply.
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 8 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: making worktree scan failures actionable.
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.
Full details: Description check

Explanation

The description explains the change, motivation, testing, platform scope, and user-facing behavior. However, the required Linked Issue field is incomplete because it contains only Fixes #, and the required visual proof includes only an after screenshot instead of both before and after evidence.

Full details: Docstring Coverage

Explanation

Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 8 files. (1 skipped: 1 unsupported.)

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

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.

@Jinwoo-H
Jinwoo-H force-pushed the pencilfish branch 2 times, most recently from 979cf63 to eeaadbe Compare September 17, 2026 21:02

@pullfrog pullfrog 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.

Caution

The new copy actions are unreachable. They render inside TooltipContent, which the shared primitive pins to pointer-events-none and whose provider defaults disableHoverableContent = true ("app tooltips are non-interactive labels"), so neither mouse nor keyboard can activate them. This is the PR's central affordance, so it currently ships non-functional.

Reviewed changes

  • Shared classifier — classifyWorktreeScanFailure maps raw Git stderr to xcode-license / developer-tools / architecture-mismatch / unknown, returning a plain-language message and a fix command for the two Apple cases.
  • Tooltip content — the worktree scan warning now shows the classified message plus fixCommand, with "Copy command" / "Copy diagnostics" buttons copying through navigator.clipboard.
  • Classifier tests — three cases covering the two Apple classifications and the architecture spawn error.

ℹ️ Nitpicks

  • Classifier tests pin the three known branches but not the unknown fallback (that it echoes the raw reason) or the fact that architecture-mismatch intentionally carries no fixCommand; those are the contract the PR description calls out.
  • The component test (RepoScanUnavailableIndicator.test.tsx) still only asserts the trigger button, so the copy actions have no coverage — which is why the interactivity issue wasn't caught.
  • navigator.platform (line 40) is deprecated; the repo standard is navigator.userAgent.includes('Mac').

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment on lines +43 to +44
const copyText = async (value: string): Promise<void> => {
await navigator.clipboard?.writeText(value)

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.

navigator.clipboard?.writeText is the one clipboard path the codebase documents as unreliable in Orca's renderer, and the optional chaining plus missing catch make a failed copy indistinguishable from a successful one. Every other copy affordance routes through window.api.ui.writeClipboardText.

Technical details
# Copy uses a clipboard path that fails silently in the renderer

## Affected sites
- `src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx:43-44` — `await navigator.clipboard?.writeText(value)`; caller uses `void copyText(...)`, so a rejection is unhandled.
- Precedent documenting the failure: `src/renderer/src/components/native-chat/NativeChatCopyButton.tsx:6-12`, `src/renderer/src/components/sidebar/AutoRenameFailedDialog.tsx:77-80`, `src/renderer/src/components/quick-open-install-rg-guidance.tsx:85-88`.

## Required outcome
- Copies go through `window.api.ui.writeClipboardText` and surface success/failure.
- A rejected write must not become an unhandled rejection, and a missing clipboard must not silently no-op.

## Suggested approach
- Reuse `useClipboardTextCopyFeedback` (`src/renderer/src/hooks/use-clipboard-text-copy-feedback.ts`): it wraps the IPC, returns a `copied`/`failed` status, and guards state after unmount.

Comment on lines +97 to +106
Copy command
</button>
) : null}
<button
type="button"
className="text-xs underline"
onClick={() => void copyText(diagnosticText)}
>
Copy diagnostics
</button>

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.

The two new labels are raw literals while every other string in this component goes through translate(...); the classifier message values are likewise English-only and rendered directly. The localization gates pass only because they verify referenced keys, not untranslated literals.

Technical details
# New user-facing strings bypass the i18n catalog

## Affected sites
- `RepoScanUnavailableIndicator.tsx:97,105` — "Copy command" / "Copy diagnostics" literals.
- `RepoScanUnavailableIndicator.tsx:38-41` — diagnostics labels ("Repository:", "Path:", …).
- `src/shared/worktree-scan-failure.ts:11,20,27-28` — `message` values shown in the tooltip.

## Required outcome
- User-visible copy resolves through the localization catalog like the rest of the component.

## Suggested approach
- Wrap the labels in `translate(...)` and run `pnpm run sync:localization-catalog`.
- The shared classifier cannot import renderer i18n: return `kind` and map it to localized messages in the component, keeping the raw reason for `unknown`.

@Jinwoo-H
Jinwoo-H force-pushed the pencilfish branch 2 times, most recently from 8c41a73 to 1736da8 Compare September 17, 2026 21:05
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

Addressed the valid review findings:

  • Copy controls now use an interactive local TooltipProvider, so they are reachable by mouse and keyboard despite the app tooltip default being non-interactive.
  • Platform detection follows the repository standard (navigator.userAgent.includes('Mac')).
  • Added the unknown-classification test and kept architecture messaging generic; no Rosetta-specific guidance is inferred.
  • Moved command mapping out of render and localized the copy action labels.

The requested focused tests now pass 5/5, and oxlint plus React lint pass.

@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: 65028f5d-4d7a-4683-a908-455004c04ff4

📥 Commits

Reviewing files that changed from the base of the PR and between 8e8a9b3 and 1736da8.

📒 Files selected for processing (8)
  • src/main/ipc/worktrees/listing/detected-provider-listing.ts
  • src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx
  • src/renderer/src/store/slices/worktrees/listing/detected-worktree-host-merge.ts
  • src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts
  • src/renderer/src/store/slices/worktrees/listing/worktree-catalog-visibility.ts
  • src/shared/worktree-scan-failure.test.ts
  • src/shared/worktree-scan-failure.ts
  • src/shared/worktree/types.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.

if (/no developer tools were found/i.test(reason)) {
return 'developer-tools'
}
if (/Unknown system error -86|EBADARCH|Bad CPU type in executable/i.test(reason)) {

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '1,100p' src/shared/worktree-scan-failure.ts
sed -n '1,160p' src/shared/worktree-scan-failure.test.ts
rg -n -- 'Unknown system error -86|EBADARCH|Bad CPU type|architecture-mismatch' src test* 2>/dev/null || true
node - <<'NODE'
const current = /Unknown system error -86|EBADARCH|Bad CPU type in executable/i
const proposed = /\bUnknown system error -86\b|\bEBADARCH\b|Bad CPU type in executable/i
for (const s of ['Unknown system error -86', 'Unknown system error -860', 'Unknown system error -861']) {
  console.log(s, { current: current.test(s), proposed: proposed.test(s) })
}
NODE

Repository: stablyai/orca

Length of output: 2696


🏁 Script executed:

rg -n -C 4 --glob '*.ts' --glob '*.tsx' --glob '*.json' 'classifyWorktreeScanFailure|worktree-scan-failure|architecture-mismatch|Unknown system error -86|EBADARCH|Bad CPU type|scan failure|scanFailure' src package.json

Repository: stablyai/orca

Length of output: 22468


🏁 Script executed:

sed -n '130,175p' src/main/ipc/worktrees/listing/detected-provider-listing.ts
sed -n '48,75p' src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx
rg -n -C 5 --glob '*.ts' --glob '*.tsx' 'function describeWorktreeScanFailure|describeWorktreeScanFailure|unavailableReason' src/main/ipc/worktrees/listing src/shared src/renderer/src/store/slices/worktrees

Repository: stablyai/orca

Length of output: 21522


🏁 Script executed:

sed -n '1,55p' src/main/ipc/worktrees/listing/worktree-listing-diagnostics.ts

Repository: stablyai/orca

Length of output: 1428


Delimit the numeric error code.

The current expression matches Unknown system error -860 because -86 is an unbounded substring. The listing stores that result as architecture-mismatch, and the local Mac UI can then show incorrect CPU-architecture guidance. Use a digit lookahead instead of a word boundary, because the numeric code starts after a hyphen. Add a near-match test for -860.

Proposed fix
-  if (/Unknown system error -86|EBADARCH|Bad CPU type in executable/i.test(reason)) {
+  if (/Unknown system error -86(?!\d)|\bEBADARCH\b|Bad CPU type in executable/i.test(reason)) {
📝 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
if (/Unknown system error -86|EBADARCH|Bad CPU type in executable/i.test(reason)) {
if (/Unknown system error -86(?!\d)|\bEBADARCH\b|Bad CPU type in executable/i.test(reason)) {

@pullfrog pullfrog 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.

Caution

Two blocking problems remain at this head: the five new translate() keys are absent from en.json, so verify:localization-catalog and verify:localization-extraction both fail, and the copy actions are still rendered inside a non-interactive TooltipContent and still copy through navigator.clipboard.

Reviewed changes (since the prior review at 937acd9)

  • Classifier returns a kind only — classifyWorktreeScanFailure now returns WorktreeScanFailureKind; the main process classifies describeWorktreeScanFailure(err) and ships failureKind on DetectedWorktreeListResult.
  • Renderer maps kind to localized copy — the indicator selects the message and fix command from a table, gated to local-Mac repos, and builds a host-aware diagnostics string.
  • Store plumbing — merge and catalog-equality comparisons include failureKind; the provider-request validator accepts only known kinds.
  • Tests — classifier cases expanded to four (adding invalid active developer path → unknown); new strings wrapped in translate().

ℹ️ invalid active developer path now gets no fix command

The developer-tools matcher was narrowed to /no developer tools were found/, and the new test pins invalid active developer path to unknown. That path error is the common post-macOS-upgrade Git breakage, and it is the case where xcode-select --install (or --reset) is the standard repair — so this may undercut the PR's goal for the most frequent macOS failure.

Technical details
# Narrowed developer-tools matcher drops the common macOS repair

## Affected sites
- `src/shared/worktree-scan-failure.ts:11` — `/no developer tools were found/i` is now the only developer-tools signal.
- `src/shared/worktree-scan-failure.test.ts:15-19` — asserts `invalid active developer path` → `unknown`.

## Required outcome
- Decide whether `invalid active developer path` / `xcode-select: error:` should keep offering `xcode-select --install`; if not, the tooltip for that case is raw stderr with no next step.

## Open questions for the human
- Is the intent to avoid prescribing `--install` when the developer dir is merely misconfigured (e.g. a custom Xcode path), or was this a side effect of tightening `xcode-select: error:`?

ℹ️ Nitpicks

  • detectedWorktreeFailureKinds (detected-worktree-provider-request.ts:20-25) manually mirrors the WorktreeScanFailureKind union and can drift when a kind is added; exporting a const tuple from worktree-scan-failure.ts and deriving both would keep them in sync.
  • fixCommandForFailureKind (component lines 23-31) is more verbose than a Partial<Record<WorktreeScanFailureKind, string>> lookup over WORKTREE_SCAN_FIX_COMMANDS.
  • navigator.platform (lines 54, 77) is deprecated; the repo standard is navigator.userAgent.includes('Mac').

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment on lines +128 to +151
<div className="flex items-center justify-start gap-3 border-t border-border/60 pt-1">
{fixCommand ? (
<button
type="button"
className="text-xs underline"
onClick={() => void copyText(fixCommand)}
>
{translate(
'auto.components.sidebar.RepoScanUnavailableIndicator.copyCommand',
'Copy command'
)}
</button>
) : null}
<button
type="button"
className="text-xs underline"
onClick={() => void copyText(diagnosticText)}
>
{translate(
'auto.components.sidebar.RepoScanUnavailableIndicator.copyDiagnostics',
'Copy diagnostics'
)}
</button>
</div>

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.

Carried from the prior review and still present: these buttons sit inside TooltipContent, which the shared primitive pins to pointer-events-none and whose provider defaults disableHoverableContent = true, so pointer events pass through and the tooltip closes once the pointer leaves the trigger — neither mouse nor keyboard can activate them.

Technical details
# Interactive controls inside a non-interactive tooltip

## Affected sites
- `src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx:128-151` — the footer buttons render inside `TooltipContent` with no `pointer-events-auto`.
- `src/renderer/src/components/ui/tooltip.tsx:46` — content className includes `pointer-events-none`; provider (line 8) defaults `disableHoverableContent = true`, commented "app tooltips are non-interactive labels".
- `src/renderer/src/App.tsx:92` — app root mounts the default provider.

## Required outcome
- The copy actions must be reachable by mouse and keyboard without holding hover on the trigger.
- Per `docs/STYLEGUIDE.md` (Tooltips), interactive content and blocking errors do not belong in a tooltip.

## Suggested approach
- Move the message + actions into a click-revealed `Popover` (or an inline row under the repo) and keep the tooltip as a short label on the icon button.
- If a tooltip must carry them, add `pointer-events-auto` to the content and set `disableHoverableContent={false}` for this instance — but that fights the primitive's documented contract.

Comment on lines +81 to +82
const copyText = async (value: string): Promise<void> => {
await navigator.clipboard?.writeText(value)

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.

Carried from the prior review and still present: navigator.clipboard is the path this repo documents as failing silently in the renderer and inside Radix surfaces, and the optional chaining plus missing catch make a failed copy indistinguishable from a successful one. Every other copy affordance routes through window.api.ui.writeClipboardText.

Technical details
# Copy uses a clipboard path that fails silently in the renderer

## Affected sites
- `src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx:81-82` — `await navigator.clipboard?.writeText(value)`; callers use `void copyText(...)`, so a rejection is unhandled.
- Precedent documenting the failure: `NativeChatCopyButton.tsx:6-12`, `AutoRenameFailedDialog.tsx:77-80`, `quick-open-install-rg-guidance.tsx:85-88`.

## Required outcome
- Copies go through `window.api.ui.writeClipboardText` and surface success/failure.
- A rejected write must not become an unhandled rejection, and a missing clipboard must not silently no-op.

## Suggested approach
- Reuse `useClipboardTextCopyFeedback` (`src/renderer/src/hooks/use-clipboard-text-copy-feedback.ts`), which wraps the IPC, exposes a copied/failed status, and guards state after unmount.

@pullfrog pullfrog 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.

Important

The interactivity fix works, but three items remain: the five new translate() keys are still absent from en.json (both localization gates fail on this head), the copy still uses navigator.clipboard (the path this repo documents as failing silently in the renderer), and there is still no success/failure feedback on copy.

Reviewed changes (delta from 8c41a73)

  • Tooltip copy actions are now reachable — the trigger is wrapped in a scoped <TooltipProvider disableHoverableContent={false}> and the content carries pointer-events-auto, so the buttons receive pointer events instead of falling through.
  • Platform detection de-deprecated — navigator.platform → navigator.userAgent.includes('Mac'), and the diagnostics line now emits Client platform: macOS.
  • Classifier test — added the unknown-fallback case.

ℹ️ Nitpicks

  • The nested <TooltipProvider> omits delayDuration, so the wrapper's default (0) replaces the app's 400 ms hover delay for this tooltip; pass delayDuration={400} if the instant-open behavior is unintended.
  • detectedWorktreeFailureKinds (detected-worktree-provider-request.ts:20-25) still manually mirrors WorktreeScanFailureKind and can drift; a shared const tuple used to derive both would keep them in sync.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment on lines +81 to +82
const copyText = async (value: string): Promise<void> => {
await navigator.clipboard?.writeText(value)

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.

Still outstanding from the previous two reviews: navigator.clipboard is the path this repo documents as failing silently in the renderer and inside Radix surfaces, and the optional chaining plus missing catch make a failed copy indistinguishable from a successful one — there is also no copied/failed feedback. Every other copy affordance routes through window.api.ui.writeClipboardText.

Technical details
# Copy uses a clipboard path that fails silently in the renderer

## Affected sites
- `src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx:81-82` — `await navigator.clipboard?.writeText(value)`; callers use `void copyText(...)`, so a rejection is unhandled.
- Precedent documenting the failure: `NativeChatCopyButton.tsx:6-12`, `AutoRenameFailedDialog.tsx:77-80`, `quick-open-install-rg-guidance.tsx:85-88`.

## Required outcome
- Copies go through `window.api.ui.writeClipboardText` and surface success/failure.
- A rejected write must not become an unhandled rejection, and a missing clipboard must not silently no-op.

## Suggested approach
- Reuse `useClipboardTextCopyFeedback` (`src/renderer/src/hooks/use-clipboard-text-copy-feedback.ts`), which wraps the IPC, exposes a copied/failed status, and guards state after unmount.

@pullfrog pullfrog 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.

Important

The catalog fix is in — verify:localization-catalog and verify:localization-extraction now pass. One item remains: the copy actions still write through navigator.clipboard, which this repo documents as failing silently in the renderer, and a failed copy is currently indistinguishable from a successful one.

Reviewed changes (delta from 1736da8)

  • English catalog — added xcodeLicense, developerTools, architectureMismatch, copyCommand, and copyDiagnostics under the RepoScanUnavailableIndicator namespace; both localization gates pass.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment on lines +81 to +82
const copyText = async (value: string): Promise<void> => {
await navigator.clipboard?.writeText(value)

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.

Still outstanding: navigator.clipboard is the path this repo documents as failing silently in the renderer and inside Radix surfaces, and the optional chaining plus missing catch make a failed copy indistinguishable from a successful one — there is no copied/failed feedback. Every other copy affordance routes through window.api.ui.writeClipboardText.

Technical details
# Copy uses a clipboard path that fails silently in the renderer

## Affected sites
- `src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx:81-82` — `await navigator.clipboard?.writeText(value)`; callers use `void copyText(...)`, so a rejection is unhandled.
- Precedent documenting the failure: `NativeChatCopyButton.tsx:6-12`, `AutoRenameFailedDialog.tsx:77-80`, `quick-open-install-rg-guidance.tsx:85-88`.

## Required outcome
- Copies go through `window.api.ui.writeClipboardText` and surface success/failure.
- A rejected write must not become an unhandled rejection, and a missing clipboard must not silently no-op.

## Suggested approach
- Reuse `useClipboardTextCopyFeedback` (`src/renderer/src/hooks/use-clipboard-text-copy-feedback.ts`), which wraps the IPC, exposes a copied/failed status, and guards state after unmount.

@pullfrog pullfrog 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.

ℹ️ No critical issues — one minor suggestion inline. The copy now goes through window.api.ui.writeClipboardText, and the previous blockers (tooltip interactivity, missing catalog keys) are resolved.

Reviewed changes (delta from b0ccb25)

  • Clipboard routing — copyText now calls window.api.ui.writeClipboardText(value) instead of navigator.clipboard?.writeText, matching every other copy affordance in the app.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment on lines +81 to +83
const copyText = async (value: string): Promise<void> => {
await window.api.ui.writeClipboardText(value)
}

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.

Now that the write is routed correctly, consider whether a failed copy should stay invisible: onClick={() => void copyText(...)} leaves a rejected writeClipboardText unhandled, and neither button shows copied/failed state. useClipboardTextCopyFeedback (src/renderer/src/hooks/use-clipboard-text-copy-feedback.ts) handles both and is what the other copy buttons use.

@pullfrog pullfrog 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.

ℹ️ No new issues in this delta — the change addresses the prior nitpicks. The earlier copy-feedback suggestion is the only item still open, so this is not an approval.

Reviewed changes (delta from d9ffc6b)

  • Remote diagnostics preserved — failureKind is now read from detected.failureKind on every host, so a remote host's classified cause reaches the tooltip instead of being forced to unknown.
  • Shared kind tuple — WORKTREE_SCAN_FAILURE_KINDS plus isWorktreeScanFailureKind replace the renderer-local Set that mirrored the union, so the validator cannot drift.
  • Fix-command lookup simplified — WORKTREE_SCAN_FIX_COMMANDS is as const satisfies Partial<Record<WorktreeScanFailureKind, string>> and fixCommandForFailureKind indexes it directly.
  • Tooltip delay restored — the scoped provider's tooltip now passes delayDuration={400}, matching the app default.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@Jinwoo-H
Jinwoo-H merged commit c45b2c9 into main Sep 18, 2026
28 checks passed
ivanvykopal pushed a commit to ivanvykopal/orca that referenced this pull request Sep 20, 2026
* fix: make worktree scan failures actionable

* fix: preserve remote worktree scan diagnostics
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