Skip to content

feat(session): persist command history so Activate survives restart - #125

Closed
cursor[bot] wants to merge 34 commits into
mainfrom
cursor/bc-99db2ac4-4cd7-487d-9523-6c7194be2d7c-756c
Closed

feat(session): persist command history so Activate survives restart#125
cursor[bot] wants to merge 34 commits into
mainfrom
cursor/bc-99db2ac4-4cd7-487d-9523-6c7194be2d7c-756c

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

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:

Current 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), the claim_deadline_at COPY/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.

seonghobae and others added 30 commits August 14, 2026 10:46
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.
Treat landed PostgreSQL readiness as Implemented and keep #61 as the
Active created-session persist slice.
Treat landed migration rollback coverage as Implemented and keep #61
as the Active persist slice.
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>
cursoragent and others added 2 commits August 16, 2026 15:33
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>
@cursor
cursor Bot marked this pull request as ready for review August 16, 2026 15:39
@cursor
cursor Bot requested a review from seonghobae August 16, 2026 15:39
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>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Do not merge this head. persist_assessment_session_commands writes session_state from 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 stored assessment_session_command rows 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.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread src/postgres_assessment_session.rs

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_state from the stale caller over the later projection. Load reconstitutes Created, replays the longer stored command list, and rejects the row as InvalidStoredIdentity. A buyer who paused, then hit a retry or a second worker that only remembered Activate, cannot reload the session.

persist_assessment_session_commands never compares stored command count to session.accepted_commands().len() before the projection UPDATE. 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.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread src/postgres_assessment_session.rs

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

View PR

Open in Web View Automation 

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.
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

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.

@seonghobae

Copy link
Copy Markdown
Contributor

e66981d6 already seeds claim_deadline_at and proves binary restore keeps the wall-clock deadline. That is the rust integration_consumption_claim_deadline_shape recovery.

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.

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.

2 participants