Controlled upgrades and state recovery
September 9, 2026 · View on GitHub
Desktop, Web, TUI and headless task entrypoints use the shared background service by default. An incompatible native client does not kill an existing service or silently open another application/database. Complete the platform and stability checks before publishing an installation.
Prepare an upgrade
deepcode service prepare-upgrade --output /safe/path/deepcode-before-upgrade
# Update the Python installation or Desktop through its normal installer.
# If a login item was installed, refresh its executable/environment definition:
deepcode service install --at-login
deepcode service start
prepare-upgrade drains and stops the selected service, then creates an offline
snapshot. --database chooses a non-default database. --timeout bounds drain;
--cancel-running is an explicit alternative. A failed drain preserves service
admission. A failure to snapshot after stopping leaves the service stopped and
reports the failure. It does not claim the upgrade is prepared.
snapshot --output PATH creates the same snapshot when hosts are already
stopped. The output path must be new and outside the Session/revision directories.
The service records its actual state layout, so a custom Session directory is
not guessed from a different CLI environment after shutdown. For older services
without layout metadata, use the original DEEPCODE_HOME and, if needed,
--sessions PATH.
Snapshots contain the SQLite database, canonical Sessions, home configuration, Provider credentials, MCP credentials and private Provider revisions. Locks exclude live database owners, active Session resources and concurrent canonical/ configuration/credential mutations. Pending Turns must be settled. SQLite's backup API, per-file checksums and an atomic manifest protect the snapshot. Symlinks are rejected. Snapshots retain private permissions and contain secrets; they are local recovery artifacts, not diagnostic exports.
Session indexes and lock files are disposable or retain their existing inodes. Project working trees, installed Plugins/tools, environment-variable values, application installers and old executables are not copied into this snapshot. Use the project's normal version control/backup for source-code recovery.
Frozen Desktop backends pin a complete bundle in a private versioned directory.
Client updater replacement does not delete that service bundle. Install the new
service definition from the updated executable (its --service install --at-login entry is equivalent to the CLI command). An older bundle remains
available for controlled rollback; runtimes are not automatically deleted.
Roll back
Prefer reverting the client installation when data remains compatible. An older runtime reads the database schema first and refuses a newer schema before changing its journal mode or starting migration. No schema downgrade is assumed.
To restore an entire pre-upgrade state, stop every writer and explicitly run:
deepcode service restore --snapshot /safe/path/deepcode-before-upgrade --replace-data
This replaces runtime data written after the snapshot. It does not reverse
shell commands or file edits in project directories. All paths must match the
snapshot's original state locations. Checksums and quiescence are checked before
replacement; the current state is preserved in a separate before-restore-*
snapshot first.
An interrupted replacement leaves a durable journal. Application startup refuses to use mixed data until the same restore command is resumed. Repeating that command is idempotent. Do not remove the journal manually. Restored pending Provider login flows are invalidated. On first startup, restored Goals and interval Automations are paused before scheduling/recovery; review them and resume explicitly. Historical events and canonical records are retained.
A before-restore forensic copy may include interrupted work from a failed newer runtime. The automatic restore path rejects snapshots containing unsettled Turns; inspect/recover that state before using it as a restore source.
Stability acceptance
scripts/foundation_soak.py runs a controlled, no-key Agent fixture through real
service processes, HTTP/WebSocket authentication, cross-client approval, repeated
input IDs, incremental replay, PTYs and ordinary client disconnections. It
measures actual elapsed time and writes live status/samples under a new private
root. Run it from an isolated, fixed source checkout:
python scripts/foundation_soak.py --root /safe/path/deepcode-soak --seconds 86400
The default cadence is 20 seconds, with ten observer connections per cycle. It checks a single tool effect per task, settled queues, zero peer/RPC residue at shutdown, p95 attach ≤2 seconds, replay ≤3 seconds and local startup ≤10 seconds. It records worker RSS, file descriptors and threads. Growth of more than 64 MiB RSS or 16 descriptors/threads over the warm baseline fails the run for investigation; raw trends remain available for review. These budgets are part of this test, not claims about every machine or workload. Long runs also reject a scheduling/sleep gap over 60 seconds (or three intervals), and sustained growth over 16 MiB across four increasing RSS quarter medians.
A short --seconds run validates the harness only. It is never evidence for a
24-hour pass. This fixture does not replace real-model, native installer,
operating-system login, or Windows acceptance. Linux container tests require a
proper init process to reap child processes (docker --init).