ADR-056: Durable workspace prepare/commit and bounded recovery
Status: Accepted Date: 2026-10-13 Task: SYN-324 (#309)
Context
The previous transaction deleted overwrite backups before SQLite COMMIT. A failed COMMIT could delete the new file without restoring its old bytes, while a failed later rename could strand earlier renames. Process death had no durable instruction describing what to recover. A hash chain cannot repair those losses.
Decision
All workspace transactions acquire an advisory file writer lock, recover prior intent, and then start SQLite with FULL synchronization. Stage writes and deletes without altering current source files. A versioned, fsynced prepare document binds a random transaction ID, database incarnation and ordered relative paths to before/after tree fingerprints. Save the old source under the transaction directory before installing each new file. Preserve replacement permissions.
An additive SQLite migration records its database incarnation and committed transaction ID with the exact prepare digest in the same SQLite transaction as the index changes. A durable matching marker means roll forward and finalize; its absence means restore preimages. Keep backups until that decision is durable. Recovery repeats safely after another crash. A missing/different-incarnation database, modified committed prepare, path escape or unexpected source content fails closed and retains recovery evidence rather than overwriting someone else’s bytes.
Workspace discovery and transaction entry recover under the same writer lock. Concurrent cooperative writers receive a retryable busy result. Co-operating leases explicitly unlock before closing their descriptor, so a descriptor inherited briefly by a subprocess does not retain a finished writer’s lock. Co-operating reindex also acquires this lock. These rules do not make advisory locks constrain external editors or make a filesystem/SQLite pair physically atomic to external readers: observers may see a short installation window before recovery/commit.
Proof boundary
This is the recovery foundation of SYN-324. It does not authenticate actor
identity, preserve an indefinite historical suffix, detect later Markdown edits
or independently prove non-truncation. Governed sealed transitions, retained
payloads and independent witnesses build on this protocol in a separate slice.
Preimages are private recovery material retained until finalization. A local
transaction .gitignore excludes them from ordinary Git staging; forcing a Git
add bypasses that safeguard. Backups must retain a consistent encrypted copy of
source, index and pending journal to retain recovery capability. Existing
Markdown remains the content source and old indexes upgrade additively.
Directory fsync is required on Unix for power-loss ordering; other platforms retain process-crash recovery without a directory durability guarantee. Strict assurance must reject an unsupported platform rather than claim this guarantee. Hardware/filesystem dishonesty, complete disk loss and privileged concurrent rewrites of source plus journal plus database require independent retained evidence and are outside this protocol. Restoring an old database snapshot with the same incarnation is indistinguishable from an uncommitted marker without independent retained evidence; this foundation does not certify historical non-rollback. No blockchain network is involved.
Verification
Deferred foreign-key COMMIT failure, overwritten/create/delete/directory cases, two writers, path and symlink rejection, changed database incarnation and subprocess death around prepare, backup, installation and SQLite commit. Reopen the workspace after each death and verify both exact source bytes and index state, then repeat recovery to establish idempotence.