feat(bot,webapp): notifications out — DMs, the GM's warnings, email that reaches people (lane 10 PR 2) #865
Loading…
Reference in a new issue
No description provided.
Delete branch "build/lane10-2-notify-out"
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 #840. Lane 10 (#736) PR 2, covering D151, D152, D153, D159 (server side only) and D161. Coverage rows: IS-167, IS-168, EX-030, EX-031, EX-034, IS-213, EX-040, EX-041.
What's in it
notification_dmon the durable stream: one line, a link, and the time for session news.closedorno_shared_serverto a new route,POST /api/bot/users/discord/{id}/dm-status, which records it on the link. That route is additive, so the contract stays at v2./identitylistsdm./identitygainsfeatures.feedback_requestedis written when a session completes.campaigns.voice_channel_idis stored and exposed to GMs. No pages yet.Migration
10a1b2c3d4e5_outbound_notifications:notifications.in_bell;platform_links.dm_failed_*;campaigns.voice_channel_id.It re-chains onto main's head at merge.
Tests
For the owner (on the questions page):
PR 8 will give the fake bot
features: ["dm"].🤖 Generated with Claude Code
Server and bot halves together; no pages. - Discord DMs (D152): `notification_dm` on the durable stream, claimed before publish, the bell's line + link + time for session news only; sent only to a bot whose GET /identity lists "dm" in its new `features`. The bot's handler is exempt from the channel check and reports refusals to the new POST /api/bot/users/discord/{id}/dm-status (closed | no_shared_server | ok); the settings carry a `warning` beside the DM column until the next DM lands. - The GM's warnings (D151): session_at_risk and audio_deletion_pending are GM-only bell rows, DM'd per the GM's grid (GM tasks x DM on by default), and posted to the channel only when no GM is reachable by DM. The at-risk poller runs for campaigns without a guild. - A group without Discord (D153, #840): session news is emailed to every member unless Sessions email is explicitly off; the from-address email backends are retired. Reminders are `session_reminder` rows per member (only the last in the bell), reading the reader's RSVP. - The reader's time: users.timezone, else the campaign's, else UTC, with the abbreviation, in notification emails and calendar invites. - One star, one wish (D161): `feedback_requested`, written on completion for the players Home asks, never in the bell. - D159: campaigns.voice_channel_id, stored and shown to GMs. - PUT notification-settings writes only cells that change and never refuses an unchanged echo, so the page's whole-grid save keeps the defaults. - Migration 10a1b2c3d4e5 on c4e6a8b0d2f1: the kind CHECK is the union with lane 9 A's and lane 7's kinds; notifications.in_bell; platform_links dm_failed_at/dm_failed_reason; campaigns.voice_channel_id. Bot contract unchanged (additive). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>The page at /campaigns/:id/safety (CAMP-18): lines and veils for everyone; for a GM, Edit, Remove with Undo (D12), "+ Add", and the waiting queue with Add as a veil / Add as a line (in the GM's wording) and Dismiss. A player gets the anonymous suggest card with the drawn words (T3(a)) and every GM named (T10(a)), and IS-017's Sent card. The flag dialog, "Flag something to Dana", behind four doors: This week's card (IS-205, about no session), the foot of a live or played session's page (T2(a), IS-210), and Table mode's players' face ("Flag something quietly"). The GM's Table-mode face gets the quiet "N waiting in Table rules & safety" in its place, polled from GET …/safety/waiting (T4(a)). The Contents gains the Table rules & safety entry, with the GM's count (IS-204, IS-212); a player is never asked for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"Make it canon" on a party thread wrote the answer, unblocked, into a wiki page the whole table reads, so members outside the party read it there (found by the lane 8 survey). CANON_AUDIENCES is ("table",) until lane 8's party and visibility blocks (IS-148) can carry the thread's audience into the article; a party answer is now a 422, like a narrower one. The thread page follows: no "Make it canon" on a party thread. Tests hold both sides; API.md and the CHANGELOG say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Admin becomes a place with sections in Home's Contents rather than a page of ten tabs, reusing the same shape lane 3 gave Settings: - adminSections.js (16 sections, the survey's addresses); ADMIN_SECTIONS' `built` flag follows settingsSections.js's own rollout rule — three sections with no legacy screen at all (Content notices, Instance health, Plans & entitlements) stay unmounted for their own PR (4c/4d). - /admin/* routes; `?tab=` redirects to the new address (paths are contracts, R1-8); `bot` (ambiguous) goes to AI models. - Home's Contents "This instance" group, the crumb ("Admin › <section>"), and IS-377's Stop for a non-administrator (AdminDoc.jsx). - The index (GET /admin/overview, already on this branch from lane 9 A): grouped status lines, D142's quiet leftovers box, and D141's accept/ decline banner for an offered server. - ADMIN-15 About this server: the admin GET/PUT this lane's server work added, now with a form. - ADMIN-1 Users: the owner's chip, "Hand the server on…" (an offer, D68), Make/Remove administrator (a dialog, then an undo), delete with the typed name and the blocked-by-campaigns state, and D56's search-scope radio cards (new GET endpoint — the setter existed, nothing read it back). - ADMIN-2 Campaigns: the instance-wide list, and an immediate (never an offer) hand-on for a campaign whose owner is unreachable or just moved on — the admin's own recovery path either way. - D141's server-scoped attention item: AttentionItem.campaign_id/ campaign_name become optional for the one kind that isn't a campaign's (server_owner_offer), checked ahead of the membership scope so it reaches an administrator with no campaigns of their own. - Every other built section keeps working at its new address by mounting pages/Admin.jsx's existing component unchanged (the tab bar is gone); AI models and Discord share one interim mount until page 4c splits BotSettings along the new addresses — a documented departure. - J1's Admin leg walked for real: the leftovers list, Backup's own heading and Contents-current row, a whole-page axe scan (no longer scoped to [role="alert"]), the desktop project settling the step, and the keyboard project reaching Admin through the account menu and its Contents sub-list. A new IS-377 journey test (Priya, stopped, axed). Backend: GET /api/admin/settings/member-search-scope (D56's setter has existed since #855; nothing read it back before this). Not in this PR (see the survey's §7 row 4a and the file's own notes): AI models/AI usage & speed/Discord's real split, Recording & retention, Backup, Content packs, Content notices, Audit log's names/categories, and Plans & entitlements — pages 4b/4c/4d. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>CI run 11143 failed two journeys on all three projects. Both are new with this PR; its base, #868's run 11142, passed them.
A fix is being diagnosed on a
fix865branch, with J0b and J2 run locally. It will be fast-forwarded onto this PR and merged up to #864.This is fixed, and the fix has been merged up train 2 (the branch is now at
b7e84bcf).J2 was a real product bug, and it has been there since #655. Every per-person notification email was silently dropped:
asyncio.run;_load_configstarted a second event loop, which raised an error;#865 is the first change that sends session news down this path, so it's the first to expose the bug.
_deliverreads SMTP in its own session and passes it through, then sends viaasyncio.to_thread. This is the same pattern the SMTP test route uses.test_the_email_reaches_smtp_from_inside_the_delivery_tasks_loop, which fails without the fix.[Quest Board] Session N is on: …). It matches mail by its session link.J0b was an expected change. The e2e fake bot now reports
features: ["dm"], like the real bot. J0b asserts both cases:Locally, J0b and J2 passed 21 of 21 across the three projects.
Also merged into the train: main (#882's J5 fix and #870) and #880 (the J6 budget), in one pass.
Follow-ups are in #887: the confirming GM is notified of their own confirmation, and J2:36 depends on the bell's first page.