feat(session): start created sessions only from published releases - #188
feat(session): start created sessions only from published releases#188cursor[bot] wants to merge 40 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>
A worker that only remembers Activate must not rewind a later Pause/Resume projection. Count stored commands after exact replay and fail closed when the in-memory history is shorter, so load still reconstitutes the paused session after the rejected persist. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Point traceability, as-built schema, and ERD at the successor that rejects a shorter command history instead of rewinding Pause/Resume. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
A stale Activate-only persist could count command rows, then overwrite session_state after Activate+Pause committed, leaving the session unloadable. Lock the assessment_session header row for the caller transaction and prove two workers cannot rewind a paused projection. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Retarget the session persist/load landing vehicle from sequential stale-prefix #129 to the header-row lock successor. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Commit created identity first so a second writer waits on FOR UPDATE instead of missing the uncommitted row. Record physical session and command uniqueness in the ERD constraint table. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Add created_session_for_start and start_created_assessment_session so a buyer cannot persist a reconstituted Created identity after the release is suspended. Exact start replay still returns the original session. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Name the start-boundary successor of the #154 header lock in traceability, ADR-0005, as-built schema, ERD, UML, quality attributes, and the changelog. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
There was a problem hiding this comment.
Stale comment
Queue
This head is the in-memory publication-gated start on the #154 header-lock lineage. Prefer #180 for session start: it locks stored
instrument_release.publication_statewithSELECT … FOR UPDATEin the same transaction as insert, so a stale in-memory Published object cannot start after the stored release is suspended.Keep this PR Draft. Do not merge in parallel with #180, #164, #153, or #154. HTTP
POST /v1/sessionsremains later and must call the stored-publication start boundary, notpersist_assessment_session(&from_persisted_created(...)).Independent last-push review is still required on whichever start landing is chosen. This is not merge approval.
Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Queue
This head is in-memory publication-gated start on the #154 header-lock lineage. start_created_assessment_session calls created_session_for_start → AssessmentSession::new with the caller-supplied release. It does not lock stored instrument_release.publication_state.
Prefer #180 for session start: it locks the stored publication row with SELECT … FOR UPDATE before INSERT, so a stale in-memory Published object cannot start after persist Suspend or Retire.
Keep this PR Draft. Do not merge in parallel with #180, #164, #153, or #154. The unique buyer-facing remainder on this head — exact start replay after a later suspend — belongs on the #180 lineage, not as a second in-memory start landing.
HTTP POST /v1/sessions remains later and must call the stored-publication start boundary. Independent last-push review is still required on whichever start landing is chosen. This is not merge approval.
Sent by Cursor Automation: Fix Issues
| /// accept a new session and no exact stored start exists, or | ||
| /// [`AssessmentSessionStartError::Persistence`] for conflicting stored identity | ||
| /// or a database failure. | ||
| pub fn start_created_assessment_session( |
There was a problem hiding this comment.
This start path still trusts the caller-supplied InstrumentRelease. created_session_for_start only wraps AssessmentSession::new; there is no SELECT … FOR UPDATE on stored instrument_release.publication_state.
A stale in-memory Published object can still insert after persist Suspend/Retire. Prefer #180 start_created_assessment_session / start_created_assessment_session_from_stored_release, which lock the stored row in the same transaction.
The unique value here is exact-start replay after a later suspend. Keep that on the #180 lineage; do not land this in-memory-only start.


Superseded by stored-publication exact-retry HTTP line
Close this in-memory start predecessor without merge. Its own current review selects the stored-publication lock line because this head can start from a stale in-memory Published release after the stored release is suspended or retired. That correction, exact retry, and the public start/reload transport are now carried by #232.
Fresh evidence immediately before closure:
1befc4277f683cc4bb6f166e3690e2775a8fca3bcca5192bc5f10e3ae6af89ef0ea8be92c81b2977(closed predecessor)6e9e2440e14b0ec057d50dd26d3add765f1e5e48(closed predecessor)c7b45145c9b693951fae498e0779d58cbe170f86main:a7637351be8f0f90c12651d3bcafd959bc52ac81#188 review explicitly says the unique buyer-facing remainder—exact retry after a later suspend—belongs on the #180 lineage rather than as a second in-memory landing. #180 added
SELECT … FOR UPDATEof stored publication state; #198 added exact replay after persisted Suspend/Retire; Git ancestry proves #232 is directly ahead of #198 by 8 commits and therefore carries those corrections plus persist-backedPOST /v1/sessionsandGET /v1/sessions/{session_ref}.Do not treat #188's review as approval or resolve its queue findings as if they were fixed on this exact head. #232 remains Draft and must independently pass unchanged exact-head CI/security/coverage/review gates.