fix(webapp): an archived campaign stops creating sessions and goes quiet #516
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/479-archived-campaign-outbound"
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?
Closes #479. Phase 1 of the v4.3.0 build order (#514), shipping early as v4.2.3.
The root was session creation.
materialize_session_seriesnow joins the campaign it was already fetching one row at a time for a timezone, and skips a series whose campaign is archived, or trashed, which turned out to share the hole:get_live_campaignreturnedNonefor a trashed campaign and the loop materialised into it anyway. The series is not deactivated, so un-archiving resumes with nothing to backfill.Suppression is a separate predicate,
campaign_service.campaign_accepts_outbound, applied at exactly five sites: session reminder, at-risk warning, three-day vote nudge, recap email, end-of-session attendance post. Each logs the campaign id once at info. The line is unprompted, not Discord-facing: a vote a player casts and a card a GM reveals still say what they were asked to say, because swallowing a button press would be a worse bug than the one fixed. The reminder check precedes theSessionReminderSentclaim and the vote check precedes thevote_reminder_sent_atstamp, so an archived campaign never burns a marker it did not use.process_audio,generate_journal_entryandwarn_before_audio_deletionare untouched and have a test each. The tests were proven to bite by making the exact wrong change the issue warns about (or campaign.is_archivedinsideget_live_campaign): those three go red and nothing else does.Already-materialised future sessions: warn, not refuse. They stay and go quiet.
POST /campaigns/{id}/archivenow returnssilenced_future_sessionson an additive subclass ofCampaignResponse, the count of confirmed or in-progress sessions still ahead, and stamps it into the audit context. No UI here; #382 can render it.No migration, no
/api/bot/*change. Newtests/test_archived_campaign_outbound.py(13 tests); full backend suite 2349 passed / 13 skipped; ruff clean.🤖 Generated with Claude Code
The open question the issue left: what to do about sessions a series had already materialised before the archive. Warn, not refuse. Deleting them would destroy rows a group may have notes and attendance on, to save a tidy-up they can do themselves; refusing would make archiving fail on exactly the campaigns it exists for, because a group that stopped playing mid-series is the common case, not the exception. So the sessions stay and simply go quiet, and `POST /campaigns/{id}/archive` now returns `silenced_future_sessions` — the count of confirmed or in-progress sessions still in the future, which is exactly the set the reminder poller would have announced. Recorded in the audit context too. Additive on a superset of `CampaignResponse`, so an existing client is unaffected; #382 can render it. No migration. The tests are the point of the commit. Five say the outbound paths go quiet; three say `process_audio`'s campaign lookup, the journal entry and the audio-deletion warning still run for an archived campaign, and those three are the ones that matter — they are what fails if anyone "simplifies" this by teaching `get_live_campaign` about `is_archived`, which was verified by making that exact change and watching them go red. The reminder test runs one poll over two campaigns rather than two polls over one, because a second poll takes the IntegrityError claim path and rolls the test transaction back underneath the assertion. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>3a43a4e3b5b2e8afb156b2e8afb1560aab58675b