Shelf: reveal-to-table #245
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Flick a ShelfCard to the players - post it to the Discord channel or a player-facing screen - with a GM-only vs player-safe layer, so a handout or read-aloud can be shown without manual redaction. (#235)
Done — merged in PR #265 (
43143d1), CI green.Scope grew from the original issue on your call: rather than picking Discord or a player screen, this ships both plus a registry so Foundry can be a third surface later, and the player screen targets logged-in members rather than being public.
The player-safe layer. New
shelf_cards.player_text— what the table may see, as against the rest of the card, which stays GM-only. When the GM hasn't authored one, the server derives a conservative default: a wiki entry's publicsummary(nevergm_notes), or a generation card's read-aloud fields only. That derivation is a strict allowlist, not a denylist — an unknown tool's output contributes nothing rather than leaking whatever it happens to call its secret — and when it comes out empty the reveal is refused rather than guessed at. Whatever does get shown is saved back onto the card, so you see exactly what the table saw and can edit it instead of revealing blind each time.Reveals are rows, not a flag on the card, for three reasons: the shown text must be frozen against later edits (a handout the table has read shouldn't retroactively change), a card can be shown more than once, and the record has to outlive the card being discarded in the sweep (#247) — hence
ON DELETE SET NULL.Surfaces are a registry.
_REVEAL_DISPATCHmaps a name to a dispatcher, so the Foundry pillar's "push into the live scene" is one entry plus one vocabulary value — no schema change, no migration, no new column. That's the extensibility you asked for. Dispatch runs before the row is persisted and is all-or-nothing, so a surface that refuses (Discord with no channel configured) never leaves a reveal claiming the table saw something it didn't.Authorization. Reading reveals is open to every campaign member — it's the players' surface. Revealing and revoking stay GM-only, and the board itself is unchanged.
Cross-component.
shelf_revealis an additive event on the durable bot stream, soBOT_CONTRACT_VERSIONstays 1; backend and bot moved in one commit. The bot postsplayer_textverbatim, never enriches it, never logs it.Migration
d9e0f1a2b3c4is the new head. New page at/sessions/:id/table, linked from SessionDetail for every member.Tested: backend 790, bot 194, frontend 421 + production build; ruff + eslint clean. Coverage includes
gm_notes/secretnever leaking, the unknown-target and failed-dispatch-reveals-nothing cases, the member-read/GM-write boundary, and the reveal surviving its card.🤖 Generated with Claude Code