fix(feedback): harden backend consistency
This commit is contained in:
@@ -18,9 +18,9 @@ Every usable row is bound to the inspected Session header identity `{createdAt,
|
||||
|
||||
`put` accepts a target only when `SessionPersistence.inspect()` observes a non-empty, append-origin `assistant/message` with that `MessageId`. Replacement-origin messages, empty usage-only assistant records, and non-assistant targets are rejected. Inspection is the cold-safe authority: it neither publishes or resumes an Agent nor commits cold-log repair merely to validate feedback. A cold `listSnapshots()` preflight classifies definite absence; inspection failure for a catalogued Session remains an infrastructure failure. A request in the narrow live-detach-to-header-materialization interval can therefore return `session-not-found`, and the caller retries after retirement materialization.
|
||||
|
||||
Before `put` commits a sidecar row, it puts the target log behind a durability barrier. A matching live Session passes through the canonical `ctx.sessions.flush` checkpoint; a catalogued cold Session is physically re-read from sequence zero through `SessionPersistence.readFrom`. The resulting observation's header identity and target are checked again. A missing flush participant, changed identity, vanished target, or cold physical-read failure prevents the sidecar write, so a committed feedback item never precedes the durable assistant message it references.
|
||||
Before `put` commits a sidecar row, it puts the target log behind a durability barrier. A matching live Session passes through the canonical `ctx.sessions.flush` checkpoint, then both live and cold paths are physically read from sequence zero through `SessionPersistence.readFrom`. The resulting observation's header identity and target are checked again. A missing flush participant, changed identity, vanished target, or physical-read failure prevents the sidecar write, so a committed feedback item never precedes the durable assistant message it references.
|
||||
|
||||
Each message item carries its own opaque version plus Host-assigned `createdAt` and `updatedAt` timestamps. `put` compares the caller's `ifVersion` only with the addressed item, so editing one message does not invalidate another. An exact retry of an already committed desired value returns that stored item before a stale `ifVersion` becomes a conflict, preserving its version and timestamps; a material update preserves `createdAt`, replaces the version, and keeps `updatedAt` from moving backward. An already-absent delete is likewise successful. Versions are tokens for equality, not counters callers may order or synthesize.
|
||||
Each message item carries its own opaque version plus Host-assigned `createdAt` and `updatedAt` timestamps. `put` compares the caller's `ifVersion` only with the addressed item, so editing one message does not invalidate another. The comparison is strict even when the desired value already matches, preventing a stale request from crossing an ABA value cycle; a conflict returns the authoritative current item so callers can reconcile without a second read. A matching-version no-op preserves the version and timestamps, while a material update preserves `createdAt`, replaces the version, and keeps `updatedAt` from moving backward. An already-absent delete is likewise successful. Versions are tokens for equality, not counters callers may order or synthesize.
|
||||
|
||||
A per-Session mutation queue encloses lifecycle inspection, sidecar read, conflict evaluation, and whole-row write. This makes one service instance's mutations serial and preserves the per-message compare-and-swap contract inside one Host process. The underlying storage-domain API provides no cross-process conditional write, so the implementation claims no cross-process linearizability or lost-update protection.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user