feat(chat): client turn-id makes fire-and-forget bulletproof
Brian fires a quick message then locks his phone to go play the hand — which drops the SSE stream, and on wake the UI re-fires via the blocking fallback. The prior (session, message) + 20s window caught the near-simultaneous case but not a re-fire minutes later. Now the UI stamps each send with a unique turnId (crypto.randomUUID) and carries the SAME id on both the stream and the fallback; the server dedupes on it. Bulletproof regardless of how long he's away — lock for an hour, come back, still exactly one execution and one log — and a genuinely new send gets a fresh id so nothing legit is swallowed. Id-keyed turns keep a long (1h) window; requests without an id keep the short (session, msg) window for near-simultaneous dupes. Verified end-to-end: two POSTs with the same turnId → one reply, one persisted exchange pair (the duplicate reused the owner's result). 9 dedup tests; suite 235. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -398,10 +398,17 @@
|
||||
// live poker session forces the cloud backend regardless of the saved pick.
|
||||
if (mode === "poker_cash") backend = "cloud";
|
||||
|
||||
// One id per send, carried on BOTH the stream and the blocking fallback so the
|
||||
// server runs this turn exactly once even if you lock your phone and it re-fires.
|
||||
const turnId = (window.crypto && crypto.randomUUID)
|
||||
? crypto.randomUUID()
|
||||
: String(Date.now()) + "-" + Math.random().toString(36).slice(2);
|
||||
|
||||
const body = {
|
||||
mode: mode,
|
||||
messages: history,
|
||||
sessionId: currentSession
|
||||
sessionId: currentSession,
|
||||
turnId: turnId
|
||||
};
|
||||
|
||||
// Only add backend if in standard mode
|
||||
|
||||
Reference in New Issue
Block a user