Fix: make large memory backups reliable - #1118
Open
StarshipSuperjam wants to merge 26 commits into
Open
Conversation
StarshipSuperjam
marked this pull request as ready for review
August 28, 2026 05:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #822
Purpose
Make off-site Engine memory backup and recovery reliable after one GitHub blob request is no longer enough.
#822 records that the live memory corpus outgrew the single-blob Git Data request path, so backup could fail precisely when the accumulated project history became most valuable.
Impact: Large supported memory stores can be backed up and restored without making a partial publication current or silently exposing a partially restored local store.
Scope
This PR supplies the reliable transport and recovery substrate for the first child of the unreleased three-PR memory-and-recall program.
memory/snapshot_format.py: deterministic v2 compression, canonical multipart layout, integrity metadata, and bounded decode.memory/backup_vault.py: verified ref-last Git publication, whole-publication retry, namespace allow-listing, privacy checks, and deadline outcomes.memory/restore_vault.py: strict v1/v2 fetch and validation, transactional replacement, durable recovery journal, and idempotent crash reconciliation.memory/capture.pyandboot.py: fail-closed writer quarantine during recovery, bounded SessionStart reconciliation, and operator-facing recovery state.Change profile — the shape of this pull request at a glance:
Part of #Nline below says it is one slice of a larger effort.This is a description, not a gate — it never blocks a merge. It is here so you can weigh the change by what it touches, not by its line count.
Impact: The local memory record format stays unchanged; the remote backup representation and its recovery safety become versioned and bounded.
Behaviors
The capabilities this change delivers, each with the test that exercises it.
.engine/tools/memory/test_snapshot_format.py(Boundary and one-unit-over fixtures reject oversized input before publication.).engine/tools/memory/test_backup_vault.py(Git-step, timeout, tip-race, and namespace fixtures keep the prior head authoritative on incomplete attempts.).engine/tools/memory/test_restore_vault.py(Immutable v1 fixtures, v2 total-loss round trips, gzip-bomb bounds, and unchanged-destination assertions cover both readers.).engine/tools/memory/test_backup_vault.py,.engine/tools/memory/test_restore_vault.py,.engine/tools/test_boot.py(Subprocess and signal-aware deadline fixtures prove wall-clock refusal rather than cooperative timeout only.).engine/tools/memory/test_restore_vault.py,.engine/tools/memory/test_capture.py(Crash-point, repeated-recovery, dangling-marker, post-lock quarantine, prior-index, and durable-journal-retirement fixtures cover the recovery protocol.).engine/tools/memory/test_backup_vault.py,.engine/tools/memory/test_restore_vault.py,.engine/tools/test_boot.py(Consent, warning-priority, and typed-outcome assertions keep the safety consequences visible.)Out of scope
The change stays at the transport and recovery boundary chosen for #822 and does not pre-implement the two successor PRs.
Impact: These boundaries keep the minor capability coherent while leaving evidence conversion and mechanized recall to their already-carried successor obligations.
Risk
4 risks, ranked, each with the bound that contains it.
USE-822-001, operator decision 2026-08-27). The operator accepted a private, unencrypted GitHub backup whose replaced objects may remain in provider history; this PR makes that residual explicit before consent rather than claiming stronger erasure or encryption.Impact: The highest-risk mutation path is fail-closed and recoverable; remaining confidentiality limits are explicit operator choices, not hidden guarantees.
Validation
The exact reconciled head passed the affected Engine inventory and focused self-test, plus the multipart boundary suite and destructive-loss restore demonstration.
a792c1491307— candidate evidence, not merge evidence (log sha256:e27c77d6929bcb153cb520947f867a2c269f7b61c2b26d1e4343322d3315afaf)a792c1491307, scope focused (tree23cef14b9a8c) — candidate evidence, not merge evidence (log sha256:2a2214a0f05b55b0dcd858c177b9e8d95c7574afeb7cb1750f895bd08ae27701)validate final importmust verify the live engine-ci run for the submitted head before ready.engine-cirun on the reconciled head stopped at the deliberately provisional PR body contract; all non-contract GitHub checks passed, and applying this complete contract triggers the required full rerun.mcp__engine_memory.health(The exact deferred helper was not discoverable or callable in this session; the saved-file fallback may be out of date, so trust this project's.codex/config.tomland restart Codex to restore the live helper.);mcp__engine_knowledge_graph.health(The exact deferred helper was not discoverable or callable in this session; the saved-file fallback may be out of date, so trust this project's.codex/config.tomland restart Codex to restore the live helper.). That area was not verified against live state.Impact: Mechanical evidence is bound to the final tree; the PR-body-only live failure is visible and is being replaced by a complete, machine-checked contract before submission.
Review
Cold review repeatedly challenged the transport and recovery design; every accepted finding was repaired, and the final operator-bounded pass was scoped to technical integrity and security governance.
FEAS-1(feasibility, serious, rejected). The plan requires reader and writer activation with no intermediate deployment, but it specifies no rollout control that prevents an upgraded writer from publishing v2 while an older Claude/Codex installation or hook can still read only v1. Merging the code together does not make activation atomic, so the release needs an enforceable upgrade/readiness gate before the first v2 publication.FEAS-2(feasibility, serious, partially-accepted). Multipart backup multiplies GitHub API requests, yet retry and adaptive limit handling are explicitly out of scope and the request-count risk has no operating remedy. A transient API failure or rate limit safely leaves the ref unchanged, but it also leaves the system without a fresh backup - the required commit point for Plan 2 - with no specified backoff, resume, or operator recovery procedure.PRODUCT-2(product-intent, nit, rejected). The no-spec selection is asserted rather than demonstrated: the packet calls issue Fix: replace oversized memory-vault blobs with compressed multipart snapshots #822 authoritative but does not say whether semantically affected settled backup or recovery product documents were checked and found absent. The criterion mapping therefore proves coverage only of the selected issue, code, tests, and operator decisions - not that all settled backup promises were considered.RISK-1(risk-governance, serious, partially-accepted). The plan publishes memory backups as unencrypted Git objects without defining the repository-access threat model, secret-exclusion policy, or retention guarantee. Gzip provides no confidentiality, and Git history can preserve sensitive material after the live backup changes, so anyone who gains repository access may gain historical private memory as well.RISK-2(risk-governance, serious, partially-accepted). Ref-last publication prevents readers from selecting an incomplete backup, but failed attempts can still leave unreferenced commits and blobs containing private data. The plan acknowledges cleanup risk without requiring a retention or orphan-cleanup procedure, leaving abandoned backup contents and storage growth ungoverned.R4-DIV-001: Recovery is back inside both promised wall-clock limits; unfinished work stays quarantined for the next bounded retry.R4-DIV-002: Startup cleanup now uses the same lock as foreground restore and cannot delete an active recovery directory.R4-SEC-001: Active recovery material is lock-protected, and quarantine clears only after every prior file is proved or atomically restored.R4-SPEC-001: Interrupted-restore reconciliation now honors the same 10-second or 180-second envelope as its caller.R4-SPEC-002: A live file is treated as recovered only when it matches the exact prior file recorded before publication.R4-TECH-001: Orphan cleanup is now serialized with restore publication and revalidates the marker before every deletion.R5-TECH-001: Fixed recovery retry so it preserves the only verified prior index.R5-TECH-002: Fixed journal retirement so a reported success cannot later resurrect a rollback-capable marker.R5-SEC-001: Fixed the duplicate security finding for verified prior-index preservation.R5-SEC-002: Fixed the writer-lock race so capture cannot enter while restore quarantine is active..engine/tools/memory/backup_vault.py,.engine/tools/memory/restore_vault.py,.engine/tools/memory/snapshot_format.py,.engine/tools/memory/test_backup_vault.pyand 3 more.engine/knowledge/graph.json.engine/tools/memory/backup_vault.py,.engine/tools/memory/restore_vault.py,.engine/tools/memory/test_backup_vault.py,.engine/tools/memory/test_restore_vault.py.engine/knowledge/graph.json.engine/tools/boot.py,.engine/tools/memory/backup_vault.py,.engine/tools/memory/capture.py,.engine/tools/memory/ledger.pyand 4 more.engine/knowledge/graph.json.engine/tools/boot.py,.engine/tools/memory/backup_vault.py,.engine/tools/memory/restore_vault.py,.engine/tools/memory/test_backup_vault.pyand 2 more.engine/knowledge/graph.json.engine/tools/boot.py,.engine/tools/memory/restore_vault.py,.engine/tools/memory/test_restore_vault.py,.engine/tools/test_boot.py.engine/knowledge/graph.json.engine/tools/memory/capture.py,.engine/tools/memory/restore_vault.py,.engine/tools/memory/test_capture.py,.engine/tools/memory/test_restore_vault.py.engine/knowledge/graph.json.engine/policies/provider-exceptions.json,.engine/tools/close.py,.engine/tools/hooks.py,.engine/tools/session_economy.pyand 2 more.claude/skills/engine-start/SKILL.md,.claude/skills/engine-upgrade/SKILL.md,.engine/build-orchestration-obligations.json,.engine/conduct/defaults.mdand 42 more.agents/skills/engine-start/SKILL.md,.agents/skills/engine-upgrade/SKILL.md,.engine/docs/ci-assurance.md,.engine/knowledge/graph.jsonand 1 more50d2e6666081, submitteda792c1491307— 61 files changed, 5629 insertions(+), 585 deletions(-); no re-review was judged necessarySpec-derived acceptance steps
The recall proposal does not govern backup; retired eADRs and stale bases are not authority.
Impact: The review history materially strengthened crash recovery, but the stop condition is now evidence-based: the scoped findings are fixed, their exact seams are regression-tested, and no further panel is being used merely because reviewers can always find more.
Demonstration
A step you can run yourself that drives the changed surface and can genuinely fail.
uv run --directory .engine --frozen -- python tools/memory/test_snapshot_format.py && uv run --directory .engine --frozen -- python tools/memory/restore_vault.py demoImpact: Run it to watch the change work — an unrun step is a promise, not proof.
Files of interest
The paths that most determine this change.
.engine/tools/memory/restore_vault.py— Owns strict v1/v2 restore, pre-mutation validation, transactional replacement, journal phases, and crash reconciliation..engine/tools/memory/snapshot_format.py— Defines the deterministic multipart wire format, integrity manifest, and resource envelope..engine/tools/memory/backup_vault.py— Publishes verified snapshot objects ref-last while preserving the configured namespace boundary and prior head..engine/tools/memory/capture.py— Prevents ordinary memory writers from crossing an active or newly appeared restore quarantine..engine/tools/test_release_gate.py— Locks the release-level multipart recovery and compatibility assertions.Impact: These files define the wire contract, publication commit point, local recovery transaction, writer exclusion, and release acceptance boundary.
AI involvement
How this change was produced and who decided what.
Impact: AI judgment is load-bearing in the crash-recovery protocol and finding adjudication, and is bounded by cold adversarial review, explicit operator calls, exact-head regression suites, and an operator-runnable destructive-loss demonstration.
Release-Impact: minor — backward-compatible new capability or an explicit deprecation