[Table Tools] 'Previously on…' pre-session recap post #110
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?
Context / Motivation
Approved session summaries exist (stored in
Session.summary,webapp/backend/app/models/session.py:115; approved viaapprove_audio,webapp/backend/app/routers/sessions.py:436-504) and reminder delivery exists (poll_session_reminders,webapp/backend/app/tasks/reminder_tasks.py:998). But the moment players actually want a recap is right before the next session — currently they have to go find it.Spec
When the final pre-session reminder fires (the smallest offset in
campaign.reminder_offsets_minutes, read atreminder_tasks.py:1054) — or a configurable dedicated T-24h slot — include a recap block in the reminder:Backend (
poll_session_reminderspayload construction,reminder_tasks.py:1090-1104): when building thesession_reminderevent dict (event_type,session_id,guild_id,channel_id,extra{confirmed_time, hours_until, title, campaign_name}), additionally attach:recap_excerpt: excerpt of the most recent session in the campaign whose audio status is approved-side (AudioProcessingStatusintrashed/approved,models/session.py:27-34) and has a non-emptysummary— truncated server-side to fit the Discord embed budget (field value ≤ 1024 chars; keep the whole embed ≤ 6000)open_threads: up to ~5 unresolved plot threads (PlotThread.resolved == False,webapp/backend/app/models/plot_thread.py:22) as short titles, if any existBot (
bot/questboard_bot/cogs/notifications.py: dispatch map:169routessession_reminder→_handle_reminderat:319-338): whenrecap_excerptis present, render a "Previously on…" embed field (and an "Open threads" field when provided). Absent keys → render exactly as today.Toggle: campaign-level setting
recap_in_reminder(default on; harmless because the block is skipped when no approved summary exists). Surface in campaign settings next to reminder offsets.Graceful skip: campaigns with no prior approved summary get today's reminder unchanged — no empty "Previously on" section.
Out of scope
send_recap_emailalready covers post-session email; evaluate separately).Acceptance criteria
References
webapp/backend/app/tasks/reminder_tasks.py:998(poll_session_reminders),:1054(offsets),:1090-1104(payload)webapp/backend/app/services/bot_pubsub.py:29(publish_bot_event)bot/questboard_bot/cogs/notifications.py:169(dispatch),:319-338(_handle_reminder)webapp/backend/app/models/session.py:115(summary),:27-34(AudioProcessingStatus)webapp/backend/app/models/plot_thread.py:22(resolved)webapp/backend/app/routers/sessions.py:436(approve_audio)Filed from the July 2026 full-project review.
Done — merged in PR #198 (backend+bot
1007bb6, frontendc4523ab). CI green. This closes out the v3.7.0 Table Tools milestone.Shipped:
recap_in_reminder(default on); migrationf9a0b1c2d3e4(round-trips)._build_reminder_recapattaches, on the final (smallest-offset) reminder only: the newest approved-side (trashed/approved) session summary, truncated ≤1024 chars on a word boundary, plus up to 5 open player-visible quests. Returns{}(graceful skip) when there's nothing to show; non-final offsets stay byte-identical.BOT_CONTRACT_VERSIONbump.Two correctness points beyond the original spec: the recap fires once on the final offset (not repeated on the 7-day/24h reminders); and
open_threadsis filtered in SQL toplayer_visible=true AND status in (active, rumored)— because #110's spec predated #115 (which replacedresolvedwithstatus/player_visible), and this posts to the players' channel, so GM-private threads can never leak (explicit test).Tests: backend 528 pass (+12); bot 192 pass (+4, run explicitly); frontend 263 (+1).
Acceptance criteria met: recap carried exactly once on the final offset; no approved summaries → byte-identical to current behavior; toggle off restores current behavior; excerpt respects the 1024-char field limit. (Webhook/email fallback recap left out of scope — degraded no-
guild_idpath, untouched.)Closing.