Repository navigation
fix(memory): add atomic owner-scoped idempotent write receipts - #310
loveRhythm1990 merged 5 commits into
Conversation
ReviewIs the issue real?Yes. Looking at the pre-existing single-store path (
So the stated problem (no exact-key idempotency, cross-session contamination risk, duplicate-on-retry) is real and verifiable in the current code, not just asserted in the PR description. Is the fix correct?The core mechanism is sound: However, I found two real correctness bugs in the payload-fingerprinting that backs "conflicting payload" rejection:
None of these three issues invalidate the overall design — PK-arbitrated insert as the concurrency primitive is the right approach — but (1) and (2) are concrete ways the idempotency guarantee can silently break in exactly the scenarios this PR is meant to harden, and should be fixed before Astra depends on this contract. P1 — bug (1) alone means the idempotency guarantee this PR exists to provide can silently fail for any retry with multi-key |
aptend
left a comment
There was a problem hiding this comment.
Deep review of exact head f37dd5409a6dd9301766b6f2a5d4fe152e65d13b, base/merge-base 4c99ae4e4ffabb5f2f0f24c5121e0f99c458674e. Read the full eleven-file diff, complete review/comment/thread history and all three commits in the series, including the follow-up against the prior feedback; traced auth/owner routing, branch resolution, primary-key insertion/replay, tombstone behavior, approximate dedup and response/filter consumers.
The earlier concerns are closed: recursively sorted fingerprints, protected reserved metadata, response omission and exclusion of receipt rows from ordinary dedup are present and the public regressions pass. The original HashMap concern is not repeated as an unfixed defect; the dependency graph's default JSON map was already ordered, and explicit recursive canonicalization now also protects preserve_order consumers.
One independently reproduced blocking idempotency defect remains: omitted branch hashes as main, while storage follows the checked-out branch. The same owner/key/payload in the same actual branch can thus acquire two different persisted receipts depending only on whether the branch is explicit. See inline reproduction.
Validation:
- Complete repository Unit Tests lane (
memoria-api,memoria-core,memoria-service,memoria-mcplibraries): 241 passes, zero failures/ignored, Rust 1.94.0, locked dependencies. The new identity unit also passed separately. - Unmodified committed service DB test and public HTTP test both passed twice against a fresh, test-owned MatrixOne container with synthetic identities/databases; no QA/production service or live embedding/LLM provider was used. Server: cached official
4.2.1-hotfix-20260916, image digestsha256:2f4c6ecfe084912474a28ebf2adb932876abbbb6b123bc17677ed65668e1ea4e. These cover eight concurrent replays, conflicts, owner separation, deletion, ordinary dedup protection, reordered nested metadata and reserved-field rejection. - Additional public authenticated HTTP branch counterexample used the existing multi-db fixture and deterministic mock embedder. Two isolated owners/rounds each produced two different IDs and two active rows in reviewb, zero in main for the same key/payload; explicit-branch replay returned the same explicit ID as a positive control.
- Exact-head Unit/DB/Clippy CI checks are green; the DB job log independently confirms both new integration tests actually ran and passed. Those tests do not exercise omitted-versus-explicit checked-out branch identity.
- Initial current-source MO harness attempts failed on temporary build capacity or unsupported SQLx default handshake character set 224 in an admin fixture connection. They are environment/fixture diagnostics, not Memoria product findings or green HTTP evidence; the final official-container runs above used the original assertions without production/fixture patches.
Please derive receipt identity from the same frozen effective branch/table used for lookup and insert, or require an explicit branch for keyed writes and fail closed on omission. Add checked-out/explicit alias and checkout-during-await coverage. Test-only probes and the disposable server will be removed after submission.
aptend
left a comment
There was a problem hiding this comment.
APPROVE — deep re-review of exact head ed65b7a7ce0e259e1d27b096319e4290f363ff45, base/merge-base e3665e8def58ed5f58b109fdd4594feed9df0398, compared with my previous reviewed f37dd5409a6dd9301766b6f2a5d4fe152e65d13b.
The branch-scope P1 is CLOSED in the implementation. Keyed writes resolve the effective checkout/normalized explicit branch once, derive the receipt from that same branch and retain the routed SQL store/table through lookup, embedding, insert and conflict replay. Omitted and explicit aliases now use one identity and one destination; a checkout during await cannot redirect the operation, and missing/deleted branches fail closed. No remaining blocking finding established.
Read all eleven current changed files, the incremental repair and all review/inline/author conversation/thread history (one resolved thread). Reconciled upstream-only metrics/MCP changes separately. Traced authentication/scoped owner/group-main guard, key validation and canonical payload hashing, PK-arbitrated winner/loser paths, sensitivity/embedding error/cancel, soft-deleted receipt behavior, ordinary dedup exclusion, reserved metadata/filter protection, response omission and batch rejection. The frozen Arc/store/table is request-owned, not a connection held across embedding; only successful insertion schedules/report new-memory work. Retention/deletion limits and explicit-branch advice for retries across separate checkout changes are documented.
Verified/reused primary CI evidence from run 38617576754, Rust 1.85.0:
- Unit Tests: api 106 + core 22 + mcp 44 + service 69 = 241 passing library tests, zero failures/ignored.
- DB job 116037610774, original logs:
owner_scoped_store_replays_real_receipts_and_rejects_missing_keysandreceipts_follow_resolved_branch_and_checkout_cannot_retarget_inflight_writeboth PASS (2/2, zero ignored). Read their actual assertions: both keyed endpoints, padded explicit branch, one branch-local row, deterministic semaphore-paused embedding/checkout, frozen destination, cross-branch separation, missing-branch rejection, metadata protection and auth/conflict controls. - Committed
concurrent_replays_have_one_receipt_conflicts_do_not_mutate_and_deletion_is_finalPASS (1/1, zero ignored): eight concurrent callers, owner/session/payload conflicts and inactive receipt rejection. - Check & Clippy succeeds; source whitespace check and isolated-worktree source state are clean.
CI checks out synthetic merge 9ad247dbdc9261862362a098eb194edf492d449d, not the literal head commit. I verified its parents and identical full source tree da0812c5793882194525f2727c9cb8a91609eecb against the exact review head before reusing the results. No fresh local Rust/DB rerun or live-provider/QA/production write is claimed. The earlier independent failed branch counterexample and original tests remain historical, not relabeled current runs. Head/state rechecked immediately before submission.
## Summary CLI strict recall compares Astra session identity with Memoria record ownership even when UC or connected personal accounts use distinct namespaces. Explicit close also races detached extraction, and process-local governance cannot recover a partial archive/purge after restart or a lost response. This completes the remaining owner/finalization work from #996 after #997: - Strict recall carries fresh Server attestation of both identities and session; CLI validates that attestation and every row before admitting content or tracking a working ID. - CLI exit and explicit close use the canonical Server-owned memory lifecycle. Exit preserves resumability and cannot fall back to local purge after an unsuccessful handoff. - A durable owner/session journal freezes extraction inputs, uncertain write payloads, consolidation plans and stage receipts. Database leases and the existing Session fence coordinate separate server instances. Missed handoffs recover from immutable canonical context; purge waits for execution settlement and verified final-turn freshness, and removes only captured working IDs. - Required failures retain source state and retry with bounded backoff. Optional reflection/scene retries reuse completed episode and purge stages. Current authority is resolved for each operation; queued input/credentials are not exposed through the owner-only status endpoint. Session deletion removes queued journal inputs. ## Related issue Refs #996. This is the remaining implementation after #997; the issue should remain open until the matched backend protocol is available and the changes have been reviewed together. **Backend dependency:** [matrixorigin/memoria#310](matrixorigin/memoria#310). Deploy Memoria's dedicated idempotent-store endpoint before enabling this Astra image. The endpoint's owner/branch/key primary-key arbitration is needed to safely replay a request after its response is lost; vector similarity deduplication cannot provide that guarantee. Old backends fail before a write and never authorize cleanup. Neither PR should be merged automatically. ## Change type - [x] Bug fix - [x] Documentation - [x] Test ## User and compatibility impact **Database rollout:** this adds fresh schema contract `2026-10-14-v102`. Astra rejects older completion markers and does not migrate databases automatically. Existing deployments require an operator-owned database rollout/preservation plan; this PR performs no reset. Upgrading only the image is insufficient. Adds authenticated `POST /sessions/{session_id}/memory/finalize` and `GET /sessions/{session_id}/memory/status`. A queue acknowledgment is not a confirmed archive. CLI retains its five-second exit budget; extraction/finalization retain 20/60-second attempt budgets. Worker leases last 90 seconds, polling every two seconds with concurrency two and retry backoff from five seconds to 16 minutes. Strict session recall adds a `memory_scope` envelope and requires a matched Server for the updated CLI. UC, connected personal and explicitly enabled self-hosted owners remain separate from Astra session authorization. Consent revocation/disconnect stops future remote operations without promising remote erasure. Astra Session deletion removes queued inputs; Memoria's retention and explicit deletion govern remote records. ## Architecture and complexity delta - Canonical owner extended: existing Session lifecycle, context coordinator and memory extraction/governance algorithms. One new owner/session work journal stores retry checkpoints, not a second session/context or memory authority. - Existing implementations/callers searched: CLI startup/cleanup, memory proxy and authority resolver, extraction runner/service, turn finalization, explicit close, write fence, context heads and deletion catalog. - Superseded paths removed: untracked close-time governance task and its process-local coordination. The managed background recovery task participates in Server cancellation/shutdown. Remote CLI maintenance now queues canonical Server work. - Direct/in-process providers retain the same governance algorithm and ordinary transport contract. Only bounded destructive working cleanup holds the Session fence across network I/O; optional reads, writes and reflection use short preparation/checkpoint transactions. ## Verification Current head: `40cbadd5`, based on main `7375abe0`. Targeted E2E Clippy, formatting and whitespace checks passed locally. The repaired handoff/resume regression passed real database CI and integration passed 295/295; the optional live harness also passed 8/8. All current test and static-check workflows have passed. At `26271bdc`, core-services passed 481/481 and core-runtime passed 104/104, including the memory regressions; earlier evidence below applies to its recorded commits/runs. Passed locally on rebased main (synthetic fixtures; designated isolated MatrixOne): - `make format-check` - `cargo test -p astra-memoria --lib` (19 tests) - `cargo test -p astra-services --test session_memory_lifecycle_db_it -- --ignored --test-threads=1` (3 tests on fresh v102; rerun of the freshly built test binary passed) - `cargo test -p astra-services --test schema_assertions core_schema_catalog_matches_live_idempotent_bootstrap -- --ignored --test-threads=1` (1 test; idempotent fresh bootstrap, generated catalog, rejection of unsupported old marker) - `cargo test -p astra-runtime --lib session_memory::durable::tests -- --ignored --test-threads=1` (4 tests before the schema-version follow-up; local package debug=0) - `cargo test -p astra-cli --lib cli::session::session_startup::tests::cli_ -- --test-threads=1` (7 tests; local runtime/CLI package debug=0) - Fresh CLI test binary: `browser_identity_services_replace_generation_and_private_skill_cache` and `escape_browser_login_restores_memory_and_external_discovery` (2 tests) - `cargo clippy -p astra-memoria -p astra-services -p astra-runtime -p astra-cli -p astra-thin-client --all-targets -- -D warnings`; CLI all-target recheck after fixture updates - `python3 scripts/schema/test_schema_inventory.py` (47 tests) - `git diff --check` The first CI run exposed a missing schema inventory classification and stale login-maintenance mocks; this follow-up classifies the journal and aligns those fixtures with Server handoff. The positive owner fixture now contains a real working-memory entry rather than arbitrary text. An unrelated macOS PTY timing case also failed on that head; terminal-reader code is unchanged. Updated-head CI is rerunning and must pass before the draft becomes ready. A full repository `make check` was not completed locally; the relevant all-target Rust checks are listed above. No online/live provider tests were enabled locally. Public entrypoints and unhappy paths: authenticated exit/close/status; unauthenticated finalization; valid UC/personal/local owner namespaces and foreign identity/owner/session; unsuccessful/malformed queue acknowledgments without purge; lost episode/scene/delete receipts; optional-stage retries without re-archive; stale final-turn watermark; unsupported backend retaining working memory; lease takeover and old worker rejection; canonical-context recovery after missed handoff. Database verification uses newly created isolated local MatrixOne databases and synthetic identities. No QA/production mutation or live model calls. CI's runtime online lane explicitly designates its isolated database for the shared test guard. Rebased on current upstream `main` at `89dcde88`. Host review used the open-code-review delegate skill: all 28 selected code/build files reviewed; all four documentation changes also read; 0 skipped, 100% change coverage. ## Final checklist - [x] Tests added at the owning layer, including public entrypoints and failures. - [x] Public/design/configuration and test documentation updated. - [x] Diff checked for credentials, private URLs, customer data and generated files. - [x] Conventional Commit-style title. ### Review follow-up: explicit Memoria branch scope Memoria #310 now derives keyed receipt identity from the resolved storage branch and freezes its routed store/table across embedding. Astra's durable HTTP adapter explicitly selects main for archive/working writes, typed/strict reads and exact-ID cleanup, matching its deterministic main-scoped receipt verification. Normal direct memory tools retain their existing checkout behavior. The loopback/MatrixOne durable lifecycle regressions now enforce the main selector in write bodies and read/delete queries. This branch also includes current main (`d2d44040`, #1006), including the upstream live-harness model/cache configuration and child-answer verification changes. Format checks and live-harness workflow contract tests pass (6 passed, 2 optional skips). Runtime all-target Clippy passes with warnings denied against this combined source tree. The updated online durable lifecycle regressions are executing in CI; full CI is still running. Follow-up review covered all 5 changed Rust files across Astra and Memoria (100%, zero skips); both accompanying protocol/design document changes were also read. ### Live harness follow-up: missing configured model The missing-model case required `invalid_request` without constraining how the model was selected. The canonical admission contract distinguishes an unknown configured name (`model_selection_invalid` / `invalid_request`) from an unknown Offering ID (`model_offering_not_found` / `tool_unavailable`). The probe now supplies an explicit fixed configured-name request and additionally verifies its policy mode, selector kind and requested name in canonical execution evidence. The failed result, exact error kind, no-child and no-fallback assertions remain required. The previous head's deterministic checks and four durable memory DB/loopback regressions passed. One rerun also passed the recovered-validation quality case, while the ambiguous missing-model assertion failed again. This follow-up changes only that fixture; it does not change runtime admission, quality thresholds or CI gates. YAML/JSON parsing, diff and format checks pass. All 47 strict case-loading tests pass. The live regression on `c653517` passed 8/8 cases. The stricter fixture also required updating the synthetic baseline in `shipped_rejection_cases_reject_extra_attempts_with_different_outcomes`; `5c9a3cd` supplies the complete fixed-name request and adds a negative Offering-ID-selector control, while retaining the extra-attempt rejection checks. The complete Harness library suite passes serially: 607/607, zero skipped. The initial default parallel local run hit six process/readiness timeout tests; the original timeouts were retained. All-target Harness Clippy passes with warnings denied. Full CI on `5c9a3cd` passes (40 successful checks), including the 8/8 live regression, core + Server-hooks shard, online memory regressions and CLI inventory/aggregates. Follow-up review covered both changed files (2/2, 100%, zero skips). ### Review follow-up: missing journal and complete inventory Commits `b56f26f7` and `94228684` address both new recovery findings: - Explicit close now ensures the owned journal row exists inside the canonical close transaction before recording finalization intent. A failed first bounded handoff can no longer produce a successfully closed session that recovery never discovers. The new database regression starts with committed canonical context and no journal, closes through `DatabaseSessionService`, reconstructs the store after restart, and recovers the final extraction and governance intent. - Finalization now uses a complete inventory mode in the shared HTTP typed-list implementation. It follows cursor pages under the existing ten-second read budget, accepts up to 4096 memories only with explicit pagination completion, and rejects overflow, invalid/missing/repeated cursor evidence, duplicate rows, and failed pages before freezing a plan. Ordinary typed recall retains its top-k behavior. Every page preserves the owner/session/type scope and the durable `main` branch. - HTTP regressions cover 501 rows, exact-bound completion, overflow, malformed pagination, repeated data, foreign ownership, and later-page failure. Database/HTTP regressions cover the final working receipt on either page and verify overflow leaves the plan unset, working memory retained, and maintenance pending without archive or delete calls. The new 501-row database regression also exposed a journal read defect: MatrixOne's default `CAST(state_json AS CHAR)` truncates at 65535 characters. All five journal read paths now decode the native JSON column via SQLx, with its JSON feature explicitly enabled. An additional database regression round-trips a large multibyte input through close, status, claim, fenced reload, and canonical recovery lookup. No schema change is required. Validation for this follow-up at `94228684`: - Local: all five isolated MatrixOne lifecycle regressions pass, including absent-journal close/restart and the large multibyte journal round-trip; formatting, diff checks, and all 47 schema inventory tests pass. The task-owned local database was removed afterward. - Exact-head CI: all eight added regression tests pass (four HTTP pagination tests, two durable runtime database tests, and two lifecycle database tests). Runtime offline tests, online core-runtime/core-services checks, and all six Static Checks jobs pass. - Full exact-head Test Suite: **28/28 jobs passed** ([run](https://github.com/matrixorigin/Astra/actions/runs/38672051447)). Static Checks: **6/6 jobs passed** ([run](https://github.com/matrixorigin/Astra/actions/runs/38672051457)). - The optional live harness first stopped in model-availability preflight with zero executed cases and a registered-but-inactive model. Attempt 2 is running (currently building Server/CLI/harness), with configuration and assertions unchanged ([run](https://github.com/matrixorigin/Astra/actions/runs/38672048150)). This optional lane is not yet verified as passing. - Incremental review covered all seven code/configuration files, zero skipped (100%); the memory design documentation was also reviewed and updated. ### Review follow-up: preserve confirmed working receipts Commit `a059246d` addresses [the stale-cleanup receipt review](#1005 (comment)). Before this fix, a cleanup read could replace a confirmed new working receipt with a stale ID from the first 16 backend rows. A successful deletion with a lost response could then leave completed extraction permanently unable to finalize. The typed-read adapter now preserves receipt identity once the current generation has prepared its working write. Pre-write observations can still update the previous generation's receipt, while frozen-plan reads retain their existing behavior. The fix reuses the existing durable write checkpoint; it adds no journal fields or alternate lifecycle. Two MatrixOne/loopback regressions exercise the canonical extraction writer and actual stale cleanup, acknowledged/lost delete responses, extraction settlement, owned explicit close, three restart-style claims, plan reconciliation, and final governance cleanup. The lost-response scenario places all 16 stale snapshots ahead of the confirmed hash-derived receipt in the backend list. The happy control uses one stale snapshot to stay within the existing one-second best-effort cleanup budget. Model inference uses the supported rule fallback, with no paid-provider dependency. Validation at `a059246d`: - Local runtime library/test compilation (`cargo check -p astra-runtime --lib --tests`), formatting, and diff checks pass. - [Online core-runtime](https://github.com/matrixorigin/Astra/actions/runs/38698235410/job/116299887310): **98/98 tests pass**, including both added close/restart regressions. The job log explicitly records each new test as PASS. - [Complete Test Suite](https://github.com/matrixorigin/Astra/actions/runs/38698235410): **28/28 jobs pass**, including CLI, integration, and macOS terminal checks. [Static Checks](https://github.com/matrixorigin/Astra/actions/runs/38698235321): **6/6 jobs pass**. - [Optional live harness](https://github.com/matrixorigin/Astra/actions/runs/38698232207) stopped during `glm-5.3` preflight, with zero executed cases; Server readiness and the DeepSeek status check succeeded. This lane is not passing, and its published diagnostics do not identify the underlying GLM failure. - Incremental review: 2/2 code files reviewed, zero skipped (100%); the memory design documentation was also reviewed and updated. ### Integration follow-up: latest main - Commit `a4bc6653` merges main `b091b23b` into this branch and resolves both harness conflicts without rewriting previous commits. - Preserve main's strict fixed-model admission policy, rejected/unexecuted result checks, no-child guarantee, and failure controls. Retain the configured-name versus Offering ID prompt distinction and extend the selector negative control to all three shipped model-rejection cases. Extra spawn attempts use a valid fixed-model request. - The Memoria durable finalization fixes, including confirmed working receipt preservation, remain intact. - Local validation on this commit: harness library **611/611 passed**; harness CI script suite **13 passed, 5 Linux-only skips** on macOS; runtime `cargo check -p astra-runtime --lib --tests`, workspace Rust formatting, and diff whitespace checks passed. - New GitHub CI is pending. Results recorded above apply to their explicitly identified earlier commits/runs. ### Diagnostic follow-up: child adoption failure reasons Commit `c683db37` follows a rebase onto latest main `45f56984` (including #1012). Existing Memoria changes are preserved. - The canonical child custody verifier now returns closed reason codes for failed conditions and unavailable evidence. The deterministic grader, semantic-evidence admission and safe progress projection share that verifier. Existing verdicts and failure classifications are preserved. - CI summaries distinguish child lifecycle/count, fanout group/slot/identity, repeated versus unsuccessful starts, model selection, result/digest consistency, parent finalization order, missing physical execution, and invalid or incomplete result windows. Public artifacts contain fixed codes only; no raw child results, arguments, identities, expected values, or provider error bodies. - The Python summary consumer validates the same 42-code vocabulary and rejects unknown codes, incompatible status/reason pairs, and additional payload fields. Existing progress records remain supported. - Validation on this exact commit: harness library **612/612 passed**, including **21 producer fault/control scenarios**; targeted harness Clippy with warnings denied passed; harness CI script tests **15 passed / 5 Linux-only skips** on macOS; workspace formatting and diff checks passed. - This adds diagnosis, not certification that the previous live fanout failure is fixed. A new normal PR CI run must reproduce or clear it with the more precise evidence. No performance gate or case acceptance criterion was changed. ### Rebase follow-up: main b95f234 - Rebased onto latest main `b95f2341` (#1013), resolving harness conflicts while preserving upstream explicit ID/title fanout selectors, pre-execution rejection handling, receipt-identity checks, and bounded tool-outcome diagnostics. Adapted the existing typed reason codes and fixtures to those canonical checks; the safe producer and consumer share 47 reason codes. - Confirmed the Memoria session-memory implementation is unchanged from the prior PR head. - Exact-head validation: harness library **614/614 passed**; targeted harness Clippy with warnings denied, workspace Rust formatting, and diff checks passed. Harness CI script tests: **15 passed / 5 Linux-only skips** on macOS. - New normal PR CI is pending; this update does not claim the live regression has passed. ### Review follow-up: close during a resumed admitted turn Commit `32f1149f` addresses [the latest P2 finding](#1005 (comment)). - Explicit close now reuses the existing Session-fenced execution-slot idle proof before preserving a prior generation's maintenance completion. An already-admitted follow-up invalidates the old plan even if its canonical watermark has not advanced. Late publication and canonical recovery remain discoverable through the existing journal and worker. - No new lifecycle state, schema, or migration. Idle repeated closes preserve stage receipts and completion; already-consolidated closed sessions continue rejecting new publications. - Added a real MatrixOne regression through actual Run admission, canonical writer reservation/commit, and the owned Session close service. It covers successful exit finalization, close before a resumed turn publishes/commits, rejection of early destructive work, normal late handoff and missed-handoff recovery after restart, final settlement, and repeated completed idle closes. - Local validation: integration-target compilation, targeted service library/integration-test Clippy with warnings denied, workspace formatting, and diff checks passed. The local database engine is unavailable, so database execution used the existing isolated MatrixOne CI lane. [Core-services job 116795821909](https://github.com/matrixorigin/Astra/actions/runs/38845746558/job/116795821909) passed **477/477**, with all **6/6 journal DB regressions** explicitly PASS, including `close_during_resumed_turn_recovers_after_prior_exit_finalization`. That regression exercised both normal late publication and missed-handoff canonical recovery, followed by completed repeated-close controls. Remaining PR checks are still running. ### Review follow-up: canonical settlement after terminal slot release Addresses [the remaining settlement-window review](#1005 (comment)). Terminal Run persistence releases the execution slot before canonical context commit. The shared memory idle check now also checks the coordinator's existing live writer/reservation expiry columns, using database time and current reads under the existing Session fence. Explicit close resets prior exit completion while either execution or canonical settlement remains outstanding; destructive checkpoints use the same guard. No new lifecycle state or schema is introduced. Repeated close after settlement still preserves receipts. The new `close_after_terminal_slot_release_waits_for_canonical_commit` database regression reproduces prior successful exit finalization followed by an admitted turn with a missed handoff, terminal status/slot release, close, then canonical commit. It asserts that the slot is empty and the old canonical watermark is still present before close, verifies pending work and rejection of early cleanup, then verifies restarted recovery through turn 2 and idempotent repeated close. The earlier occupied-slot cases now also commit terminal status before canonical context, matching production order. All cases additionally reject cleanup after commit while writer authority remains live. Validation at `0adda4c2`: - `cargo clippy -p astra-services --lib --test session_memory_lifecycle_db_it -- -D warnings`: passed (with the existing local debug-profile overrides). - `cargo fmt --all --check` and `git diff --check`: passed. - Local MatrixOne/Docker is unavailable. [Normal PR core-services CI](https://github.com/matrixorigin/Astra/actions/runs/38859768037/job/116839203868) passed **478/478 tests**, including `close_after_terminal_slot_release_waits_for_canonical_commit` and all **7/7 journal database cases**. This regression requires no live provider/model. Other CI checks are still running. ### Main rebase: preserve durable handoff and sole post-loop dispatch Rebased onto main `7375abe0`, retaining the merged runtime/storage latency improvements and sandbox fixes. The harness conflict resolutions preserve the existing typed child-proof diagnostics and fail-closed case without changing their content. Main now gives Server a sole detached post-loop memory-dispatch owner. The integration preserves that ownership while still publishing this PR's bounded durable input before terminal completion. Finalization always performs the bounded preparation, then defers dispatch when the host owns post-loop settlement. Skipping preparation together with dispatch would leave the post-loop worker without its durable input; dispatching in both places would undo main's ownership fix. The new ignored MatrixOne regression `post_loop_owner_preserves_durable_handoff_before_dispatch` calls actual turn finalization with the post-loop-owner host and a durable service. It requires the owner/session journal input and fingerprint to be persisted before return, while extraction remains unrequested and no memory backend write has occurred. Existing direct-dispatch and deferred-owner tests remain in place. Validation: `cargo check -p astra-runtime --lib --tests`, `cargo clippy -p astra-runtime --lib --tests -p astra-services --test session_memory_lifecycle_db_it -- -D warnings`, `cargo fmt --all --check` and `git diff --check` passed locally (using the existing local debug-profile overrides). The runtime database regressions share one test-only isolated-database guard module, preserving Clippy duplicate-module enforcement. Normal PR CI has restarted for `26271bdc`; the new database regression is not claimed as locally executed. ### CI follow-up: wait for canonical context after terminal settlement At `26271bdc`, ordinary unit-test shards and the three core online shards passed. [Integration CI](https://github.com/matrixorigin/Astra/actions/runs/38869788360/job/116871693438) ran 295 tests with 294 passing; `e2e_matrix_execution_handoff_resume_reaches_provider_http` observed canonical `completed_turn=0` while expecting 1. The failed online-core aggregate reflects that integration failure rather than additional failing test suites. Merged main #999 moved `run_settlement_finished` into the atomic terminal transaction, which precedes canonical context commit. The E2E test was still treating that event as a canonical-commit barrier. It now polls the actual owner/session context watermark under a 15-second deadline before retaining the original exact-turn assertion. A missing commit still fails by timeout and an unexpected higher turn still fails the assertion. No product acceptance condition is weakened. Validation at `40cbadd5`: `cargo clippy -p astra-runtime --features e2e-hooks --test system_matrix_http_e2e -- -D warnings`, `cargo fmt --all --check` and `git diff --check` passed (using the existing local debug-profile overrides). [The normal PR integration rerun](https://github.com/matrixorigin/Astra/actions/runs/38874422974/job/116886451691) passed **295/295 tests**, with an explicit PASS for `e2e_matrix_execution_handoff_resume_reaches_provider_http`. Baseline memory regression evidence at `26271bdc`: [core-services 481/481](https://github.com/matrixorigin/Astra/actions/runs/38869788360/job/116871693462), including the terminal-slot-release regression, and [core-runtime 104/104](https://github.com/matrixorigin/Astra/actions/runs/38869788360/job/116871693406), including the pre-terminal durable-handoff/post-loop-dispatch regression. The separate [optional live harness run](https://github.com/matrixorigin/Astra/actions/runs/38869785427/job/116871492386) completed 8 cases, 7 passing. Its failed fanout case passed root exit, successful fanout-tool count, two-child count and child-adoption proof; its hard semantic judge reported `assessment=unavailable` / `InfraVerificationUnavailable`. Model preflight passed. The retained safe artifact does not distinguish input preparation, judge invocation or response-parsing errors, so it does not establish a model-quality failure. No model selection, grading threshold or fail-closed behavior is changed by this test synchronization fix. Current-head CI follow-up: [optional live harness run 38874421050](https://github.com/matrixorigin/Astra/actions/runs/38874421050) completed **8/8 passed**, with no failed, cancelled or unavailable cases. The prior unavailable semantic assessment did not recur. No grading or model change was required for this rerun. Integration passed **295/295**, including the original failing recovery case; all test and static-check workflows have passed. Approved-by: XuPeng-SH <39627130+XuPeng-SH@users.noreply.github.com>
Problem and behavior
Astra issue matrixorigin/Astra#996 needs partial-stage retries that survive process death and lost HTTP acknowledgments. Ordinary store uses a fresh UUID plus approximate/vector deduplication; concurrent requests can create duplicates and similar content can reuse another operation's session metadata. That is insufficient evidence for a durable archive-before-purge decision.
This adds an optional, atomic single-memory idempotency protocol:
POST /v1/memories/idempotentrequires an exact operation key and retains existing authentication, scoped memory-write permissions, branch/main guards, sensitivity checks and per-user SQL routing.The dedicated route makes old backends fail before a write, so Astra must not fall back to ordinary store after a missing or malformed acknowledgment. Deploy this Memoria change before the matched Astra durable-finalization client
in matrixorigin/Astra#1005. Both PRs are drafts for review.
Review follow-up
Addresses the review comment:
serde_json/preserve_order, so conversion throughjson!already sorts HashMap keys; reordered multi-key/nested metadata is now covered by regression tests and remains stable if that feature is enabled later.Validation
Passed against newly created, isolated local MatrixOne databases and synthetic identities:
cargo test -p memoria-service --lib idempotency_tests(1 test)cargo test -p memoria-service --test idempotent_store_db_it -- --test-threads=1(1 test; 8 concurrent replays, conflicting content/session, owner separation, soft-delete retry)cargo test -p memoria-api --test idempotent_store -- --test-threads=1(1 public HTTP test; repeated receipts, missing/conflicting keys, authentication, owner separation and batch rejection)cargo clippy -p memoria-api -p memoria-service -p memoria-storage -p memoria-core --lib --test idempotent_store --test idempotent_store_db_it -- -D warningsgit diff --checkBroad all-target Clippy also exposes pre-existing warnings in
subject_id_filter.rs,governance_pool_budget_e2e.rsand scheduler lib tests; these unrelated files are unchanged. No QA/production writes or live provider calls were used. No schema migration is needed.Host review used the open-code-review delegate skill: all 9 selected Rust files reviewed; 2 documentation files read separately; 0 skipped, 100% coverage of this change. Documentation describes the API, deployment dependency and deletion limits.
Review follow-up: resolved branch receipts
Fixed the blocking branch-scope finding from the exact-head review. Keyed writes snapshot the effective checkout (or normalized explicit branch), resolve its table once, and retain the routed SQL store/table for receipt lookup, insertion and conflict replay across embedding. An omitted branch and the same explicit branch now share one receipt; invalid branches fail closed. Clients requiring a fixed retry scope must send it explicitly; Astra #1005 pins its durable reads, writes and cleanup to main.
Validation: the public authenticated HTTP tests pass (2 tests), including checked-out/explicit aliases on both keyed endpoints, branch-local row counts, checkout during a semaphore-paused deterministic embedding, cross-branch key separation and missing-branch rejection. The existing eight-way concurrent replay/conflict/deletion DB regression and receipt identity unit test pass. Clippy passes for API/service/storage/core libraries and both idempotency integration targets with warnings denied. All database tests used an isolated local MatrixOne database and mock embeddings.