fix(desktop): make external file changes and save conflicts explicit

Merged
KI
2mo ago
+856-136

What & why

Editing an open .env while a script replaces the file leaves two different versions. The v2 editor preserved the unsaved buffer but gave no visible warning, and saving showed a generic “Do you want to save?” dialog whose Save action force-overwrote the newer disk contents. In a CDP reproduction, this removed the script-generated token.

Original report: Slack thread in #ext-ardent.

  • Show a visible Save button and an external-change banner with Compare.
  • Replace the generic conflict alert with a shared, localized side-by-side disk/local diff. Offer Keep editing, Reload from disk, and explicitly destructive Overwrite file. Focus Keep editing by default; prevent reload when the disk version cannot be read.
  • Preserve edits across overlapping reads, obsolete read errors, and save/delete/rename races. Refresh open documents on watcher overflow.
  • Resolve canceled close requests and activate the conflicted file when a pane or background tab cannot close safely. Only the active tab/pane presents the review dialog.
  • Reuse the review dialog in the legacy editor/diff view; translate new copy into every shipping locale.

Local reference implementations: VS Code's textFileEditorModel.ts guards dirty buffers and version changes during reads; textFileSaveErrorHandler.ts uses explicit Compare/Overwrite conflict actions. Wave's preview-model.tsx exposes a visible Save action for edited buffers. Manual save remains intentional; this PR does not add autosave or silently merge simultaneous edits.

How I tested it

Real desktop CDP before/after: worktree low-foxtrot, renderer http://localhost:6825, CDP 9482, verified local-dev session, isolated fixture repo with a gitignored .env containing synthetic values. Opened the file through the Files tree and used real mouse/key input; external writes and atomic replacements used the filesystem. No injected editor/document state.

JourneyBeforeAfter
Clean .env, script atomically replaces itRefreshes correctlyRefreshes correctly, including two consecutive replacements
Dirty .env, script adds a tokenBuffer stays stale with no external-change warningBuffer preserved; visible warning and Compare
Cmd+S with newer disk contentsGeneric Save/Don't Save/Cancel; Save removes the generated tokenDisk/local diff shows the token and email edit; Keep editing focused; review performs no write
Dismiss / compare / reload—Escape preserves both versions; Compare does not write; Reload loads the token-bearing file and clears warning
Save normallyKeyboard or close promptVisible Save immediately writes the edited email and retained token
Delete/recreate while dirty—Buffer preserved; reload disabled for unreadable disk; recreation restores external-change warning
Close/reopen; conflict while closing—Cancel works; Save/reopen retains contents; conflicts keep the file open for review
Close a background dirty tab and Save All—Activates the conflicted file and displays exactly one review dialog
Explicit overwrite—Replaces disk contents only through the clearly labeled overwrite action

Captured before/after screenshots and a detailed verification record locally in /tmp/low-foxtrot-evidence/; archive: /tmp/low-foxtrot-file-sync-evidence.zip. Screenshots are not attached to GitHub. Console error capture was empty during the initial after-flow and save/delete/close checks.

The original reporter's app build and make script were unavailable; the CDP reproduction uses the stated edit → external replacement → save workflow. Reload-ordering and overflow races are unit-tested, not claimed as CDP reproductions. A symlink resolving outside the workspace was rejected by the existing filesystem boundary; that policy is unchanged.

  • 14 focused tests pass (file-document store and snapshot adapter). Six initial reload/overflow regression cases failed before the first fix.
  • bun run lint passes.
  • bun run check:i18n passes, including strict compilation of every shipping locale.
  • Repository bun run typecheck passed all 42 tasks; desktop bunx tsc --noEmit passed again after the final focus/close-guard changes.
  • Full bun run test reported 34 failures across five desktop Git/worktree/shell test files. Each file passes in isolation, totaling 65 passing tests. No full-suite pass is claimed.

Checklist

  • PR title follows conventional commits (type(scope): subject)
  • bun run lint and bun run typecheck pass (CI fails on lint warnings too)
  • New user-facing strings translated and bun run check:i18n passes
  • Original report linked; real CDP before/after captured
  • "Allow edits from maintainers" is checked on fork PRs (same-repository branch)

Summary by CodeRabbit

  • New Features

    • Added a visual file-conflict dialog showing local edits alongside the disk version.
    • Added options to keep editing, reload from disk, or overwrite the disk file.
    • Added a banner for externally changed files with a Compare action.
    • Added a Save button to text-file panes with progress feedback.
  • Bug Fixes

    • Prevented stale file reads from overwriting newer edits.
    • Improved save and close flows with clearer focus and dismissal behavior.
  • Localization

    • Added translations for file-conflict messages across supported languages.