feat(session): persist command history so Activate survives restart - #125
feat(session): persist command history so Activate survives restart#125cursor[bot] wants to merge 34 commits into
Conversation
Store participant and published-release identity for SessionState::Created with exact replay and fail-closed rebinding. Command-replay persistence stays outside this first slice.
Assert the Database error message and source, and fail the replay SELECT after ON CONFLICT by redirecting search_path so classify runs instead of the insert.
Linux llvm-cov leaves the isolated query_one ? tail uncovered unless the Err arm is an explicit match. Keep the search_path redirect test.
Linux branch coverage missed the later AND operands of exact-replay classification. Rebind each stored field independently, and prove a domain-legal u64::MAX creation time fails closed as ValueOutOfRange.
The replay SELECT failure constructed Database evidence without checking its safe display text or source, leaving those two production lines uncovered on Linux.
SHOW transaction_isolation can fail after the caller transaction is already aborted. Persist must surface that as a typed database error instead of leaving the probe Result uncovered.
Satisfy clippy::manual_let_else in the library test that instantiates AssessmentSessionPersistenceError::Database.
Name instrument_version_ref in the public persist contract and assert the committed version column. Keep TRACEABILITY, changelog, and as-built schema at Active PR #61 rather than promoting the slice to protected-main truth. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Restore persisted created-session identity from PostgreSQL without asking whether the original release still accepts new sessions, so later suspend or retire cannot rewrite provenance. Missing rows return none; later stored states and malformed lookup references fail closed. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Name the opened successor so TRACEABILITY, as-built schema, and ERD point at the persist-and-load head instead of the persist-only #106 slice. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Add the headline reconstitution case: create while published, then suspend or retire so AssessmentSession::new fails, then restore the original Created identity and Activate. Cover numeric-like participant, release, and version references. Point AS_BUILT_SCHEMA at Active PR #109 instead of predecessor #61. Fail closed on every later stored state and on load against a missing table. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Store accepted session commands in assessment_session_command and project the current lifecycle state. Load reconstitutes created identity without re-checking publication eligibility, then replays commands so Pause/Resume still work after process restart. Exact command replay is idempotent; sequence reuse and evidence rebinding fail closed. Later stored states without command history still fail closed. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Point TRACEABILITY, as-built schema, and ERD at the successor that stores assessment_session_command and replays Activate after restart. Keep #109 named as the persist-and-load predecessor. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
There was a problem hiding this comment.
Stale comment
Do not merge this head.
persist_assessment_session_commandswritessession_statefrom the caller after exact command replay, but it never checks whether stored history is already longer. A stale Activate-only persist therefore rewinds a later Pause/Resume projection while leaving the extra command rows in place. The next load replays Pause and then fails closed (InvalidStoredIdentity), so the buyer-visible restart path this PR claims to add is one stale worker away from an unloadable session.Prefer successor #129 (
cursor/bc-440a0236-de0f-42c1-9733-96845efafa17-4e74). That head counts storedassessment_session_commandrows after replay, fails closed on a shorter in-memory history, and proves the paused session still loads after the rejected persist.Keep #121, #109, persist-only #106, and #61 parked behind #129. HTTP session-creation remains the next buyer gap after this slice lands; it still depends on #87 RFC 9457.
Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Stale comment
Do not merge #125. After Activate+Pause is durable, a later persist of a shorter in-memory history (Activate-only, or empty commands) still classifies the prefix as an exact duplicate, then writes
session_statefrom the stale caller over the later projection. Load reconstitutes Created, replays the longer stored command list, and rejects the row asInvalidStoredIdentity. A buyer who paused, then hit a retry or a second worker that only remembered Activate, cannot reload the session.
persist_assessment_session_commandsnever compares stored command count tosession.accepted_commands().len()before the projectionUPDATE. Exact same-history persist is idempotent; a strictly shorter history is not fail-closed on this head.Prefer successor #129 (
fix(session): reject stale shorter command history on persist). Do not land #125, #121, #109, #106, or #61 in parallel. Do not land #100. HTTP session-creation remains the next buyer-visible gap and still depends on the RFC 9457 primitive in #87.Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Do not merge #125. Later-state persist on this head can rewind a Pause/Resume projection when a stale worker persists a shorter Activate-only history. Prefer successor #129 for command-history persist.
Created persist/load/start landing is #138, not this head and not #121. Rebase #129 onto #138 after #138 lands. Do not open a third later-state PR. Do not land #100.
Sent by Cursor Automation: Fix Issues
Processing consumption rows now require claim_deadline_at after #81. Seed the wall-clock deadline and prove binary restore preserves it.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Do not merge this head: later-state persist can rewind a Pause/Resume projection. Prefer #138 for created persist/load/start and #129 for the stale-prefix command-history fence. Never self-approve. |


Superseded by repaired durable-session line
Close this predecessor without merge. Its command-history persistence has been carried forward through the stale-history repair in #129 and into the current durable-session HTTP line #232; its one branch-only late recovery delta is isolated on #177.
Fresh evidence immediately before closure:
e66981d666241b5cbe8b2649393240ce1ea93e65a196a2d5196bef8110857f44538785b0ceda28fe(closed predecessor)c7b45145c9b693951fae498e0779d58cbe170f868a0db4611a684285185f9edadf086a1808444ab9main:a7637351be8f0f90c12651d3bcafd959bc52ac81Current review on #125 identifies a valid buyer-visible defect: a stale shorter in-memory command history can rewind the stored projection and make the session unloadable. #129 is the explicit successor that rejects that stale-shorter history. Git ancestry proves #232 is directly ahead of #129 by 16 commits with #129 as the merge base, so the repaired append-only command-history invariant is carried forward into #232 together with later persist/load/start-lock and public start/reload HTTP work.
A separate compare shows the only #125-side delta absent from #232 is
tests/postgres_recovery_invariants.rs(27 additions / 3 deletions), theclaim_deadline_atCOPY/restore repair intentionally isolated on #177.Do not resolve #125's stale-review findings as if fixed on this exact head; they remain the reason this predecessor is closed. #232 and #177 remain Active PR work and must independently satisfy their unchanged exact-head CI/security/review gates.