• v4.2.3 64b5e15920

    v4.2.3
    All checks were successful
    CI / Docker image build (push) Successful in 24s
    CI / Backend lint (ruff) (push) Successful in 36s
    CI / Bot/backend version sync (push) Successful in 38s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 50s
    Release / Create Forgejo release (push) Successful in 14s
    CI / Bot tests and audit (push) Successful in 1m36s
    CI / Frontend tests, audit, and build (push) Successful in 1m44s
    Release / Build and push versioned images (push) Successful in 4m52s
    CI / Backend migration, tests, and audit (push) Successful in 6m46s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 17m32s
    Stable

    claude-bot released this 2026-09-06 02:20:47 +00:00 | 171 commits to main since this release

    The first slice of v4.3.0, shipped early because three of these are things the Discord bot was telling your players that were not true. A failed summary is no longer announced as ready, every notification now links back to the session it is about, the bot no longer asks Discord for a permission it never used, and a campaign you archive stops scheduling sessions and pinging people about them. Plus three small web fixes: players can name their own character, wiki articles are readable on a phone, and an unreachable duplicate page is gone.

    No migration. The database schema is unchanged since v4.2.0 (e9fa0b1c2d3e remains the head).

    Bot API contract v1, unchanged. The notification payload gained an optional link field; an older bot ignores it and a newer bot renders without it. BOT_EXPECTED_APP_VERSION is bumped so the bot's startup log matches.

    One Discord setting you can now turn off. The bot no longer needs the "Message Content Intent" toggle in the Discord Developer Portal. Leaving it on is harmless; turning it off is one fewer privileged permission.

    No new settings. Nothing to add to .env.

    Fixed

    • [webapp] The wiki article page no longer squeezes the article text into a
      sliver on a phone
      (#376). The infobox now stacks below the article instead
      of sitting beside it below the lg breakpoint, so the article body gets the
      full screen width on narrow screens.

    • [webapp] Archiving a campaign now makes it go quiet (#479). Archiving was
      a display state and nothing more: it changed which list a campaign appeared
      in, and every scheduled task went on treating it as fully live. The root was
      not the reminders — it was session creation. The hourly materialiser selected
      active recurring series with no campaign check at all, so an archived campaign
      with a weekly game kept minting confirmed sessions 28 days ahead, every hour,
      and the reminder poller then correctly reminded a group that had stopped
      playing about every one of them. A campaign a GM had explicitly retired could
      therefore be the noisiest thing on a Discord server. Materialisation now skips
      archived campaigns (and trashed ones, which had the same hole), and the five
      notifications that fire with nobody in the loop — the session reminder, the
      at-risk warning, the three-day vote nudge, the recap email and the
      end-of-session attendance post — are suppressed for a campaign that is
      archived, each logging the campaign id once so an operator can see why nothing
      was sent. Nothing else changes: an archived campaign still transcribes and
      summarises its recordings, still writes journal entries, still warns before
      retention deletes its audio, and still answers a vote or a "reveal to Discord"
      that somebody actually asked for — the line is unprompted, not
      Discord-facing, because swallowing a button press would be a worse bug than
      the one being fixed. Future sessions that a series already materialised are
      kept, and archiving is never refused because of them: deleting them would
      destroy rows a group may have notes and attendance on, and refusing would make
      archiving fail on exactly the campaigns it exists for. They simply stop
      speaking, and the archive response now returns how many just went quiet.
      Un-archiving resumes everything, materialisation included.

    • [bot] A failed transcription no longer announces itself as "Session Summary
      Ready"
      (#372). When summarisation failed, the backend sent the same
      session_summarised event with an error instead of a summary, and the bot
      posted the success embed anyway: a green "Session Summary Ready" headline over
      the body "No summary was generated.", followed by 👍/⚠️/ reactions inviting
      the table to rate a summary that did not exist. The failure now gets its own
      red notice that names the session, says the recording was kept so it can be
      run again, and points a GM at the retry on the session page — with no feedback
      reactions, because there is nothing to rate. The underlying error goes to the
      bot's log rather than the channel.

    • [bot/webapp] Every Discord notification now links back to Quest Board
      (#391). Twelve of the thirteen notification embeds named a session and then
      left you to go and find it; only the vote reminder carried a link, and the
      summary embed's footer said "Full transcript available to GMs on Quest Board"
      without being clickable, because Discord embed footers cannot hold links. The
      backend now attaches the session's URL to every notification it sends, and
      each embed renders it as an "Open in Quest Board" field — the summary's
      signpost among them, moved out of the footer so it can actually be followed.
      Nothing changes for an instance with no APP_URL set: the field is omitted
      rather than filled with a link that goes nowhere.

    • [bot] The bot no longer asks Discord for the privileged Message Content
      intent
      (#394). It never read message text — every command is a slash
      command — but requesting the intent meant a self-hoster who did not tick
      "Message Content Intent" in the Discord Developer Portal got a bot that
      refused to connect and, under Compose's restart policy, restarted for ever.
      The intent is gone, so that toggle is no longer needed; Server Members
      Intent is still required
      and is what per-speaker recording attribution
      depends on. If a privileged intent ever is missing, the bot now says which
      one and where to enable it, once, and keeps serving /health instead of
      crash-looping. .env.example and the Discord setup docs have been corrected —
      they told you to enable Message Content and called Server Members optional,
      which was backwards.

    Internal (no operator-visible change)

    • Deleted the orphaned CampaignNotes page and its unused /campaigns/:id/notes
      route (#384). It was linked from nowhere and duplicated the campaign journal
      already shown on the campaign page itself; the "back to dashboard" pattern it
      was cited as an example of still exists in CampaignHighlights.jsx.
    • Added regression tests locking in that a player already sees an actionable
      "+ Add character" button on their own row when they have no character yet
      (#383). Investigation found the underlying behaviour was already fixed as a
      side effect of the multi-character work in #330; no production code changed.
    Downloads