Skip to content

fix(memory): preserve trusted state head across ledger writes - #9739

Merged
Git-on-my-level merged 1 commit into
BasedHardware:mainfrom
Git-on-my-level:codex/fix-memory-state-head-contract
Jul 14, 2026
Merged

fix(memory): preserve trusted state head across ledger writes#9739
Git-on-my-level merged 1 commit into
BasedHardware:mainfrom
Git-on-my-level:codex/fix-memory-state-head-contract

Conversation

@Git-on-my-level

@Git-on-my-level Git-on-my-level commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Preserve the trusted canonical V3 state-head fields when the legacy memory ledger advances its independent head.
  • Repair a previously clobbered state head only from the transactional canonical apply-control record; malformed or absent control remains fail-closed.
  • Centralize the trusted state-head schema so canonical apply and legacy ledger writers use the same contract.

Root cause and durable guard

The legacy ledger replaced users/{uid}/memory_state/head with only current_head_commit_id, deleting the canonical reader's required uid, generation, and sequence fields. The V3 reader correctly failed closed with 503 as a result. Both ledger transaction variants now retain a validated canonical header or recover it from canonical apply control, and regression tests execute the ledger transaction followed by the V3 trusted reader.

Product invariants affected

  • INV-MEM-1 — the fix preserves the single canonical memory-state contract and does not add a tier or tier-specific collection.

Verification

  • backend/.venv/bin/python -m pytest -q backend/tests/unit/test_memory_ledger.py backend/tests/unit/test_memory_apply_store.py backend/tests/unit/test_v3_account_generation_source.py (45 passed)
  • make preflight (passed)
  • backend/test.sh exercised the full runner, but an unrelated existing fast-unit CPU-duration ratchet rejected tests/unit/test_activate_task_intelligence_dogfood_user.py::test_missing_control_plans_explicit_read_at_default_generation after its assertions passed (0.22s over 0.12s limit).

Review in cubic

@Git-on-my-level
Git-on-my-level merged commit 6aca211 into BasedHardware:main Jul 14, 2026
24 of 27 checks passed
kodjima33 added a commit that referenced this pull request Jul 15, 2026
… (#9783)

POST /v3/memories returned retryable 503s (Sentry OMI-DESKTOP-2F3, 23
events / 7 users) because the legacy memory-ledger transaction performed
a Firestore read after staging writes: `_state_head_write_payload()`
(added in #9739) reads `memory_state/apply_control` as a fallback, but it
was invoked inside `transaction.set(state_ref, ...)` — after
`projection_writer(transaction)` and the commit write. Firestore rejects
this with `ReadAfterWriteError`, which `create_memory()` maps to HTTP 503.

Root cause / durable guard: all transactional reads must precede the
first write. Build the state-head payload (including the apply_control
fallback read) before staging any writes, in both `_append_commit_transaction`
and `_append_commit_with_builder_transaction`, then reuse it for the final
state_ref write. #9739's canonical-head preservation is unchanged.

The existing tests used a lenient transaction fake that ignored ordering,
so they missed the regression. Added a strict transaction fake that raises
ReadAfterWriteError when a get() lands after a set(); the two new tests
fail on the pre-fix code and pass with the fix, covering both the direct
and builder append paths through the apply_control fallback.

Tests: `pytest tests/unit/test_memory_ledger.py` (29 passed); verified the
two new tests fail on pre-fix source (ReadAfterWriteError) and pass after.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant