feat(response): persist in-progress events across restart - #174
feat(response): persist in-progress events across restart#174cursor[bot] wants to merge 4 commits into
Conversation
A two-item path can now reload the same answers after restart without re-checking live session activity. Gapped sequence, reused client or server identity, and blank session references fail closed. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Store accepted response_event rows with observed and received time so a two-item path reloads the same answers after process restart. Exact replay is idempotent; client, server, sequence, or session rebinding fails closed under READ COMMITTED. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Match the ERD and #174 temporal contract so source-valid time cannot replace platform receipt time, and fail closed on inverted or rebound timestamps. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
There was a problem hiding this comment.
Stale comment
Same mid-session persist slice as #182. Keep the observed/received time contract, but do not land this head beside #182. Prefer #182, which adds restart scoring-prefix proof, recovery copy of
response_event, and the same temporal fail-closed checks.Do not merge #174 or #53 in parallel.
Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Stale comment
Verdict: COMMENT
HEAD
71c751497f47cfc50eb9384268b5b2fa3104a601persist/load is sound. I did not find a fail-open, 3NF, naming, injection, or idempotency defect that should block this slice.Do not APPROVE and do not merge: the PR is draft, Runtime CI was still queued at review time, and independent last-push approval is still required. Do not self-approve.
What holds
- Product persistence only. No bodies, no scores, no
fast-mlsirmkernels.response_eventnames are two-or-more-wordsnake_case. Public refs stay opaqueTEXTwith the shared numeric-like CHECK.- Unique
(session_ref, client_event_ref)and(session_ref, server_sequence)plusobserved_at <= received_atmatch ADR-0015.persist_response_ledger/load_response_ledgeruse parameterized SQL and exactSHOW transaction_isolation = read committed.- The insert-then-classify CTE is the safer READ COMMITTED shape. Exact PK replay is
Duplicateand does not overwrite times. Client/item/digest mismatch isConflictingReplay. Sequence reuse isSequenceConflict. Session rebinding of the same server ref isConflictingReplay. Gapped stored history fails closed.- Domain tests cover the two-item Korean path, gapped/rewound sequence, and reused identities. PostgreSQL tests cover exact replay, rebinding, inverted/zero/arity time, Repeatable Read rejection, and a deleted-prefix gap.
Do not prefer #182 blindly
#182 (
86e5669) adds recovery COPY, AS_BUILT, and a scoring-prefix persist proof. It does not make this head obsolete:
- This head persists a whole ledger in one transaction. #182 persists one event and leaves the loop to the caller.
- This head returns
Nonefor a missing session. #182 returns an empty ledger, which hides “never stored” versus “empty persist”.- Both migrations already store
observed_at/received_at. #182’s AS_BUILT text omits those columns.Keep #174, #182, and #53 draft. Do not land them in parallel. The landing vehicle needs this head’s ledger API and #182’s recovery/AS_BUILT/scoring-prefix proof.
Residual gaps on this head
tests/postgres_recovery_invariants.rsstill COPY-restores only outbox/inbox/consumption/snapshot. In-progress answers are not restore-proven.docs/architecture/AS_BUILT_SCHEMA.mddoes not namemigrations/0020_response_event.sql.- ADR-0015 cites Berenson et al. (1995) and ISO 8601-1, but
docs/doctoring/standards-and-evidence.mdstill lacks the isolation paper.- No persist test does load → record item 3 → persist → reload, so continue-after-restart is implied rather than proven.
- UML
ResponseEventstill omits observed/received time.Next buyer action
Close those evidence gaps on one landing vehicle, then keep HTTP
POST /v1/sessions/{session_ref}/responsesoff this branch. That later slice must load the stored ledger beforerecord(), then persist with distinct observed vs received time, and reject new answers unless the session is Active.Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Stale comment
Review
Do not land this head. The
response_eventpersist landing is #182 (4dd8e6d). That successor also reloads distinct observed/received clocks after restart.Keep this PR Draft. Do not add HTTP or snapshot-reload here. Prefer #182 over #53 and snapshot-only #151.
Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Stale comment
Prefer #201 as the
response_eventpersist landing vehicle.This head (
71c7514) remains sound, but #201 adds stored-time reload,InvalidEventTimeArity, recovery COPY, AS_BUILT, doctoring isolation citations, and the load → record item 3 → persist → freeze scoring-prefix proof.Keep #174, #182, and #53 draft. Do not land them in parallel with #201. Independent last-push approval is still required. Do not self-approve.
Next buyer slice after #201 lands: HTTP
POST /v1/sessions/{session_ref}/responseson #195, not on this branch.Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Review
Do not merge this persist slice.
Prefer PR #208 for in-progress response_event persist/load, first-write observed/received times, recovery COPY proof, and continue-after-restart scoring prefix.
This head is an earlier overlapping persist attempt. Landing #174, #182, #201, and #53 in parallel would fight over the same table and reconstruction contract. HTTP responses stay on #195. Independent last-push review is still required on #208. Do not self-approve.
Sent by Cursor Automation: Fix Issues
|
Closing as superseded by PR #201. #201 explicitly carries this durable response-prefix work and adds the missing recovery COPY, AS_BUILT/doctoring evidence, persisted observed/received-time reload, arity failure contract, and continue-after-restart scoring-prefix proof. Its current scope says to prefer #201 over #174/#182/#53 and not land these overlapping persistence slices in parallel. Continue on #201 or its later successor. |


Why
Protected main can freeze a completed response snapshot, but a buyer still loses in-progress answers on process restart. A two-item Korean Big Five path that has not completed cannot be scored or continued from durable evidence.
This is independent of Active PR #161/#149 (session HTTP), #154 (session command lock), #151 (completed snapshot reload), and #162 (restricted linkage). Do not fold those slices into this head.
What
ResponseLedger::from_persistedrebuilds server sequence1..nafter restart without re-checking live session activity.migrations/0020_response_event.sqlstores opaque event identity, session binding, client idempotency, item version, payload digest, server sequence, and distinct observed/received timestamps.persist_response_ledger/load_response_ledgeruseREAD COMMITTEDinsert-then-classify. Exact replay is idempotent; client, server, sequence, or session rebinding fails closed.Out of scope
POST /v1/sessions/{session_ref}/responses#55/#151)#161/#149/#154)Test plan
cargo test --test response_ledgercargo test --lib postgres_response_eventcargo test --test documentation_architecture_contract --test traceability_active_pr_contractcargo clippy --all-targets -- -D warningscargo test --test postgres_response_event_persistence --test postgres_response_event_error_contract(needsTEST_DATABASE_URL)Do not merge until exact-head checks and independent last-push approval are satisfied. Do not self-approve.