Skip to content

feat(session): start sessions from locked stored publication state - #180

Closed
cursor[bot] wants to merge 41 commits into
mainfrom
cursor/bc-e544bc1d-8a98-4278-966e-afde3de1e1bf-624d
Closed

feat(session): start sessions from locked stored publication state#180
cursor[bot] wants to merge 41 commits into
mainfrom
cursor/bc-e544bc1d-8a98-4278-966e-afde3de1e1bf-624d

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Superseded by exact-retry and HTTP successor line

Close this predecessor without merge. Its stored-publication FOR UPDATE start lock was carried into #198 and then into the current durable-session HTTP line #232.

Fresh evidence immediately before closure:

Current #180 review says the lock itself is sound but explicitly selects #198 because #180 cannot replay an already-stored exact start after a later persisted Suspend/Retire. Git ancestry now proves #232 is directly ahead of #198 by 8 commits (and directly ahead of #180 by 10), so the stored-publication lock and the corrected exact-retry contract are carried forward together with the public start/reload HTTP adapter.

The same review also notes first-insert sealing as a later concern. That was subsequently handled on the persist lineage before #232; this closure does not claim the predecessor itself fixed it.

Do not treat stale #180 reviews as approval. #232 remains Draft and must independently satisfy unchanged exact-head Runtime CI/security/coverage/review gates before merge.

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 6 commits August 16, 2026 15:54
Rejecting a shorter command history is not enough under READ COMMITTED.
Lock the created-session row with SELECT … FOR UPDATE before inserting or
counting commands so a concurrent Activate-only persist cannot count a
prefix and then rewind a later Pause/Resume projection.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Point TRACEABILITY, as-built schema, and ERD at the successor that
locks assessment_session before command insert or count.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
New sessions must call AssessmentSession::new through
created_session_for_start / start_created_assessment_session so a
draft, suspended, or retired release cannot insert a row. Keep the
#146 header-row lock and command-history persist. Reconstitution
remains load, not start.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Name the start-boundary landing vehicle so TRACEABILITY, ERD, UML,
and as-built schema point at this head instead of the #146 lock
predecessor.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
In-memory AssessmentSession::new is not enough: a stale Published
object could insert after another transaction persisted Suspend or
Retire. Lock instrument_release with SELECT FOR UPDATE in the same
start transaction, add start_created_assessment_session_from_stored_release,
and fail closed on missing, unpublished, locale-mismatched, or
digest-mismatched stored evidence.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Point TRACEABILITY, as-built schema, ERD, and UML at the successor
that locks instrument_release before a new session insert.

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

Queue

This remains the preferred session-start landing while it locks stored instrument_release with SELECT … FOR UPDATE before insert. Prefer this head over #188 (in-memory start on the #154 lock lineage), #164, #153, and #154 for the start boundary. Do not merge those in parallel.

Residual (successor, not a lock defect)

start_created_assessment_session calls created_session_for_start first. After a later suspend, an exact HTTP retry of an already stored start fails closed as unpublished instead of returning the original session. persist_assessment_session exact replay still works if the caller already has the Created aggregate, but the start boundary itself does not load-and-return that row. HTTP POST /v1/sessions idempotency after publication withdrawal needs that replay, or a documented fail-closed retry contract.

This is not merge approval. Keep Draft until exact-head checks and independent last-push review land. Do not self-approve.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

cursor Bot pushed a commit that referenced this pull request Aug 16, 2026
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

Review (cca5192)

No blocking start-path defect on the stored-publication lock. load_published_instrument_release uses SELECT … FOR UPDATE, and durable start fails closed on missing/unpublished/locale-mismatched evidence before insert. Command-history header lock is a separate slice on this lineage.

Residual: persist classification of an existing instrument_release row remains unlocked. A Duplicate published result can still lose the row to a concurrent Suspend/Retire. That persist-side lock is #196; do not fold HTTP or a second start-lock rewrite into this head.

This review is not independent last-push approval. Do not merge while draft/checks are incomplete. Prefer this head over #164/#153/#138 for locked stored-release start. Prefer #196 for persist-classification lock. HTTP POST /v1/sessions remains #149.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread src/postgres_instrument_release.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.

Review (exact head cca5192)

Stored-publication start locks and inserts on the same caller transaction. start_created_assessment_session and start_created_assessment_session_from_stored_release both take SELECT … FOR UPDATE on instrument_release before INSERT. A stale in-memory Published object fails closed when the stored row is missing, unpublished, or digest-mismatched. Locale has no silent fallback. Exact start replay while still published returns Duplicate.

This head is the lock slice. It is not the merge vehicle now that #198 exists.

Prefer

Prefer #198 (6e9e244) for start-from-store plus exact start replay after later persist Suspend or Retire. Prefer this head over #153, #164, #188, #154, #146, and #138 only if #198 is abandoned.

Residuals (do not block the lock itself)

  1. persist_assessment_session still first-inserts a reconstituted Created aggregate with no stored publication lock. Exact replay of an already-stored Created row after suspend must stay legal; first insert after suspend must not. That seal is still open.
  2. Exact start retry after persist Suspend/Retire fails closed as unpublished instead of returning the original session. #198 claims that retry contract.
  3. Stored-path tests prove Suspend and digest mismatch. Retire, missing stored row, and version mismatch share the same fail-closed branch and are not asserted on the PostgreSQL path.
  4. No tenant_ref on assessment_session. Isolation is the global session_ref primary key. HTTP must not treat this slice as tenant-isolated.
  5. HTTP POST /v1/sessions remains #149 / #161 and must call start_created_assessment_session_from_stored_release (or load+start on the same transaction), not persist_assessment_session.

Checks / merge

Keep Draft. Do not self-approve. Do not merge until exact-head checks and independent last-push approval land. PostgreSQL jobs need TEST_DATABASE_URL. CodeRabbit CLI agent auth timed out in this environment; this review is from the source at cca5192.

Next slice after #198: seal persist_assessment_session first-insert against unpublished reconstitution.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

/// Returns [`AssessmentSessionPersistenceError`] for unsupported isolation,
/// a non-created session, conflicting replay, an out-of-range timestamp,
/// or a database failure.
pub fn persist_assessment_session(

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.

persist_assessment_session is still a public first-insert API. A reconstituted from_persisted_created aggregate can INSERT after persist Suspend/Retire because this function never locks instrument_release.publication_state.

Keep exact replay of an already-stored Created row after later suspend. Seal only the first-insert path: if no assessment_session row exists, require the same locked published snapshot that start uses, then INSERT. That residual is out of scope here and is the next slice after #198.

.unwrap();

client
.execute(

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.

This proves stored suspended plus a stale in-memory Published object cannot insert. Add the same assertion for retired, a missing instrument_release row, and a stored instrument_version_ref mismatch. Those arms already return InstrumentReleaseUnavailable / InvalidStoredRelease; they are not exercised on this PostgreSQL path.

cursor Bot pushed a commit that referenced this pull request Aug 16, 2026
Catalog next action copies release_ref and exact locale into #205
load_published_instrument_release plus persist first-insert seal.
Prefer #205 over #180 for start-from-store.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 16, 2026
Keep #196 as the persist-classification lock. Prefer #227 for combined
Created-only publication locks and #180 for command-history start lock.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@seonghobae seonghobae closed this Aug 16, 2026
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