Skip to content

fix: let a slow add-source wizard finish instead of failing with a phantom disk error - #291

Merged
pmaxhogan merged 2 commits into
mainfrom
fix/stale-dialog-token
Aug 14, 2026
Merged

pmaxhogan merged 2 commits into
mainfrom
fix/stale-dialog-token

Conversation

@pmaxhogan

Copy link
Copy Markdown
Owner

The bug (diagnosed from a user diagnostic bundle)

Creating a backup source failed every time with "Driven hit a disk error reading a file" — with zero real disk errors anywhere and zero trace of the six failed add_source calls in the logs. Root cause chain:

  1. add_source resolved the local folder from a single-use dialog token with a 5-minute TTL. A real wizard session (big-tree exclusion preview + Drive destination browsing + thinking) outlives that, so Finish always failed.
  2. take_dialog_token consumed the token before any validation, so the first failed Finish destroyed it and every retry in the same session failed too.
  3. Both failure modes were mapped to local.io_error, whose copy tells the user their disk is failing.
  4. Command rejections were never logged, so the diagnostic bundle contained nothing to diagnose with.

The fix

  • add_source peeks up front and spends the token only at commit (the same R3-P2-1 pattern the restore command already uses), so failed validation leaves the token intact.
  • DIALOG_TOKEN_TTL 5 min → 60 min; the binding now stores its expiry instant (testable without Instant underflow).
  • New stable code internal.stale_dialog_token (SPEC §24 + en-US copy "pick the folder again") replaces the local.io_error mapping for unknown/spent/expired tokens across add_source, exclusion previews, restore, and the diagnostic export. The dialog-cancel sentinel is untouched.
  • Every CommandError is now logged (WARN) as it serialises across the IPC boundary — the single seam every rejection passes through — so failures land in the rolling log and diagnostic bundles. Known trade-off: a UI polling loop against a persistently failing command writes one WARN per poll; accepted for diagnosability.
  • Wizard error UI: the add-source and setup wizards render a muted, monospace technical-detail line (stable code + redacted backend message) under the localized error.

⚠️ Deliberate amendment to the R8-P2-1 contract

R8-P2-1 said backend English must never render. This PR narrows that: the localized t(errors.${code}.long) string remains the only primary error, but a dedicated muted detail element may carry the stable code + backend message. Rationale: two failures sharing one code (expired token vs. genuinely unreadable folder) were indistinguishable on screen, which is how a token expiry masqueraded as a disk error. The recovery-reveal-error-i18n test is updated to enforce the new shape (primary line exactly localized; backend text only inside *-error-detail).

Tests

  • cargo check --workspace --all-targets clean; cargo fmt --check clean (dockerized); driven-core 555 + driven-app 417 lib tests + ipc_path_validation pass
  • New: token-expiry rejection test (expired_dialog_token_is_rejected_by_peek_and_take), stale-token i18n copy test, detail-line rendering assertions
  • UI: eslint 0 errors, prettier clean, vue-tsc clean, 778 vitest pass

README checked, no changes needed (no stale claims; behavior-level fix below the feature list).

🤖 Generated with Claude Code

https://claude.ai/code/session_01LRMdu3VkuhL6Ny6hRcnFpU

pmaxhogan and others added 2 commits August 14, 2026 18:26
…antom disk error

Root cause (diagnosed from a user diag bundle): add_source resolved the local folder from a single-use dialog token with a 5-minute TTL, consumed the token before validating anything, and mapped every token failure to local.io_error. A wizard session that outlives the TTL (big-tree exclusion preview + destination browsing) therefore failed at Finish with 'Driven hit a disk error reading a file' - and because the take consumed the token even on failure, every retry in the same session failed the same way. No disk error ever occurred, and none of the six rejected add_source calls left any trace in the logs.

- add_source now PEEKs the token up front and spends it only at commit (the restore command's R3-P2-1 pattern), so a failed validation leaves the token intact
- DIALOG_TOKEN_TTL raised 5min -> 60min to cover a real wizard session; binding stores its expiry instant so tests can force expiry
- new stable code internal.stale_dialog_token (SPEC s24 + en-US copy 'pick the folder again') replaces the local.io_error mapping for unknown/spent/expired tokens across add_source, exclusion previews, restore, and the diagnostic export; the dialog-cancel sentinel is untouched
- every CommandError is now logged (warn) as it serialises across the IPC boundary, so rejected commands appear in the rolling log and diagnostic bundles
- the add-source and setup wizards render a muted technical-detail line (stable code + redacted backend message) under the localized error, so same-code failures are distinguishable on screen

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRMdu3VkuhL6Ny6hRcnFpU
@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Area main this PR delta
Rust (lib crates) 84.58% 84.59% +0.01 (OK)
UI (vue/ts) 93.11% 93.02% -0.09 (OK)

Gate: passed - no coverage regression (epsilon 0.1 pp).

@pmaxhogan
pmaxhogan merged commit 1fbba98 into main Aug 14, 2026
18 checks passed
@pmaxhogan
pmaxhogan deleted the fix/stale-dialog-token branch August 14, 2026 23:42
@github-project-automation github-project-automation Bot moved this from Todo to Done in Driven Aug 14, 2026
pmaxhogan added a commit that referenced this pull request Aug 15, 2026
🤖 I have created a release *beep* *boop*
---


##
[2.11.0](v2.10.1...v2.11.0)
(2026-08-14)


### Features

* **ui:** live disk/network throughput graphs + visible upload recovery
([#290](#290))
([4fe156c](4fe156c))


### Bug Fixes

* **core:** exempt Zone.Identifier from the ads_skipped warning
([#288](#288))
([f37ff68](f37ff68))
* let a slow add-source wizard finish instead of failing with a phantom
disk error ([#291](#291))
([1fbba98](1fbba98))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
pmaxhogan added a commit that referenced this pull request Aug 15, 2026
…d fix (#292)

The #291 wizard fix changed the step-2 error layout (+40px) but never
got a baseline pass - the visual suite only runs at the release gate,
which is exactly how the v2.11.0 release run went red (2 failed
snapshots, both `setup-wizard/step2-error`). Regenerated via `just
visual-update` on current main; all 102 visual tests pass locally in the
same Docker harness. Baseline-only change; after merge the v2.11.0 tag
will be re-cut so the release pipeline can pass.

README checked, no changes needed (test-only).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01A7q3CvJzL4zZmDA9CbXyQQ
pmaxhogan added a commit that referenced this pull request Aug 15, 2026
The v2.11.0 tag's release pipeline failed at the visual-regression gate
(setup-wizard baselines predating #291) before publishing any artifacts, so
no 2.11.0 build ever shipped. The baselines were regenerated in #292; this
empty commit gives release-please a releasable change so 2.11.1 carries the
full 2.11.0 payload (#288, #290, #291) through a green pipeline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A7q3CvJzL4zZmDA9CbXyQQ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant