mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-24 18:36:58 -06:00
* M10: retain local crash recovery on current main * i18n: Backstage Recover in every catalog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
5.7 KiB
Markdown
86 lines
5.7 KiB
Markdown
# Local document recovery
|
||
|
||
Desktop WordCraft periodically keeps a local copy of unsaved work, including new drafts.
|
||
File › Recover lists available copies. The page opens at startup when copies are found.
|
||
Restore opens an unsaved document with no file path. Save As chooses where to keep it.
|
||
Recovery never writes to the original document. AutoSave's permission to write to an earlier
|
||
file is cleared on restore.
|
||
|
||
Continue without restoring leaves copies available. Discard asks for confirmation and deletes
|
||
the selected copy, including its retained generation. It does not delete the editing document.
|
||
After discarding that document's copy, another copy is made only after a further edit.
|
||
A successful save removes that document's recovery copy on the next worker cycle while it
|
||
remains open. Copies left after switching documents can still be explicitly discarded.
|
||
|
||
Documents with a password do not get plaintext recovery copies. Applying a password waits for
|
||
any pending snapshot, removes the current document's recovery generations, and fails if cleanup
|
||
fails. Encrypted recovery is not implemented. Documents opened with a password keep recovery
|
||
disabled for that session. Removing the password allows future recovery again.
|
||
|
||
## Storage and timing
|
||
|
||
Copies live in `recovery/` under the app's profile folder. `WORDCRAFT_PROFILE_DIR` selects a
|
||
separate profile for isolated runs, including preferences, logs and control keys.
|
||
`WORDCRAFT_NO_PREFS` disables recovery as well as preferences. Headless engine/CLI sessions
|
||
and the browser do not create recovery files implicitly.
|
||
|
||
The desktop host checks for a dirty document every frame but snapshots at most once per ten
|
||
seconds, and only when its revision changes. It clones the shared document model for a bounded
|
||
worker queue. Encoding, file I/O, full-document validation and discovery run on the worker.
|
||
An unchanged document does not schedule repeated recovery repaints. The most recent edits can
|
||
be lost if a crash happens before the next snapshot or while its write is still pending.
|
||
|
||
Version 1 snapshots wrap the native JSON document with an id, document name, timestamp and
|
||
monotonic generation. Native JSON now includes media bytes, opaque preserved parts, and
|
||
chart/SmartArt/OLE source markup and relationships. Older JSON without these fields still opens.
|
||
Paragraph cache revisions are transient and regenerated on load; undo history and selection are
|
||
not part of the document snapshot. Recovery preserves the current in-memory model. It cannot
|
||
recover content already lost during import or make later export more faithful than the exporter.
|
||
|
||
Each generation is written exclusively to a temporary file, flushed and synchronized, decoded
|
||
and compared with the source document, then renamed to a new generation. The last valid
|
||
snapshot is retained until its successor is valid. Discovery falls back to an older valid copy
|
||
if the newest is corrupt and reports unreadable files. Abandoned temporary files are cleaned
|
||
only after acquiring that record's lock. Native file locks prevent other running instances from
|
||
restoring or deleting a live record. Unix folders/files use modes 0700/0600; Windows uses the
|
||
profile's inherited permissions. Directory synchronization runs on Unix; Windows synchronizes
|
||
the file but does not yet explicitly synchronize directory metadata. Power-loss durability on
|
||
Windows is therefore unverified.
|
||
|
||
Limits are 256 MiB of encoded JSON per snapshot, 2 GiB of snapshot files per profile, 128 records
|
||
and 16 generations per record during discovery. Ordinarily only two valid generations remain.
|
||
Copies are retained until saved or explicitly discarded; there is no automatic age-based deletion.
|
||
Over-limit writes report an error and retain the previous valid copy. JSON stores binary parts
|
||
as byte arrays, so it can be substantially larger than DOCX. This first version has a fixed
|
||
interval, English fallback for new labels, and no cloud, version-history or browser recovery.
|
||
|
||
## Commands and verification
|
||
|
||
`file.recover={}` reads cached `{enabled,busy,documents,error}`. Each document has `id`, `name`
|
||
and `savedAt`. `{"refresh":true}`, `{"restore":"id"}` and `{"discard":"id"}` queue worker
|
||
operations. Poll until `busy` is false; inspect `error` for failure. A restore is cancelled if the
|
||
document id or revision changes while it loads. Programmatic restores do not open dialogs;
|
||
user restores over dirty work use the existing Save Changes prompt.
|
||
`ui.recoveryDiscard={"id":"id"}` opens confirmation; `{"answer":"discard"}` or
|
||
`{"answer":"cancel"}` answers it.
|
||
|
||
Focused tests are `cargo test -p wordcraft-engine recovery`,
|
||
`cargo test -p wordcraft-engine --test recovery_format`, and
|
||
`cargo test -p wordcraft-ui-egui recovery`. They cover lifecycle, preservation, original-file
|
||
protection, failed writes, corruption, locks, password cleanup and the actual recovery buttons.
|
||
The contribution also uses a native SIGKILL/restart replay in temporary profiles for both new
|
||
drafts and edited existing files. It uses synthetic inputs and hashes the original before and
|
||
after. No Microsoft Word operation is needed for this reliability test.
|
||
|
||
## Maintenance validation
|
||
|
||
The 2026-10-12 rebase retains main’s editable graphic models, native font setup, current
|
||
options module, fixed-width Backstage and filename-aware Save As. The SIGKILL/restart
|
||
observations above belong to the original contribution, not a rerun on this branch. Current
|
||
validation executes the focused lifecycle and preservation tests in temporary profiles and
|
||
all repository CI gates; no daily-driver recovery folder or native app is operated.
|
||
|
||
The desktop host chooses the private recovery profile. Recovery commands accept known
|
||
snapshot IDs, never file paths or a profile override. This internal store is separate from
|
||
the automation roots that constrain opening and saving user documents.
|