Transaction and operational recovery
Day Shift uses repository transactions for multi-path mutations such as structural workspace migration and promotion. The safety model is evidence-first: inspect the command result and retained operational state, decide whether the correct outcome is no retry, a fresh plan, roll-forward, or restore, and invoke only the owning command’s explicit recovery lane.
Files that describe operational state
Section titled “Files that describe operational state”The repository write lock is .day-shift/state/write.lock. It records a transaction id, opaque owner token, host, process id, heartbeat, and lease expiry. Transaction staging lives under .day-shift/state/transactions/<transaction-id>/; a complete retained transaction has a restricted manifest.json, restricted staged files, and a status record. Successful recovery writes a sanitized receipt under .day-shift/state/recovery-records/. Migration and promotion also bind a durable domain record as one of the transaction’s canonical writes.
These paths are implementation evidence, not a manual repair interface. Never edit a manifest, status, staged file, lock, durable record, or recovery receipt to make a command appear terminal.
Compact state storage and large files
Section titled “Compact state storage and large files”Current writers use version 2 for trace-link dispositions and workspace migration records. Dispositions share repeated evidence path sets; migration records can derive repeated path lists from retained mappings when they reconstruct exactly. Existing version-1 records remain readable, and merely reading them does not migrate them. These changes preserve expanded evidence and provenance; they do not make lifecycle decisions.
Migration records are authoritative evidence, not disposable caches. Large compressed backups under .day-shift/state/storage-backups/ preserve original bytes and are not read as active disposition evidence. Full expanded CLI output can still be large after on-disk compaction. Prefer supported projections or selected-record inspection. New projects may have no migration records at all.
Use a current CLI/runtime for every consumer before upgrading storage: older readers may reject or fail to use version-2 evidence. The checkout maintenance scripts for existing disposition and migration records are separate from normal workspace migrate; their read-only previews, revision-bound apply and backup procedures are documented beside their codecs in the source tree. Do not delete records or rewrite transaction evidence to reduce file size. A compacted file can still exceed a configured evidence limit; see performance settings.
Transaction sequence and irreversible boundary
Section titled “Transaction sequence and irreversible boundary”A write-capable command captures destination-presence and content-revision preconditions during preview. Apply acquires one exclusive lock, revalidates those preconditions under the lock, stages every intended write and retirement with restrictive permissions, and only then replaces canonical targets in deterministic path order. Each replacement is postcondition-checked before the next operation.
Before the first canonical replacement, an interruption is a truthful no-write outcome. Between replacements or during postcondition verification, the outcome is incomplete and retained evidence identifies which targets remain at their before, intended, retired, missing, or diverged state. After every postcondition verifies, cleanup failure does not erase the committed verdict; it leaves the verified manifest and status available for inspected cleanup.
Classify before acting
Section titled “Classify before acting”Use the write command’s JSON result and read-only inspection evidence:
| Evidence | Safe response |
|---|---|
| Active lock or owner liveness not safely disproven | Wait. Do not take over or delete the lock. |
| Stale lock without matching transaction evidence | Stop. Expiry alone is insufficient for takeover. |
| Preview/apply precondition conflict | No staging or canonical write occurred; inspect the changed path and generate a fresh preview. |
no-write with retained staging |
Preserve it until inspection proves cleanup or an owning recovery action is safe. |
incomplete with all current targets matching before or intended revisions |
Use the exact offered restore or roll-forward action with the current fingerprint. |
| Any diverged target, invalid or missing manifest, missing staged bytes, or mismatched durable record | Stop. Recovery must reject rather than guess. |
committed with cleanup errors |
Verify terminal postconditions and receipt evidence, then resume the supported cleanup path. |
An expired lock may be taken over only when owner liveness is disproven and matching transaction evidence makes ownership unambiguous. The same transaction id and exact current evidence fingerprint must still match after lock acquisition.
Choose roll-forward or restore
Section titled “Choose roll-forward or restore”roll-forward applies the verified intended staged bytes and retirements that remain incomplete. restore replays verified before-bytes and reverses retirements when every affected target can be returned safely. Neither action is a general undo command.
The recovery call must bind the retained transaction id, exact current evidence fingerprint, action, bounded actor, and bounded reason. The owning command also requires its recovery confirmation token. Migration uses workspace migrate --recover; promotion uses the matching task promote --recover or work promote --recover lane.
Recovery is fail-closed. A stale fingerprint, live lock, diverged target, unavailable action, unsafe restore, missing staged evidence, wrong transaction kind, or conflicting recovery receipt causes no recovery mutation. Generic transactions do not gain a public recovery command merely because low-level staging exists.
Interrupted recovery and receipts
Section titled “Interrupted recovery and receipts”If recovery is interrupted after target changes but before receipt persistence, the next inspection can prove that postconditions already match and let the same exact action finish the receipt. If the receipt exists but cleanup was interrupted, an identical current proof can resume cleanup without rewriting targets. A receipt whose transaction, action, durable-record binding, fingerprint, or sanitized audit fields differ is a conflict and must remain untouched.
A stale lock-release residual can be resumed only when terminal receipt evidence matches and liveness is disproven. Never delete the lock first; recovery owns the verification and cleanup order.
Growing and recovering a lifecycle run
Section titled “Growing and recovering a lifecycle run”New lifecycle runs use a version-2 journal. The header stays bounded while immutable segments retain entries, contract-revision checkpoints, and paged invalidation lists. The former version-1 limits of 512 entries, 128 checkpoints, and a 512 KiB manifest do not limit the lifetime of a version-2 run. Individual results and segments still have byte limits. Status and resume verify current files and retained evidence; a stored header never substitutes for a current lifecycle review.
Upgrade an existing run in its original directory:
day-shift resume run upgrade-storage --run-directory .day-shift/state/runs/run-example --dry-run --format jsonday-shift resume run upgrade-storage --run-directory .day-shift/state/runs/run-example --apply --expected-source-snapshot <expectedSnapshot> --format jsonUse the exact expectedSnapshot returned by the preview. Upgrade preserves the original manifest under legacy/sha256/, all historical identities and evidence, and the existing lifecycle posture. It grants no readiness, disposition, or close authority. An interrupted upgrade retains its publication marker so the same command can finish. Older runtimes reject the new schema; continue with the upgraded runtime.
A bound command records invocation ownership before execution and saves each child result and its final envelope before publishing the batch. If capture fails, retain the command’s original outcome and inspect its durableRecoveryPath:
day-shift resume run recover-capture --run-directory .day-shift/state/runs/run-example --dry-run --format jsonday-shift resume run recover-capture --run-directory .day-shift/state/runs/run-example --apply --expected-source-snapshot <expectedSnapshot> --format jsonRecovery requires a completed saved result, unchanged publication inputs, verified bytes, and proof that the original owner is no longer running. It never reruns the underlying command. Publication and cleanup are idempotent. A missing final envelope leaves the outcome unresolved; interrupted older invocations cannot be reconstructed from truncated terminal output.
Use resume run status --page-lane entries --page-size 25 for bounded history output. Inspection still checks the full retained history, so its cost grows with the run. resume run pack writes version-2 packs as a small header with immutable journal and evidence objects, retaining the source unchanged.
Stop conditions
Section titled “Stop conditions”Stop normal writes when any target is diverged, when operational metadata is malformed, when the only verified copy is staged, when a live lock exists, or when the owning command does not offer the desired action. Record the failed command, transaction id, current fingerprint, changed or uncertain targets, residual risk, and next read-only inspection.
Do not represent manual deletion, force replacement, automatic retry, review-driven mutation, demotion, or arbitrary transaction rollback as shipped recovery. Use Migrate an existing workspace for the full structural migration protocol and Troubleshooting for symptom-driven triage.