Let owners/operators mute or unmute new public/private sends from a human seat while retaining history, incoming messages, gameplay and credentials. Fence metadata and durable audit writes with live authority, membership and versions. Preserve exact admin/message retries and inherit restrictions through seat recovery. Validation: types/lint, 5431 offline tests, build, 52 HTTP integration tests, 24 administrator HTTP groups, independent SQL/privacy/UI review and desktop/mobile refresh acceptance. All 2230 prior games and 4746 seats preserved. Additive migration 0016. Verified runtime fingerprint 588967fbb9c2e3b6abe2990c4fce96ee69e2256fb7e3ee416314b87593cebba9; documentation-only grammar/button-label review gives b5e57bdd26812debede91c588e38f247f1fa895ad23e7ae30bc8550eedcd90ec. New-revision deployed acceptance remains open; full admin/rules scope is unfinished.
4.9 KiB
Table discussion and private seat messages
The table now has an expandable Table discussion panel beneath the game. Humans can speak publicly or choose a named human seat for a private conversation, in the lobby, during any pending decision and after the game. A human using AI control can still talk. Permanent AI opponents do not read or answer messages; AI negotiation remains deferred until non-AI feature completion.
Message delivery and audience
Each message is plain text, limited to 1,000 characters. HTML and URLs remain literal text, with no executable markup or external links. The server supplies the sender's identity. A public message is visible to all present and future seats in that room; a direct message is visible only to its sender and recipient. Private messages belong to those game seats: recovery or voluntary handover gives the new controller access to their history, and revokes the old credential's access. The panel explains that boundary before sending.
Discussion does not spend spice, change alliances, seal a plan, evaluate prose promises or execute game actions. Use the existing authoritative game controls to carry out an agreement. This is communication infrastructure, not a new ruling about binding deals or a completed negotiation/AI strategy system.
The latest 50 messages in the selected conversation load on opening and refresh serially every five seconds, with bounded requests. Load older messages pauses live replacement while browsing saved history; Return to latest messages resumes updates. Channel changes discard obsolete read results. There are no public direct-message counts, typing indicators or global sequence numbers. Messages are stored durably; pagination does not delete old history.
Discussion moderation
An administrator can mute new messages from a seat. The player sees a persistent explanation; reading history, receiving messages and playing remain available. The setting follows recovery or handover and is never shown as another player's private activity. New sends are checked at database commit. An exact previously committed send can still be confirmed while muted; no new message is created. Refresh availability after an unmute, or allow the normal latest-message poll to update the controls.
Saved retries and state isolation
The browser saves an exact message UUID, recipient and body in tab storage before sending. An uncertain response keeps the input locked for Retry exact message; refresh restores that same request. Exact repeats save once, and conflicting UUID reuse never returns another message's body. An already uncertain retry is not discarded just because a later request fails. Explicitly discarding retry details cannot undo an already committed send. Unsent drafts are local inputs; only pending sends have refresh recovery. No credentials enter this storage.
A late response from an unmounted panel cannot erase a newer pending send. Successful writes return their own receipt even if the seat subsequently rotates. Replaying under a different seat session cannot reactivate the earlier operation; the current owner can inspect the conversation and discard obsolete retry details.
Additive migration 0007_clammy_sprite.sql creates room_messages and its indexes.
Every SQL read and insert checks the exact active seat token, room membership and
human status; inserts check recipient membership at commit. The internal sequence
is used only for ordering. Cursor lookups apply the same audience filter as the
message page, so an unknown or hidden UUID does not expose message metadata.
One new message per sender per second bounds accidental rapid submission; exact
retries do not consume another slot. There is no game-version conflict to refresh:
message operations never write game JSON, reset readiness, advance bots or enter
a gameplay continuation. Existing saved games and schema history are preserved.
Evidence and remaining work
table-talk-recovery.test.ts exercises production SQL against the real migrations:
private/public projection, hidden cursors, pagination, exact concurrent retries,
changed payloads, active-token and membership fences, rotation during a write,
store recreation, human AI control and unchanged game rows. The HTTP tests use
real authenticated rooms and sealed setup choices, verify origin/error handling,
exact retries and actual saved-kit recovery. Client tests cover input/receipt
validation, identity-scoped storage and late-completion cleanup.
Independent review found two retry races during implementation; both have been fixed and regression tested. Browser observations, the final check/build/HTTP report and saved-row comparison are source-bound private checkpoint artifacts. This prototype does not certify complete network-failure browser behavior, notification/unread design, full accessibility/visual acceptance, complete negotiation rules or any AI strength target. Existing release gates remain closed.