improve delete worktree failure diagnostics (#302)

## Summary
- move delete-worktree failures out of transient toast-only UX and keep
them inline in the delete modal
- add parsed diagnostics for common failure modes, including a short
summary, likely cause, and suggested next step
- make the raw error easier to review and share with raw and sanitized
copy actions

Closes #301.

## BEFORE:

<img width="1127" height="860" alt="image"
src="https://github.com/user-attachments/assets/dd09ba1e-be8c-450c-a1dd-f1cde2a48802"
/>

## AFTER: 

<img width="1384" height="835" alt="image"
src="https://github.com/user-attachments/assets/6b0d1459-21fa-4264-9e54-45540f584538"
/>

## Problem
Before this change, delete-worktree failures were difficult to work
with:

1. The failure message was effectively raw backend or git output.
2. Users had to infer the meaning of the error themselves.
3. The UI did not explain what likely went wrong or what to do next.
4. Sharing the error for debugging was awkward when it included
machine-local absolute paths.
5. The confirmation modal was not being used as the primary diagnostic
surface for a destructive action that frequently fails for
understandable reasons.

This was especially frustrating for common cases such as:
- modified or untracked files in the worktree
- a process still using the worktree directory
- permission errors on Windows
- missing worktree directories or stale worktree records

## What changed

### Modal failure UX
- keep delete failures inline inside
`packages/ui/src/components/worktree-selector.tsx`
- clear modal-local error state when opening or closing the dialog
- keep the success toast on successful deletion, but use the modal
itself for failure presentation

### Human-readable diagnostics
- parse JSON-shaped backend error payloads such as `{"error":"..."}`
before classification
- classify common delete failure patterns into:
  - `localChanges`
  - `inUse`
  - `notFound`
  - `permissionDenied`
  - `unknown`
- render three user-facing lines above the raw error:
  - summary
  - likely cause
  - suggested next step

### Copy flows
- add `Copy error` for the original failure text
- add `Copy sanitized` to redact common absolute path and username
patterns before copying

### Modal content and sizing
- present the target worktree in a simpler two-line summary block
- update the delete description text to plain English: `Deletes this
branch worktree and its local folder.`
- size the delete modal deliberately for desktop use while allowing
vertical expansion to the viewport limit before scrolling

### i18n coverage
- add the new delete diagnostic strings across all currently supported
locales touched by this area:
  - `en`
  - `es`
  - `fr`
  - `he`
  - `ja`
  - `ru`
  - `zh-Hans`

## Why this approach
- It keeps the backend contract unchanged and solves the UX problem
where it occurs.
- It preserves access to the raw failure text instead of hiding
implementation detail entirely.
- It gives users immediate guidance without forcing them to translate
git errors into next actions.
- It improves bug reporting without requiring a separate logging or
export workflow.

## Not included
- server-side preflight guards that block delete when the worktree is
still assigned or in use
- process-aware worktree locking detection
- automatic retry or force-delete-and-retry flows

Those are useful follow-ups, but this PR is intentionally scoped to
failure presentation and debuggability.

## Files changed
- `packages/ui/src/components/worktree-selector.tsx`
- `packages/ui/src/lib/i18n/messages/en/instance.ts`
- `packages/ui/src/lib/i18n/messages/es/instance.ts`
- `packages/ui/src/lib/i18n/messages/fr/instance.ts`
- `packages/ui/src/lib/i18n/messages/he/instance.ts`
- `packages/ui/src/lib/i18n/messages/ja/instance.ts`
- `packages/ui/src/lib/i18n/messages/ru/instance.ts`
- `packages/ui/src/lib/i18n/messages/zh-Hans/instance.ts`

## Validation
- `npm run typecheck --workspace @codenomad/ui`
- `npm run build --workspace @codenomad/ui`
- `npm run typecheck --workspace @neuralnomads/codenomad-electron-app`

## Notes for reviewers
- The error classifier is intentionally heuristic and string-based. It
is meant to improve the common cases without increasing backend
coupling.
- The sanitized copy flow is conservative and focused on path and
username redaction, not full structured log scrubbing.

---------

Co-authored-by: Shantur Rathore <i@shantur.com>
This commit is contained in:
VooDisss
2026-04-17 19:12:17 +03:00
committed by GitHub
parent 9d9a6a79ec
commit e022a158eb
8 changed files with 381 additions and 15 deletions

View File

@@ -166,6 +166,30 @@ export const instanceMessages = {
"instanceShell.backgroundProcesses.actions.output": "Sortie",
"instanceShell.backgroundProcesses.actions.stop": "Arrêter",
"instanceShell.backgroundProcesses.actions.terminate": "Terminer",
"instanceShell.worktree.delete.error.title": "Echec de suppression",
"instanceShell.worktree.delete.error.fallback": "Impossible de supprimer le worktree",
"instanceShell.worktree.delete.error.causeLabel": "Cause probable :",
"instanceShell.worktree.delete.error.nextStepLabel": "Etape suivante suggeree :",
"instanceShell.worktree.delete.error.summary.localChanges": "Git a refuse de supprimer ce worktree car il contient des fichiers modifies ou non suivis.",
"instanceShell.worktree.delete.error.summary.inUse": "CodeNomad n'a pas pu supprimer ce worktree car quelque chose utilise encore des fichiers dans ce dossier.",
"instanceShell.worktree.delete.error.summary.notFound": "CodeNomad n'a pas pu supprimer ce worktree car le dossier ou l'enregistrement du worktree est introuvable.",
"instanceShell.worktree.delete.error.summary.permissionDenied": "CodeNomad n'a pas pu supprimer ce worktree car l'acces au dossier a ete refuse.",
"instanceShell.worktree.delete.error.summary.unknown": "CodeNomad n'a pas pu supprimer ce worktree.",
"instanceShell.worktree.delete.error.cause.localChanges": "Modifications locales",
"instanceShell.worktree.delete.error.cause.inUse": "Un autre processus utilise ce worktree",
"instanceShell.worktree.delete.error.cause.notFound": "Le dossier ou l'enregistrement du worktree est manquant",
"instanceShell.worktree.delete.error.cause.permissionDenied": "Permissions du systeme de fichiers insuffisantes",
"instanceShell.worktree.delete.error.cause.unknown": "Le backend a renvoye une erreur de suppression non classee",
"instanceShell.worktree.delete.error.nextStep.localChanges": "Activez la suppression forcee si vous voulez jeter les modifications locales, ou nettoyez le worktree puis reessayez.",
"instanceShell.worktree.delete.error.nextStep.inUse": "Fermez les terminaux, editeurs, observateurs ou processus en arrière-plan qui utilisent ce worktree puis reessayez.",
"instanceShell.worktree.delete.error.nextStep.notFound": "Rechargez les worktrees puis reessayez. Si cela echoue encore, inspectez le chemin du worktree sur le disque.",
"instanceShell.worktree.delete.error.nextStep.permissionDenied": "Verifiez les permissions du systeme de fichiers et fermez les applications qui peuvent verrouiller ce dossier, puis reessayez.",
"instanceShell.worktree.delete.error.nextStep.unknown": "Consultez l'erreur brute ci-dessous pour les details, puis reessayez apres avoir corrige le probleme signale.",
"instanceShell.worktree.delete.error.copyRaw": "Copier l'erreur",
"instanceShell.worktree.delete.error.copySanitized": "Copier la version nettoyee",
"instanceShell.worktree.delete.error.copySuccess": "Erreur de suppression copiee",
"instanceShell.worktree.delete.error.copySanitizedSuccess": "Erreur de suppression nettoyee copiee",
"instanceShell.worktree.delete.error.copyFailure": "Impossible de copier l'erreur de suppression",
"versionPill.appWithVersion": "Appli {version}",
"versionPill.ui": "UI",