[Frontend] Show players the same "Previously on" recap the GM already gets #388
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?
Impact: MEDIUM
Found in the August 2026 session lifecycle review (#319).
What the user experiences
The "Previously on" recap and open-loops panel is genuinely one of the best pieces of UX in the product — it renders exactly the recap a returning player wants — but it only renders on the GM's Session Shelf. Players get no equivalent anywhere:
SessionTable, the player mirror of the shelf, only shows cards the GM has explicitly revealed during play, not a session recap.Evidence
webapp/frontend/src/components/ColdOpenPanel.jsx:76-147— the recap/open-loops component, already computing exactly what's needed.webapp/frontend/src/pages/SessionShelf.jsx:1212— the only place it renders, andSessionShelfis GM-only.webapp/frontend/src/pages/SessionTable.jsx:93-98— the player-facing mirror shows only explicitly-revealed cards, with no recap equivalent.Why it matters for a hosted product
The data and the component already exist; withholding them from players is the one part of this finding that costs nothing to fix and directly serves the "find what happened" journey players are otherwise left to reconstruct from Discord scrollback.
Proposed fix
Render
ColdOpenPanelonSessionTablefor players, and at the top of a confirmedSessionDetailfor anyone who hasn't yet seen it. This is the audit's M3.Acceptance criteria
SessionTablerenders the same cold-open recap panel the GM's Session Shelf already shows.SessionDetailshows the recap for players, not just GMs.Picking this up as v4.3.0 phase 7 (#514), first on the last frontend lane (#388 → #389 → #382). The same
ColdOpenPanelis mounted on the player table and near the top of a confirmed session page for every member, collapsible and remembered per session; the table fetches its session once so it also learns its campaign, which gives the nav bar its campaign links there (a gap left by #378). If the recap endpoint turns out to be GM-gated on the backend it will be widened to members, since it holds nothing players should not see.Done in the phase 7b PR (auto-merging on green); ships with v4.3.0.
GET /api/sessions/{id}/shelf/cold-openmoves from the GM gate to the member gate, andSessionTableandSessionDetailmount the existingColdOpenPanel; no second implementation.One thing worth recording, because the issue (and the plan) assumed otherwise: the recap payload is not entirely player-safe as it stands.
open_loopsincludes plot threads withplayer_visible: false, and that flag is an invariant enforced in three other places (reminder_tasks.pysays GM-only threads "must never be included here";routers/bot.pyandrouters/campaigns.pyboth filter on it). So the widening is a narrowing on the way out, done in the same service call rather than a second endpoint:previously_onis unchanged (the session summary is already member-readable via the session detail endpoint, so nothing new is exposed);open_loopsis filtered toplayer_visible=Truefor non-GMs;carried_countis reported as 0, since it counts cards on the GM's private shelf. Non-members still get 403.On the front end the panel gained a collapsible mode for the two player surfaces: a fold/unfold toggle on its own local-storage key rather than the shelf's permanent ×, because a player who puts the recap aside before the game wants it back during it, and because the GM's dismissal on the shelf must not silently swallow it on the session page.
SessionTablealso fetches its session for the campaign id and declares it, so the nav bar gains that campaign's links there (the #378 gap).Tests: 4 new backend, the old GM-only test replaced; frontend covers both surfaces and the collapsible mode. Full suites green: backend 2504 passed / 13 skipped, frontend 787 passed.