Skip to content

fix(files): resolve diff-accept SaveFuture on real write outcome - #74

Merged
jwp2987 merged 1 commit into
mainfrom
fix/issue4-diff-storage-completion
Aug 5, 2026
Merged

fix(files): resolve diff-accept SaveFuture on real write outcome#74
jwp2987 merged 1 commit into
mainfrom
fix/issue4-diff-storage-completion

Conversation

@jwp2987

@jwp2987 jwp2987 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Part of #4. Fixes 2 of the remaining serial failures (the "diff-storage" pair).

Problem

TuiDiffStorage::dispatch_write resolved its SaveFuture on successful dispatch, because FileModel is event-based — save/delete/rename_and_save spawn the write and report completion later via FileModelEvent::{FileSaved, FailedToSave}. So accept_and_save reported Success even when the actual write failed, diverging from the accept contract (DiffStorageHelper joins the save futures and maps any error to DiffApplicationFailed).

Fix

Add a completion-waiter to FileModel:

  • save_waiters: HashMap<FileId, Vec<oneshot::Sender<..>>>
  • a shared report_save_outcome funnel every write-completion callback routes through — identical set_version + FileSaved/FailedToSave behavior as before, plus resolving any waiters
  • save_completion(file_id) → a future resolving Ok(()) on FileSaved / the error on FailedToSave

dispatch_write registers a waiter for a successful dispatch and returns that future. The waiter is registered synchronously before the spawned write can resolve; a synchronous dispatch error still short-circuits.

Result

  • accept_reports_write_dispatch_failure — now reports DiffApplicationFailed when the parent-dir creation fails.
  • accept_renames_and_reports_source_as_deleted — the completion future removes the "write not drained by .await" flake.
  • warp_files 30/0; all 9 tui_diff_storage tests green; warp builds clean.

No existing event behavior changed; no assertions weakened.

🤖 Generated with Claude Code

`TuiDiffStorage::dispatch_write` resolved its `SaveFuture` on successful
*dispatch* (an already-ready `Ok`), because `FileModel`'s save/delete/
rename_and_save are event-based: they spawn the write and report completion
later via `FileModelEvent::{FileSaved, FailedToSave}`. So `accept_and_save`
reported `Success` even when the async write failed — a silent divergence
from the accept contract `DiffStorageHelper` assumes (it joins the futures
and maps any error to `DiffApplicationFailed`).

Add a completion-waiter mechanism to `FileModel`:
- a `save_waiters: HashMap<FileId, Vec<oneshot::Sender<..>>>` field,
- a shared `report_save_outcome` funnel that every write-completion callback
  (local/remote save, rename, local/remote delete) now routes through — it
  performs the exact same `set_version` + `FileSaved`/`FailedToSave` emit as
  before, then additionally resolves any waiters for the file,
- `save_completion(file_id)` returning a future that resolves `Ok(())` on
  `FileSaved` / the error on `FailedToSave`.

`dispatch_write` now registers a `save_completion` waiter for a successful
dispatch and returns that future, so a failed write (e.g. a create whose
parent path is a file, not a directory) surfaces as `DiffApplicationFailed`.
The waiter is registered synchronously before the spawned write can resolve;
a synchronous dispatch error still short-circuits without leaving a waiter.

Fixes `accept_reports_write_dispatch_failure` (now reports the failure) and
`accept_renames_and_reports_source_as_deleted` (the completion future removes
the "write not drained by .await" flakiness). warp_files 30/0, all 9
tui_diff_storage tests green, `warp` builds clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gDYSHa4oDvQbfungWwG1h
@jwp2987
jwp2987 merged commit db720bb into main Aug 5, 2026
1 check failed
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