12 / 15 · Failure & Recovery
What happens when something goes wrong
The rebuild must preserve unsent work, show an actionable state and make destructive actions precise.
These rules specify the new build. They do not claim that every rule is already implemented in the reference app. Wire details belong to 11 API Contracts; records belong to 10 Data Structure. Never clear a browser database or remove legacy sources as an error-recovery shortcut.
1 · Mandatory source-to-note flow
FR-17 is part of the web core, P03/P04. It is not an optional AI or canvas extension. The baseline flow is:
- Sign in; connect the user’s Drive separately. Show the app account and connected Drive identity.
- Create/open a note and place the cursor. Open
/drive; browse/search all file types, paginate and choose exactly one existing source. - Insert a source reference at that saved cursor in the same note. Canceling, switching notes or closing the picker invalidates pending searches and inserts nothing.
- Write normal Markdown-compatible paragraphs beside/below the source; these are annotations in the note, stored with its content. Timed media annotations are a separate media-metadata feature.
- Preview/play supported media or use an original-source link for other file types. An unsupported preview does not block linking the file.
- Save locally and sync the note. Reopen it and follow the same Drive file ID; no second upload or full file download is needed just to create the reference.
- Test a GB-sized source, navigation away/back, offline note editing and a failed source request. Writing remains available even when a source cannot be fetched.
A saved reference identifies the source, not its bytes. Renaming/moving the Drive file normally keeps its ID. UI source links generated for display must not be appended to the stored note on every render.
2 · Visible storage and sync states
| State | Display and permitted action | Data rule |
|---|---|---|
| Local only | “Saved in this browser” and Connect Drive. The user may write, search local notes and export. | No cloud guarantee; keep notes under the signed-in account. |
| Connected, inactive | Show connected Drive email and explicit Activate note storage action. | Consent/connection alone must not trigger an unannounced migration or upload. |
| Active, clean | “Synced” with last confirmed time. | Only use this state when the local queue is empty and relevant writes have confirmed results. |
| Active, pending / uploading | Show queued note/folder count and “Saved locally; waiting for Drive” / “Syncing”. | Text typed during upload remains a newer pending version. |
| Ticket accepted | “Processing in Drive”, not “Synced”. | Keep browser jobs until their corresponding results are applied. |
| Partial failure | Show successes and failed items with Retry; keep the editor usable. | Clear only successful matching versions; retain failed and newer jobs. |
| Offline / retry waiting | “Saved locally; retrying when online”, Retry now and Export. | No repeated blocking dialog; queue and bases survive reload. |
| Session expired | Keep local editing available in the already-loaded workspace; show Sign in again before network actions. | Do not silently switch accounts or send an old account’s work with a new account’s session. |
| Drive authorization required | Show Reconnect Drive and the expected identity. | Pause Drive writes; keep queue/bases locally. |
| Conflict / unreadable source | Identify the affected note/source, preserve versions, offer inspection/export. | Never replace valid text with an empty result or mark a failed write as success. |
Use persistent status for the account, not a global “last request succeeded” flag. Counts distinguish note jobs from folder actions; the status must not reset to clean merely because another tab opened a list.
3 · Failure decision table
| Trigger | Required behavior / user message | Recovery and retry rule |
|---|---|---|
| Network failure, timeout, 429 or 503 | Preserve local edits; “Saved locally; Drive is unavailable.” | Retry transient errors with exponential backoff and jitter: 1, 2, 4, 8, 16, 30 seconds cap, jitter ±20%; honor a longer Retry-After. Automatic attempts pause while offline/hidden, resume on return. These are rebuild defaults, not measured current timings. |
| Drive 403 | Inspect provider reason; distinguish permission, quota and rate limiting. | Retry only known transient rate-limit reasons. For storage quota, pause affected writes and ask the user to free space; for permissions, reconnect/request access. Do not retry permanent 403 responses forever. |
| App 401 / expired session | Pause server traffic, retain account-local content and queue. | Sign in again, verify userKey matches the queue owner, then resume. If another account signs in, use a different namespace and leave old work untouched. |
| Drive token revoked / invalid_grant | Show Reconnect Drive; do not label a note deleted. | After reconnecting, verify Drive identity and existing folder/file mappings. A different Drive account requires an explicit new destination/import choice; never automatically replay the old queue into it. |
| Lost push response / missing ticket | Do not create a fresh note just to get a new response. | Retry the original operation with its stable token/file allocation; correlate by ID/key, not result order. A 404 ticket means missing receipt, not successful processing. |
| Acknowledgement arrives after another edit | Keep the newer pending token/document. | Apply only the dispatched version’s outcome; a response without an echoed token is associated with the captured dispatch token. Compare inside the same browser write lock. |
| Changed ETag / revision | Re-read the target, compare base/local/remote. | Merge independent changes; preserve incompatible text in a deliberate conflict copy. No blind overwrite or extra conflict copy on every retry. |
| Incomplete listing / missing source page | Keep cached notes and continue pagination or show a retryable listing error. | An incomplete list does not prove deletion. Confirm missing IDs individually; preserve unconfirmed local creations. |
| Drive file renamed or moved | Keep the stable source ID and refresh display metadata when available. | A path/name is not identity. No duplicate upload to “repair” a rename. |
| Drive source inaccessible, trashed or deleted | Show “Source unavailable” with Retry and Remove reference. Keep annotations. | A 403/404 is not enough to distinguish deletion from lost permission. Do not destroy the note or upload an automatic copy. |
| Malformed note/unknown editor block | Show a note-specific import/read error and preserve local/base/queue plus original bytes. | No empty-document fallback followed by save. Require a validated import/repair or a separate recoverable copy; see Markdown Compatibility. |
| 413 / request or item too large | Show the failed size boundary and retain the full local document/job; offer export. | Split independent jobs into smaller batches. A single oversized note needs a deliberate reduction/export decision; never truncate, drop it from the queue or retry the same oversized body forever. |
| IndexedDB quota, transaction failure or blocked storage | “Not saved locally” remains visible; keep editor text in memory and offer immediate export. | Do not claim durable saving or close the editor automatically. Retry storage after space is available; no destructive clearing of other notes. |
| Another tab holds the write lock | Keep visible work local and show pending, without starting a competing write. | Use the account’s browser lock. Do not break exclusivity using a timeout; after a tab closes, retry normally. |
| Migration receipt mismatch / failed read-back | Stop source cleanup and report transfer not confirmed. | Re-export/reconcile changed legacy data. A GET/status endpoint never initiates destructive migration. |
| Server restart with encrypted tickets | Use account-scoped encrypted files and recover eligible queued work with duplicate-safe writes. | Completed results remain retrievable until consumed. Inspect uncertain tickets before manual cleanup; never bulk-delete queued packets during a restart. |
4 · Deletion and account lifecycle
This table is the required rebuild policy. Confirm the exact selection and action once. A reference, a note, a library item and a Google Drive original are different objects.
| Action | What changes | What remains / confirmation requirement |
|---|---|---|
| Remove a source block from a note | Update that note’s editor content and queue a normal note save. | Original Drive/local media remains. Do not call a media-delete endpoint. |
| Delete a note permanently | Mark its exact ID deleted under the write lock, remove browser projection, enqueue idempotent remote deletion when active; clean note-owned assets according to scope. | Referenced originals and other notes remain. Offline: show “Deleted locally; removal from Drive pending”, not confirmed cloud deletion. |
| Delete a Notes folder | Confirm number of descendant folders/notes; delete that subtree and note-owned assets, not linked source files. | References from surviving notes may become unavailable; preserve their text. Canceling changes nothing. |
| Delete a Files library item | The UI must state whether it removes app placement/metadata or permanently deletes the original source. | Use distinct named actions: “Remove from library” keeps Drive bytes; “Delete original from Google Drive” requires explicit provider warning and confirmation. Do not infer source destruction from a generic note-selection action. |
| Disconnect Google Drive | Revoke/remove the app’s Drive token, stop network sync and retain account-local notes, bases and pending work. | No Drive files are deleted. Explain that queued edits are not yet in Drive; offer Export before disconnecting. |
| Log out | Revoke the current session; stop in-flight requests and remove account content from the visible interface and private HTTP caches. | Do not silently discard unsent IndexedDB work. Explain retained device data; next account uses its own namespace. Explicit device cleanup requires export-or-discard confirmation. |
| Reset app data (optional FR-21) | Provide a preview of app metadata, folder placement, server canvas/media metadata and current device cache to be reset. Cancel is default. | Keep Drive originals and synced Markdown. If unsent notes exist, export/read-back or explicit discard is required. Unlink app-managed Drive indexes rather than deleting remote files. |
| Delete account (optional FR-21) | After export-or-discard decision, remove registry access, all server sessions for this account, tokens, app metadata and note-owned server assets; remove current device namespace after confirmation. | Default retains Google Drive files and Markdown. State that other devices and exported copies require separate cleanup. Do not promise remote browser wipe. |
| Delete remote app data (separate optional destructive choice) | If implemented, enumerate precisely the app-managed Markdown files selected for deletion and separately confirm it. | Never delete an entire Drive, unrelated source originals or the full remote folder based only on its name. This choice is not implicit in reset, logout or account deletion. |
| Restore/purge legacy Trash | Operate only on explicitly selected legacy trash records; apply original folder rules and account ownership. | Do not change new permanent Delete into a universal soft-delete workflow. |
A partly failed destructive operation stays visible with per-item retry information. A successful local deletion is not proof of remote deletion. Tombstones must survive pending deletes; late list/detail/merge responses cannot resurrect the item. Read the API deletion boundaries before implementing provider destruction.
5 · Restore without replaying old destruction
- Stop the app or put the affected account in recovery mode; preserve the incident files, browser namespace and current Drive state. Never restore a stale
sessions.jsonas active login authority. - List what is recoverable: current browser text/queue, user-exported note package, Drive Markdown/history if available, and server metadata/canvas/media backups. Synced Drive content is not a substitute for a versioned backup.
- Restore server metadata/assets into an isolated root with matching ownership. Keep token-encryption keys separate and access controlled; if keys are unavailable, reconnect Drive rather than trying arbitrary secrets.
- Import recovered note content into an isolated account-local namespace and validate/read it back. Do not write a new plaintext note archive onto the server.
- Compare every pending operation/base/revision with current Drive before enabling writes. Stale delete/folder-delete jobs require an explicit review; restored queue data must not replay destruction automatically.
- Reconnect the verified Drive identity. Resolve differences before resuming sync, then test one synthetic note. Keep the original recovery material until the user confirms successful read-back.
14 Setup & Operations owns the backup matrix and operational commands. No fixed RPO/RTO or “zero data loss” guarantee is claimed; record backup age, last confirmed Drive version and unresolved local changes in each recovery report.
6 · Verification and implementation prompt
Implement the required state and deletion tables in prompt/failure-recovery.html.
Map provider errors by reason; never convert an error into an empty successful list.
Use ID/key-correlated outcomes and captured dispatch tokens; keep newer pending versions.
Separate removing a reference, deleting a note, removing library metadata and deleting an original source.
Disconnect/logout do not delete Drive files. Account reset/deletion use the explicit scope table and export-or-discard choice.
For each row write a synthetic test with initial records, event, expected UI state, expected local queue and expected remote calls.
Run V06-V10, V16 and the applicable acceptance cases. Record required behavior separately from observations of the reference app.Use 15 Acceptance & Fixtures for the initial records, numerical budgets and expected results. FR-21 account lifecycle remains optional; reference removal, source failures and safe disconnect are core behavior.