• v4.6.2-rc3 d4522bdcee

    v4.6.2-rc3
    All checks were successful
    CI / Backend lint (ruff) (push) Successful in 29s
    CI / Which journeys does this change need? (push) Successful in 12s
    CI / Does this change touch the recording path? (push) Successful in 15s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 57s
    CI / Bot/backend version sync (push) Successful in 31s
    Release / Create Forgejo release (push) Successful in 24s
    CI / Feedback site tests (push) Successful in 49s
    CI / Bot tests and audit (push) Successful in 2m6s
    CI / Docker image build (push) Successful in 3m21s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 8m31s
    Release / Build and push versioned images (push) Successful in 12m48s
    CI / End-to-end journeys (desktop) (push) Successful in 11m6s
    CI / Frontend (shard 2/2) (push) Successful in 18m2s
    CI / Frontend (shard 1/2) (push) Successful in 22m43s
    CI / End-to-end journeys (phone) (push) Successful in 29m47s
    CI / Backend migration, tests, and audit (push) Successful in 33m46s
    CI / End-to-end journeys (keyboard) (push) Successful in 32m57s
    Pre-release

    claude-bot released this 2026-10-30 06:40:49 +00:00 | 307 commits to main since this release

    Prerelease of 4.6.2 for testing on the dev server; not for production.

    Added

    • [repo] The feedback site has a name-rating page at /names. Testers draw
      names from the built-in generator (no AI), mark each up or down, and leave
      notes per style; the admin readout tallies them (#1257).
    • [webapp] A map marker can be edited: its label, line, kind, page, map, holding, position and who sees it (#1260).
      PATCH /api/campaigns/{id}/markers/{marker_id}. A GM edits any marker; a player edits only her own, as a note, place or danger, with no links. A marker's page can no longer be one in the wiki's bin, on create or edit.
    • [webapp] Maps and atlases can be renamed, changed and deleted into a bin, and travel links and trail stops can be removed (#1263).
      PATCH and DELETE for maps and atlases (GM only), GET /api/campaigns/{id}/maps-bin with restore routes, and a daily purge that removes the rows, pictures and tiles after the same grace period as the wiki bin. DELETE for a travel link and for a trail stop; a travel link can be GM-only. Migration f1263a0b1c2d.
    • [webapp] A map has a thumbnail, at most 1024 px on its longest side (#1263).
      The worker writes it when it cuts the map's tiles, and GET /api/campaigns/{id}/maps/{map_id}/thumb serves it with the same audience check as a tile. A daily task writes it for maps cut before this release.
    • [webapp] The map, planned parts that were missing (#1263). ![[Map: X]] in
      a page now shows a small picture of that map, linking to it. Place and
      travel-link labels that would sit on top of each other no longer do: the
      more important one keeps its label and the other shows on hover or focus.
      A marker that opens a map of its own wears a "+" and says so in its name. A
      group's page says "Holdings on the map" for the markers that name it as
      holder. The party page shows where the party is and its trail, for the party
      being viewed.
    • [webapp] The map, the rest of what was planned (#1263, #1260). A GM or a
      marker's author can Edit a marker (what is here, kind, a line, who sees
      it) and Move it; a GM also links it to a page ("The page it's about"),
      a holder ("Held by", for a holding) and a map it opens. A travel-link
      marker has Link to another map…, with a note and "Everyone at the
      table" or "GMs only", and each link can be removed. Every marker a player
      added says whose it is and who sees it, whatever its kind. A GM gets a
      Maps in this atlas list with rename, level name, who sees it, Replace
      the picture
      and delete for each map, atlas settings, and a Bin with
      Restore. Each trail stop links to its session and a GM can remove one or
      end the trail at a spot on the map; a campaign with two parties has a party
      switcher; a party on a map below shows on the map above; the trail is drawn
      across travel links; the party's own marker opens a small panel; the kind
      chips are remembered. Every removal asks first, and every failure shows the
      server's sentence with Try again.
    • [webapp] A trail stop reached by portal or magic has no line to it (#1273).
      After a stop at a portal, "Add a stop" asks where the party came out: a
      marker on any map (that portal's linked exits first), a spot, or nothing
      yet, so a session can end with the party mid-portal. "No line to it: they
      went by portal or magic" marks any stop the same way. The trail is drawn
      broken there, and also where it changes map with no link to show the way.
      Two portals on the same map can now be linked, such as the ways in and out
      of somewhere with no map of its own.
    • [webapp] The party's trail: "Add a stop…", stops in an order a GM can change, and a shorter list (#1275).
      "Where the party ended…" is now "Add a stop…". A GM moves a stop earlier or
      later within its session. The list under the map is grouped by session,
      newest first, with only the latest open.
      POST /api/campaigns/{id}/parties/{party_id}/trail/{stop_id}/move.

    Changed

    • [repo] The feedback site's readout is tabbed (#1257). The name sampler
      opens first, with a sub-tab per generator style holding its marks and notes;
      the finished redesign rounds sit under "Completed: redesign".
    • [webapp] "On this map" stays short (#1276). The markers under the map
      are grouped by kind, folded, with a "Find a marker" search that opens every
      kind with a match.
    • [webapp] A "travel link" marker is now called a portal. The map's legend
      says "Portals", a marker's kind is "A portal", and a marker made at the far
      end of a link is "Portal to …". Any marker can still open a map of its own.

    Fixed

    • [webapp] The wiki editor's [[ link menu opens under the line you are
      typing (#1261).
      It stood near the top of the text box whatever line you
      were on, so on a page with text it covered the first lines and the link
      being written. It now opens under the cursor's line, stays inside the
      editor, and opens above the line when there is no room below.
    • [webapp] A map whose picture is refused is no longer made twice (#1263).
      "Add a map" makes the map (and its atlas) once. If the picture is refused,
      Try again sends only the picture; or the map is left without one and its
      page offers "Add the picture". The ![[Map: X]] embed loads the map's
      thumbnail, falling back to the whole picture.
    • [webapp] Removing a marker that a trail stop uses no longer fails with a server error (#1263).
      A stop made from a marker now keeps the marker's map and position, so it stays on the trail when the marker goes. The migration fills them in for existing stops.
    • [webapp] Choosing a page in the article picker is clicking its name (#1268).
      The separate "Choose" link is gone, and on the map's marker form the name and
      the link no longer run together as one word.
    • [webapp] The party's trail runs in session order (#1269). Stops were
      ordered by when they were entered, so filling in earlier sessions after the
      latest one moved "The party is here" back to the last one entered. A stop
      made between sessions comes after the last session played when it was
      entered. Migration a1269b0c1d2e places the stops already recorded.
    • [webapp] "Where did the party end up?" finds a marker by searching its
      name, and offers every kind (#1271).
      Dangers and notes were missing from
      the list, and a dropdown of every marker would not have scaled to a long
      campaign's map. With no marker chosen, it goes on to choose a spot.
    • [webapp] Embedding a map in a wiki page: the [[ menu finds maps, and the preview draws them (#1277).
      After Map: the menu offers the campaign's maps by name, not "Create" a
      page; a map whose name starts with what is typed is offered among the
      pages. The editor's preview showed the embed as a page still to be written.
    • [webapp] A large map opens in moments, not 10–15 seconds (#1282). The
      worker now cuts each map at half size, a quarter and so on, until the whole
      map fits in one tile, and the page reads the level that fits the screen: an
      8192 × 6144 map opened zoomed out needs a few dozen tiles, not all 768. Its
      thumbnail shows at once underneath and sharpens as the tiles arrive. Maps
      already uploaded get their levels from the same backfill as the thumbnails
      (celery -A app.tasks.celery_app call app.tasks.map_tiles.backfill_map_thumbs),
      and daily after that.
    • [webapp] The party's trail and "Add a stop", from the 4.6.2-rc2 review (#1283).
      An older session in the trail list says "Show", and choosing a stop's place
      on the map opens its session, so its stops' Remove and Earlier/Later are
      easy to find. The marker searches in "Add a stop" list nothing until you
      type, apart from a portal's linked exits, so the form stays short.
    Downloads
  • v4.6.2-rc2 04371566ea

    v4.6.2-rc2
    All checks were successful
    CI / Which journeys does this change need? (push) Successful in 11s
    CI / Does this change touch the recording path? (push) Successful in 13s
    CI / Bot/backend version sync (push) Successful in 26s
    CI / Backend lint (ruff) (push) Successful in 31s
    Release / Create Forgejo release (push) Successful in 25s
    CI / Feedback site tests (push) Successful in 47s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 58s
    CI / Bot tests and audit (push) Successful in 2m7s
    CI / Docker image build (push) Successful in 2m19s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 9m12s
    Release / Build and push versioned images (push) Successful in 9m49s
    CI / Frontend (shard 2/2) (push) Successful in 13m22s
    CI / Frontend (shard 1/2) (push) Successful in 15m37s
    CI / End-to-end journeys (keyboard) (push) Successful in 10m53s
    CI / End-to-end journeys (desktop) (push) Successful in 12m1s
    CI / End-to-end journeys (phone) (push) Successful in 10m35s
    CI / Backend migration, tests, and audit (push) Successful in 50m16s
    Pre-release

    claude-bot released this 2026-10-29 21:57:58 +00:00 | 314 commits to main since this release

    Prerelease of 4.6.2 for testing on the dev server; not for production.

    Added

    • [repo] The feedback site has a name-rating page at /names. Testers draw
      names from the built-in generator (no AI), mark each up or down, and leave
      notes per style; the admin readout tallies them (#1257).
    • [webapp] A map marker can be edited: its label, line, kind, page, map, holding, position and who sees it (#1260).
      PATCH /api/campaigns/{id}/markers/{marker_id}. A GM edits any marker; a player edits only her own, as a note, place or danger, with no links. A marker's page can no longer be one in the wiki's bin, on create or edit.
    • [webapp] Maps and atlases can be renamed, changed and deleted into a bin, and travel links and trail stops can be removed (#1263).
      PATCH and DELETE for maps and atlases (GM only), GET /api/campaigns/{id}/maps-bin with restore routes, and a daily purge that removes the rows, pictures and tiles after the same grace period as the wiki bin. DELETE for a travel link and for a trail stop; a travel link can be GM-only. Migration f1263a0b1c2d.
    • [webapp] A map has a thumbnail, at most 1024 px on its longest side (#1263).
      The worker writes it when it cuts the map's tiles, and GET /api/campaigns/{id}/maps/{map_id}/thumb serves it with the same audience check as a tile. A daily task writes it for maps cut before this release.
    • [webapp] The map, planned parts that were missing (#1263). ![[Map: X]] in
      a page now shows a small picture of that map, linking to it. Place and
      travel-link labels that would sit on top of each other no longer do: the
      more important one keeps its label and the other shows on hover or focus.
      A marker that opens a map of its own wears a "+" and says so in its name. A
      group's page says "Holdings on the map" for the markers that name it as
      holder. The party page shows where the party is and its trail, for the party
      being viewed.
    • [webapp] The map, the rest of what was planned (#1263, #1260). A GM or a
      marker's author can Edit a marker (what is here, kind, a line, who sees
      it) and Move it; a GM also links it to a page ("The page it's about"),
      a holder ("Held by", for a holding) and a map it opens. A travel-link
      marker has Link to another map…, with a note and "Everyone at the
      table" or "GMs only", and each link can be removed. Every marker a player
      added says whose it is and who sees it, whatever its kind. A GM gets a
      Maps in this atlas list with rename, level name, who sees it, Replace
      the picture
      and delete for each map, atlas settings, and a Bin with
      Restore. Each trail stop links to its session and a GM can remove one or
      end the trail at a spot on the map; a campaign with two parties has a party
      switcher; a party on a map below shows on the map above; the trail is drawn
      across travel links; the party's own marker opens a small panel; the kind
      chips are remembered. Every removal asks first, and every failure shows the
      server's sentence with Try again.
    • [webapp] A trail stop reached by portal or magic has no line to it (#1273).
      After a stop at a portal, "Add a stop" asks where the party came out: a
      marker on any map (that portal's linked exits first), a spot, or nothing
      yet, so a session can end with the party mid-portal. "No line to it: they
      went by portal or magic" marks any stop the same way. The trail is drawn
      broken there, and also where it changes map with no link to show the way.
      Two portals on the same map can now be linked, such as the ways in and out
      of somewhere with no map of its own.
    • [webapp] The party's trail: "Add a stop…", stops in an order a GM can change, and a shorter list (#1275).
      "Where the party ended…" is now "Add a stop…". A GM moves a stop earlier or
      later within its session. The list under the map is grouped by session,
      newest first, with only the latest open.
      POST /api/campaigns/{id}/parties/{party_id}/trail/{stop_id}/move.

    Changed

    • [repo] The feedback site's readout is tabbed (#1257). The name sampler
      opens first, with a sub-tab per generator style holding its marks and notes;
      the finished redesign rounds sit under "Completed: redesign".
    • [webapp] "On this map" stays short (#1276). The markers under the map
      are grouped by kind, folded, with a "Find a marker" search that opens every
      kind with a match.
    • [webapp] A "travel link" marker is now called a portal. The map's legend
      says "Portals", a marker's kind is "A portal", and a marker made at the far
      end of a link is "Portal to …". Any marker can still open a map of its own.

    Fixed

    • [webapp] The wiki editor's [[ link menu opens under the line you are
      typing (#1261).
      It stood near the top of the text box whatever line you
      were on, so on a page with text it covered the first lines and the link
      being written. It now opens under the cursor's line, stays inside the
      editor, and opens above the line when there is no room below.
    • [webapp] A map whose picture is refused is no longer made twice (#1263).
      "Add a map" makes the map (and its atlas) once. If the picture is refused,
      Try again sends only the picture; or the map is left without one and its
      page offers "Add the picture". The ![[Map: X]] embed loads the map's
      thumbnail, falling back to the whole picture.
    • [webapp] Removing a marker that a trail stop uses no longer fails with a server error (#1263).
      A stop made from a marker now keeps the marker's map and position, so it stays on the trail when the marker goes. The migration fills them in for existing stops.
    • [webapp] Choosing a page in the article picker is clicking its name (#1268).
      The separate "Choose" link is gone, and on the map's marker form the name and
      the link no longer run together as one word.
    • [webapp] The party's trail runs in session order (#1269). Stops were
      ordered by when they were entered, so filling in earlier sessions after the
      latest one moved "The party is here" back to the last one entered. A stop
      made between sessions comes after the last session played when it was
      entered. Migration a1269b0c1d2e places the stops already recorded.
    • [webapp] "Where did the party end up?" finds a marker by searching its
      name, and offers every kind (#1271).
      Dangers and notes were missing from
      the list, and a dropdown of every marker would not have scaled to a long
      campaign's map. With no marker chosen, it goes on to choose a spot.
    • [webapp] Embedding a map in a wiki page: the [[ menu finds maps, and the preview draws them (#1277).
      After Map: the menu offers the campaign's maps by name, not "Create" a
      page; a map whose name starts with what is typed is offered among the
      pages. The editor's preview showed the embed as a page still to be written.
    Downloads
  • v4.6.2-rc1 593e003b4c

    v4.6.2-rc1
    All checks were successful
    CI / Backend lint (ruff) (push) Successful in 29s
    CI / Feedback site tests (push) Successful in 44s
    CI / Docker image build (push) Successful in 13s
    CI / Bot/backend version sync (push) Successful in 50s
    CI / Does this change touch the recording path? (push) Successful in 14s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m7s
    CI / Which journeys does this change need? (push) Successful in 18s
    Release / Create Forgejo release (push) Successful in 15s
    Release / Build and push versioned images (push) Successful in 58s
    CI / Bot tests and audit (push) Successful in 1m59s
    CI / End-to-end journeys (phone) (push) Successful in 10m31s
    CI / End-to-end journeys (keyboard) (push) Successful in 11m9s
    CI / End-to-end journeys (desktop) (push) Successful in 11m14s
    CI / Frontend (shard 2/2) (push) Successful in 18m31s
    CI / Frontend (shard 1/2) (push) Successful in 22m21s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 23m44s
    CI / Backend migration, tests, and audit (push) Successful in 1h0m15s
    Pre-release

    claude-bot released this 2026-10-28 19:21:24 +00:00 | 337 commits to main since this release

    Prerelease of 4.6.2 for testing on the dev server; not for production.

    Added

    • [repo] The feedback site has a name-rating page at /names. Testers draw
      names from the built-in generator (no AI), mark each up or down, and leave
      notes per style; the admin readout tallies them (#1257).
    • [webapp] A map marker can be edited: its label, line, kind, page, map, holding, position and who sees it (#1260).
      PATCH /api/campaigns/{id}/markers/{marker_id}. A GM edits any marker; a player edits only her own, as a note, place or danger, with no links. A marker's page can no longer be one in the wiki's bin, on create or edit.
    • [webapp] Maps and atlases can be renamed, changed and deleted into a bin, and travel links and trail stops can be removed (#1263).
      PATCH and DELETE for maps and atlases (GM only), GET /api/campaigns/{id}/maps-bin with restore routes, and a daily purge that removes the rows, pictures and tiles after the same grace period as the wiki bin. DELETE for a travel link and for a trail stop; a travel link can be GM-only. Migration f1263a0b1c2d.
    • [webapp] A map has a thumbnail, at most 1024 px on its longest side (#1263).
      The worker writes it when it cuts the map's tiles, and GET /api/campaigns/{id}/maps/{map_id}/thumb serves it with the same audience check as a tile. A daily task writes it for maps cut before this release.
    • [webapp] The map, planned parts that were missing (#1263). ![[Map: X]] in
      a page now shows a small picture of that map, linking to it. Place and
      travel-link labels that would sit on top of each other no longer do: the
      more important one keeps its label and the other shows on hover or focus.
      A marker that opens a map of its own wears a "+" and says so in its name. A
      group's page says "Holdings on the map" for the markers that name it as
      holder. The party page shows where the party is and its trail, for the party
      being viewed.
    • [webapp] The map, the rest of what was planned (#1263, #1260). A GM or a
      marker's author can Edit a marker (what is here, kind, a line, who sees
      it) and Move it; a GM also links it to a page ("The page it's about"),
      a holder ("Held by", for a holding) and a map it opens. A travel-link
      marker has Link to another map…, with a note and "Everyone at the
      table" or "GMs only", and each link can be removed. Every marker a player
      added says whose it is and who sees it, whatever its kind. A GM gets a
      Maps in this atlas list with rename, level name, who sees it, Replace
      the picture
      and delete for each map, atlas settings, and a Bin with
      Restore. Each trail stop links to its session and a GM can remove one or
      end the trail at a spot on the map; a campaign with two parties has a party
      switcher; a party on a map below shows on the map above; the trail is drawn
      across travel links; the party's own marker opens a small panel; the kind
      chips are remembered. Every removal asks first, and every failure shows the
      server's sentence with Try again.

    Changed

    • [repo] The feedback site's readout is tabbed (#1257). The name sampler
      opens first, with a sub-tab per generator style holding its marks and notes;
      the finished redesign rounds sit under "Completed: redesign".

    Fixed

    • [webapp] The wiki editor's [[ link menu opens under the line you are
      typing (#1261).
      It stood near the top of the text box whatever line you
      were on, so on a page with text it covered the first lines and the link
      being written. It now opens under the cursor's line, stays inside the
      editor, and opens above the line when there is no room below.
    • [webapp] A map whose picture is refused is no longer made twice (#1263).
      "Add a map" makes the map (and its atlas) once. If the picture is refused,
      Try again sends only the picture; or the map is left without one and its
      page offers "Add the picture". The ![[Map: X]] embed loads the map's
      thumbnail, falling back to the whole picture.
    • [webapp] Removing a marker that a trail stop uses no longer fails with a server error (#1263).
      A stop made from a marker now keeps the marker's map and position, so it stays on the trail when the marker goes. The migration fills them in for existing stops.
    Downloads
  • v4.6.1 cd79fd1c85

    v4.6.1
    All checks were successful
    CI / Bot/backend version sync (push) Successful in 27s
    CI / Which journeys does this change need? (push) Successful in 18s
    CI / Does this change touch the recording path? (push) Successful in 19s
    CI / Feedback site tests (push) Successful in 51s
    CI / Docker image build (push) Successful in 27s
    Release / Create Forgejo release (push) Successful in 9s
    Release / Build and push versioned images (push) Successful in 37s
    CI / Backend lint (ruff) (push) Successful in 2m2s
    CI / Bot tests and audit (push) Successful in 2m4s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 2m28s
    CI / Frontend (shard 2/2) (push) Successful in 7m40s
    CI / Frontend (shard 1/2) (push) Successful in 10m27s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 26m37s
    CI / End-to-end journeys (phone) (push) Successful in 27m10s
    CI / End-to-end journeys (keyboard) (push) Successful in 29m17s
    CI / End-to-end journeys (desktop) (push) Successful in 29m36s
    CI / Backend migration, tests, and audit (push) Successful in 32m36s
    Stable

    claude-bot released this 2026-10-28 07:36:26 +00:00 | 353 commits to main since this release

    A point release: the map no longer goes blank while you place a marker, the GM
    guide says what size of map works best, and the dependency updates since 4.6.0,
    with security fixes in four packages. Tried on the dev server as 4.6.1-rc1.

    Upgrading from 4.6.0

    • No database migrations. Back up, then docker compose pull and
      docker compose up -d.
    • BOT_CONTRACT_VERSION is unchanged (2). The backend and the bot can be
      updated one at a time.
    • Starter content packs are on this release's page too, so Admin → Content
      packs keeps offering them.

    Added

    • [docs] The GM guide says what size of map works best (#1253). A new part of
      "Prep" in Running your first campaign (/help/gm) covers where maps live,
      the shape and size that suit the map frame (landscape, 3,000 to 4,000 pixels
      on the long side), why a very large map is slow to open, and the limits on a
      map's picture, including WebP's lower one.

    Fixed

    • [webapp] The map no longer goes blank when you press "Add a marker"
      (#1251).
      Starting to place a marker wiped Leaflet's own classes off the
      map, and without them every tile shrank to nothing; the picture stayed gone
      until a reload. The map now stays visible while you pick the spot.

    Security

    • [repo] Dependency updates with security fixes: aiohttp 3.14.4, pypdf
      6.21.2, Pillow 13 and authlib 1.9.1, along with the routine updates merged
      since 4.6.0.
    Downloads
  • v4.6.1-rc1 129d558230

    v4.6.1-rc1
    All checks were successful
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 57s
    CI / Backend lint (ruff) (push) Successful in 33s
    CI / Bot/backend version sync (push) Successful in 22s
    CI / Feedback site tests (push) Successful in 47s
    CI / Does this change touch the recording path? (push) Successful in 15s
    CI / Which journeys does this change need? (push) Successful in 11s
    Release / Create Forgejo release (push) Successful in 22s
    CI / Bot tests and audit (push) Successful in 2m2s
    CI / Docker image build (push) Successful in 5m1s
    Release / Build and push versioned images (push) Successful in 5m30s
    CI / Frontend (shard 1/2) (push) Successful in 9m20s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 10m26s
    CI / Frontend (shard 2/2) (push) Successful in 17m30s
    CI / End-to-end journeys (desktop) (push) Successful in 13m40s
    CI / End-to-end journeys (phone) (push) Successful in 26m9s
    CI / Backend migration, tests, and audit (push) Successful in 33m44s
    CI / End-to-end journeys (keyboard) (push) Successful in 28m2s
    Pre-release

    claude-bot released this 2026-10-28 05:51:14 +00:00 | 354 commits to main since this release

    Prerelease of 4.6.1 for testing on the dev server; not for production.

    Added

    • [docs] The GM guide says what size of map works best (#1253). A new part of
      "Prep" in Running your first campaign (/help/gm) covers where maps live,
      the shape and size that suit the map frame (landscape, 3,000 to 4,000 pixels
      on the long side), why a very large map is slow to open, and the limits on a
      map's picture, including WebP's lower one.

    Fixed

    • [webapp] The map no longer goes blank when you press "Add a marker"
      (#1251).
      Starting to place a marker wiped Leaflet's own classes off the
      map, and without them every tile shrank to nothing; the picture stayed gone
      until a reload. The map now stays visible while you pick the spot.
    Downloads
  • v4.6.0 216e8574c9

    v4.6.0
    All checks were successful
    CI / Does this change touch the recording path? (push) Successful in 20s
    CI / Which journeys does this change need? (push) Successful in 26s
    CI / Docker image build (push) Successful in 29s
    CI / Bot/backend version sync (push) Successful in 51s
    Release / Create Forgejo release (push) Successful in 24s
    CI / Backend lint (ruff) (push) Successful in 1m1s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m29s
    CI / Feedback site tests (push) Successful in 1m37s
    CI / Bot tests and audit (push) Successful in 2m44s
    Release / Build and push versioned images (push) Successful in 5m40s
    CI / Frontend (shard 2/2) (push) Successful in 9m49s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 10m19s
    CI / Frontend (shard 1/2) (push) Successful in 11m11s
    CI / End-to-end journeys (phone) (push) Successful in 22m42s
    CI / End-to-end journeys (keyboard) (push) Successful in 24m22s
    CI / End-to-end journeys (desktop) (push) Successful in 24m44s
    CI / Backend migration, tests, and audit (push) Successful in 33m50s
    Stable

    claude-bot released this 2026-10-18 06:52:52 +00:00 | 379 commits to main since this release

    The in-world calendar, campaign templates and cloning, pictures brought in from a
    web address with a Files page for each campaign, the line of the night, choosing
    who was involved in an event, and the Pathfinder 2e and D&D 5e starter content
    packs. Tried on the dev server as 4.6.0-rc1 and -rc2.

    Upgrading from 4.5.3

    • Five database migrations, all additive (a1191b0e5701, a1190e7e0a01,
      a1181f11e5a1, a126ca1e0d01, a125c0b1e5e1). Back up first, then run
      make migrate-safe from the new release before docker compose up -d.
    • BOT_CONTRACT_VERSION is unchanged (2). Update the backend and the bot
      together all the same: "In the story" and "Line of the night" on the approved
      summary post are the bot's. An older bot keeps working without them.
    • Campaign exports are schema version 21. 4.6.0 imports older files; an
      older Quest Board cannot import a 4.6.0 export.
    • Starter content packs. After upgrading, an administrator can fetch the
      Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 packs from Admin → Content packs.
      They come from this release's own page. Nothing is loaded automatically.
    • Pictures by address. The page-picture "from a web address" fetch now
      takes https only, refuses compressed responses, checks every redirect, and
      is rate limited (30 a person and 60 a campaign in ten minutes).

    Added

    • [webapp] The screens for bringing a picture in, and for a campaign's files (#1181). The wiki editor offers "Bring it in" for a picture written as an outside address (and from the picture button, by address), rewriting the text to the stored copy, with the server's plain reason when a fetch is refused. Wiki health lists "Pictures still on other sites" with a "Bring it in" and an Undo. Settings → Wiki has "Who can add pictures and files", and the editor hides the picture controls the server would refuse. Settings → Files lists a campaign's stored files as a grid or a table, with previews, filters, the source address, files nothing uses, a viewer with zoom, and Remove (showing the text that would break). Admin → Files lists them across campaigns by size, and Preview and Open are separate, recorded steps; the Privacy page says so.
    • [webapp] Bring a picture in from a web address, and a Files page for each campaign and for admins (#1181). Anyone who can edit a page can have the server download a picture once and store it as an upload (JPEG, PNG, WebP or GIF, metadata stripped, never SVG); the source address and time are kept on the file and shown to GMs and admins only. Downloads count toward the campaign's storage, and are limited to 30 a person and 60 a campaign in ten minutes. A new campaign setting, "Who can add pictures and files" (everyone who can edit, or GMs only), is enforced on every upload route and on bring-it-in. Wiki health gains "Pictures still on other sites", with a "Bring it in" per page that rewrites the text as the GM's own edit. Campaign → Settings → Files lists every stored picture, map image, handout and attachment with previews, who added it, its address and whether anything still uses it, and removes them; orphans are listed apart. Admin → Files lists the same across campaigns by size, list only: opening or previewing another campaign's file is a deliberate act and is written to the audit log.
    • [webapp] A GM can change who was involved when rewording an event. Reword can now also set an event's people (a character, a member, or an NPC page; or nobody, for scenery or a narrated happening). The change is saved with the reword, so it is recorded like one (who and when), survives a regenerate, and the next summary is composed from the corrected people. The event log and the summary's anchors show the corrected people, and the GM still sees who the model named. A GM-only search lists the campaign's characters and members first, then NPC pages.
    • [webapp] The line of the night, on screen. Settings, General has "Line of the night: Chosen by the GM / Voted by the table". On a session's Review tab the GM marks, moves or clears the line of the night on an approved highlight, sees when voting closes, can close it now, and picks after a tie or a vote nobody cast. Players read the line of the night first with a small label, and while voting is open vote for a line (changing or withdrawing the vote), with counts hidden until it closes. The campaign Highlights page marks the lines of the night and can show only those.
    • [webapp] The line of the night. A session can have one highlight marked as its line of the night. A campaign setting (Settings, General) says who picks it: the GM (the default), or the table by vote. In GM mode the GM marks, moves or clears one approved highlight. In vote mode every member, GMs included, votes once and can change the vote; voting opens when the GM approves the summary and closes three days later, or when the GM closes it early. Counts stay hidden until it closes, then the most votes wins, and a tie or no votes falls to the GM. The approved-summary post in Discord names a chosen quote as the line of the night. Exports carry the setting and each session's line of the night; imports accept files without them.
    • [webapp] The Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 starter packs can be fetched from Admin → Content packs. Each release carries them as assets and the fetch address follows the running version, so a release candidate uses its own release. The reviewed SOURCES.md for each pack is in docs/content-packs/4.6.0/. (#1200)
    • [webapp] Start a new campaign from an existing one, and save a campaign's
      setup as a file (#125).
      A GM of the source campaign (an archived one is
      fine) can copy its settings, game system, calendar, wiki page templates and
      reputation levels, and the wiki pages and maps they tick, into a new campaign
      they own, as a background job like an import. The world starts ticked
      (places, organisations, deities, creatures, artifacts, concepts, historical
      events, documents, NPCs); player characters, quests and pages made from a
      session start unticked. GM-only text stays GM-only, personal notes are never
      copied, links to pages left behind become plain words, everything that points
      at a session is dropped (timeline events keep their in-world dates) and quests
      come back not started. The campaign's own content packs are copied only with
      the current affirmation, and the table's lines and veils only with "Invite the
      same people", which sends each other member an ordinary single-use invitation
      (a GM of the old campaign is invited as a GM); nobody is added directly. A
      setup file is a campaign file with "setup_only": true: "Start from a file"
      is the ordinary import. New routes: GET /api/campaigns/{id}/setup,
      POST /api/campaigns/{id}/setup-file, POST /api/campaigns/{id}/clone.
      One audit entry is written on each campaign.
    • [webapp] "A one-shot" accepts one_shot: true, and is stripped back
      further (#125).
      Its single reminder is now the day before (it was two
      hours), the "who hasn't answered" nudge is off, and once its session has been
      played the GM gets one "Needs you" suggestion to archive it
      (archive_one_shot), answered with
      POST /api/campaigns/{id}/archive-suggestion/dismiss and never repeated.
    • [webapp] "Invite the same people" now sends each person their invitation. Starting a campaign from another with that ticked puts " invited you to ." in each other member's bell, and emails it where their settings say so, with a link to join. Nobody is added until they accept, and a used, expired or withdrawn invitation says so instead of linking (#125).
    • [webapp] The screens for starting a campaign from another (#125). Settings → Archive has "Start a new campaign from this one…" and "Save the setup as a file…", one dialog for both: pick the wiki pages (the world starts ticked), maps, Lines & Veils, the campaign's own packs (after affirming them again), and whether to invite the same people. New campaign has "Start from a file" beside "Import a campaign". Needs you asks "Archive it?" for a played one-shot, the bell shows an invitation to a campaign with its join link, and an invitation link says when it was for a GM.
    • [repo] The in-world calendar engine (#126, step 1; nothing user-facing yet). A calendar definition format (months, festival days and leap days with Gregorian-style rules, an optional "outside the week" flag for festival and leap days, week, moons, era; every entry has a stable id), validated by app/schemas/world_calendar.py; pure arithmetic in app/services/world_calendar.py and, identically, webapp/frontend/src/calendar/engine.js (sort numbers, weekdays, moon phases, session ranges, formatting, and the check and date remapping for a calendar change). One shared file of test cases is run by pytest and vitest; scripts/calendar_cases.py regenerates it from brute-force enumeration (--check verifies it). Four generic presets.
    • [webapp] Date the story: the in-world calendar's screens (#126). Settings gains a Calendar section for GMs: start from one of four generic shapes, copy another campaign's calendar or load a file, then edit the months, festival and leap days (placed "after" a month, with leap rules and an "outside the week" tick), the week, the moons, the era and how a date reads, with a live preview and a check before saving that lists what a change would refuse or how many dates it would move. One date picker serves everything: a month grid with a column for each day of the week, festival days as their own cells, years before year 1 ("200 before DR"), the moons' phases as text, and the arrow, Home, End and Page keys; on the wiki it also offers Day, Month or Year with "About". A GM dates a session from Manage ("In the story…": the day it started, "The next day", hours or days covered, "Ran past midnight"), and the review offers one quiet row, "Where in the story?", with one press to save. Every member sees "In the story: 3–5 Mirtul, 1492 DR" under a dated session's head and on the Sessions list, and the campaign home says where the story has got to. A wiki timeline event can carry a date or a named age ("the Age of Ash", placed by choosing what it comes after), and the Storyline can show the timeline "As we played" or "In the world", with "9 days later" between sessions.
    • [webapp] The in-world calendar's server side (#126; the screens follow). A campaign can have one calendar (GET/PUT/DELETE /api/campaigns/{id}/calendar: every member reads it, only a GM writes), copied in from one of four generic presets (GET /api/calendar-presets), from another campaign the caller GMs, or from a JSON file (/calendar/file). A change is checked first (POST /calendar/check): removing a month or day that a session or wiki event is dated on is refused with the list of what uses it, and every other change moves the stored dates with their entries and recomputes the sort order in the same save. Sessions take an in-world start day and a span in hours or days (under 24 hours stays on the start day, plus "ran past midnight") through the ordinary session update, GM only; the session, the Sessions list and the campaign home carry the formatted range, the moons' phases on the start day, the campaign's "today", and a prefilled start for an undated session. Wiki timeline events take an in-world date at day, month or year precision (with "about"), or a named age with no year, and the Storyline's timeline read model carries each line's in-world label and sort number. The campaign export is now schema 21 and carries the calendar and every date; a version 20 file imports with no calendar. Additive migration a126ca1e0d01.
    • [bot] The approved summary's Discord post says where it was in the story. When the session has in-world dates, the post gains a line "In the story: 3–5 Mirtul, 1492 DR" (additive: extra.world_range, no contract bump). The text comes from names the GM typed into the calendar, so it is reduced to letters, digits, spaces and a few punctuation marks before it is posted.

    Changed

    • [webapp] Fewer read-only pages change anything when they are opened. Making a ready-made name set is now a POST (the GET only shows what is cached), the secrets health report no longer upgrades secrets (that happens at startup), an out-of-date ownership offer is expired by a background task rather than by looking at it, and the sheet pages no longer seed the built-in definitions on read (#1178).
    • [webapp] The last gaps in stars and wishes (#122) and downtime (#124) are
      closed.
      A GM sees the players' stars and wishes on the session's Review
      tab, named only where the player signed. The star-and-wish ask goes only to
      players marked as having attended, once any attendance is recorded. On the
      party page a player edits or removes their own waiting downtime and a GM any
      entry (and can approve it as they save it), with Undo after a removal. Downtime
      totals now count approved days only, show "Days since Session N" per
      character, and keep days still waiting apart as "(+3 waiting)". The downtime
      list's totals gain a pending_days figure, and their days no longer
      includes entries waiting for a GM.
    • [repo] Renovate moves the Playwright image and @playwright/test together, in one PR, so a bump can no longer leave the journeys on an image without the browsers its version needs.

    Fixed

    • [webapp] The page-picture "from a web address" fetch now checks every redirect (#1181). It used to check only the address it was given, then follow redirects freely, so a public address could redirect the server to a private one. Each hop is now checked again, at most five are followed, a connection is made only to the address that was checked, and anything but a public address (carrier-grade NAT, 0.0.0.0, IPv4-mapped IPv6 and the like) is refused.
    • [webapp] Switching a campaign to "Voted by the table" no longer reopens voting on sessions already approved. They keep the GM's pick and show no vote; votes already cast are never deleted. "N still deciding" on This week counts players only, like the "Are you in?" line.
    • [repo] The end-to-end journeys checks are named once each ("End-to-end journeys (desktop)"), not "(desktop) (desktop)". The job name no longer spells the matrix value, which Forgejo appends itself (#1203).
    • [webapp] The hourly import sweep no longer breaks a slow "Start a new campaign from this one". It queued every waiting job as an import, which ended a clone with "uploaded file no longer on server". Jobs now carry a kind (import or clone) and a clone keeps its choices, so the sweep queues the right task. GET /api/campaigns/import/jobs/latest and the job responses say which kind a job is, so the import page can tell them apart (#125).
    • [webapp] Six screen fixes from the 4.6.0-rc1 render check. Settings → Files shows each file as a stacked card on a phone and tells files on one page apart by size and date; the party page's Downtime and "What we carry" stack on a phone; the clone dialog keeps its buttons together in a footer that stays in view; the calendar date picker shows just the year when the era has no label; the review tab no longer treats "no summarisation run yet" as an error.
    • [repo] A prerelease's image job no longer reports a failure after pushing every image. The :latest push was skipped with [ … ] && …, which made the step exit 1 for every prerelease.
    • [webapp] This week no longer counts the GM in "have answered". Under "Are you in?" it now reads "4 of 5 players have answered", "No players have answered yet." when only the GM has, and nothing at a table with no players, the same as the session page. Both pages print one shared line, so they cannot drift apart again. (#1202)

    Security

    • [webapp] The check on addresses the server fetches pictures from now refuses IPv6 forms that hide a private IPv4 address (#1181). NAT64 (64:ff9b::/96, 64:ff9b:1::/48), 6to4 (2002::/16) and Teredo (2001::/32) are blocked outright, and the IPv4 address inside each, and inside an IPv4-mapped one, is checked as well. 64:ff9b::7f00:1 used to pass. The same check serves the page picture's "from a web address" fetch.

    • [webapp] The server no longer decodes a compressed answer when it fetches a picture from a web address (#1181). It asks for the file uncompressed, refuses an answer that says it is compressed (a few KB of gzip could unpack to hundreds of MB before the size limit was checked), and counts the limit on the bytes that arrive. Multicast, site-local and IPv4-compatible addresses are refused as well, and the fetch ignores proxy settings in the environment.

    • [bot] multidict moves to 6.9.1, out of two advisories (PYSEC-2026-4244, CVE-2026-104874) that the daily dependency audit flagged on 15 and 16 Oct.

    Downloads
  • v4.6.0-rc2 94deb061b9

    v4.6.0-rc2
    All checks were successful
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m4s
    Release / Create Forgejo release (push) Successful in 15s
    CI / Which journeys does this change need? (push) Successful in 17s
    CI / Does this change touch the recording path? (push) Successful in 18s
    CI / Bot/backend version sync (push) Successful in 26s
    Release / Build and push versioned images (push) Successful in 26s
    CI / Backend lint (ruff) (push) Successful in 35s
    CI / Feedback site tests (push) Successful in 58s
    CI / Bot tests and audit (push) Successful in 2m18s
    CI / Docker image build (push) Successful in 2m20s
    CI / Frontend (shard 2/2) (push) Successful in 9m58s
    CI / Frontend (shard 1/2) (push) Successful in 12m39s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 19m10s
    CI / End-to-end journeys (phone) (push) Successful in 22m5s
    CI / End-to-end journeys (keyboard) (push) Successful in 24m19s
    CI / End-to-end journeys (desktop) (push) Successful in 24m35s
    CI / Backend migration, tests, and audit (push) Successful in 41m44s
    Pre-release

    claude-bot released this 2026-10-17 21:59:38 +00:00 | 381 commits to main since this release

    Prerelease of 4.6.0 for testing on the dev server; not for production.

    Added

    • [webapp] The screens for bringing a picture in, and for a campaign's files (#1181). The wiki editor offers "Bring it in" for a picture written as an outside address (and from the picture button, by address), rewriting the text to the stored copy, with the server's plain reason when a fetch is refused. Wiki health lists "Pictures still on other sites" with a "Bring it in" and an Undo. Settings → Wiki has "Who can add pictures and files", and the editor hides the picture controls the server would refuse. Settings → Files lists a campaign's stored files as a grid or a table, with previews, filters, the source address, files nothing uses, a viewer with zoom, and Remove (showing the text that would break). Admin → Files lists them across campaigns by size, and Preview and Open are separate, recorded steps; the Privacy page says so.
    • [webapp] Bring a picture in from a web address, and a Files page for each campaign and for admins (#1181). Anyone who can edit a page can have the server download a picture once and store it as an upload (JPEG, PNG, WebP or GIF, metadata stripped, never SVG); the source address and time are kept on the file and shown to GMs and admins only. Downloads count toward the campaign's storage, and are limited to 30 a person and 60 a campaign in ten minutes. A new campaign setting, "Who can add pictures and files" (everyone who can edit, or GMs only), is enforced on every upload route and on bring-it-in. Wiki health gains "Pictures still on other sites", with a "Bring it in" per page that rewrites the text as the GM's own edit. Campaign → Settings → Files lists every stored picture, map image, handout and attachment with previews, who added it, its address and whether anything still uses it, and removes them; orphans are listed apart. Admin → Files lists the same across campaigns by size, list only: opening or previewing another campaign's file is a deliberate act and is written to the audit log.
    • [webapp] A GM can change who was involved when rewording an event. Reword can now also set an event's people (a character, a member, or an NPC page; or nobody, for scenery or a narrated happening). The change is saved with the reword, so it is recorded like one (who and when), survives a regenerate, and the next summary is composed from the corrected people. The event log and the summary's anchors show the corrected people, and the GM still sees who the model named. A GM-only search lists the campaign's characters and members first, then NPC pages.
    • [webapp] The line of the night, on screen. Settings, General has "Line of the night: Chosen by the GM / Voted by the table". On a session's Review tab the GM marks, moves or clears the line of the night on an approved highlight, sees when voting closes, can close it now, and picks after a tie or a vote nobody cast. Players read the line of the night first with a small label, and while voting is open vote for a line (changing or withdrawing the vote), with counts hidden until it closes. The campaign Highlights page marks the lines of the night and can show only those.
    • [webapp] The line of the night. A session can have one highlight marked as its line of the night. A campaign setting (Settings, General) says who picks it: the GM (the default), or the table by vote. In GM mode the GM marks, moves or clears one approved highlight. In vote mode every member, GMs included, votes once and can change the vote; voting opens when the GM approves the summary and closes three days later, or when the GM closes it early. Counts stay hidden until it closes, then the most votes wins, and a tie or no votes falls to the GM. The approved-summary post in Discord names a chosen quote as the line of the night. Exports carry the setting and each session's line of the night; imports accept files without them.
    • [webapp] The Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 starter packs can be fetched from Admin → Content packs. Each release carries them as assets and the fetch address follows the running version, so a release candidate uses its own release. The reviewed SOURCES.md for each pack is in docs/content-packs/4.6.0/. (#1200)
    • [webapp] Start a new campaign from an existing one, and save a campaign's
      setup as a file (#125).
      A GM of the source campaign (an archived one is
      fine) can copy its settings, game system, calendar, wiki page templates and
      reputation levels, and the wiki pages and maps they tick, into a new campaign
      they own, as a background job like an import. The world starts ticked
      (places, organisations, deities, creatures, artifacts, concepts, historical
      events, documents, NPCs); player characters, quests and pages made from a
      session start unticked. GM-only text stays GM-only, personal notes are never
      copied, links to pages left behind become plain words, everything that points
      at a session is dropped (timeline events keep their in-world dates) and quests
      come back not started. The campaign's own content packs are copied only with
      the current affirmation, and the table's lines and veils only with "Invite the
      same people", which sends each other member an ordinary single-use invitation
      (a GM of the old campaign is invited as a GM); nobody is added directly. A
      setup file is a campaign file with "setup_only": true: "Start from a file"
      is the ordinary import. New routes: GET /api/campaigns/{id}/setup,
      POST /api/campaigns/{id}/setup-file, POST /api/campaigns/{id}/clone.
      One audit entry is written on each campaign.
    • [webapp] "A one-shot" accepts one_shot: true, and is stripped back
      further (#125).
      Its single reminder is now the day before (it was two
      hours), the "who hasn't answered" nudge is off, and once its session has been
      played the GM gets one "Needs you" suggestion to archive it
      (archive_one_shot), answered with
      POST /api/campaigns/{id}/archive-suggestion/dismiss and never repeated.
    • [webapp] "Invite the same people" now sends each person their invitation. Starting a campaign from another with that ticked puts " invited you to ." in each other member's bell, and emails it where their settings say so, with a link to join. Nobody is added until they accept, and a used, expired or withdrawn invitation says so instead of linking (#125).
    • [webapp] The screens for starting a campaign from another (#125). Settings → Archive has "Start a new campaign from this one…" and "Save the setup as a file…", one dialog for both: pick the wiki pages (the world starts ticked), maps, Lines & Veils, the campaign's own packs (after affirming them again), and whether to invite the same people. New campaign has "Start from a file" beside "Import a campaign". Needs you asks "Archive it?" for a played one-shot, the bell shows an invitation to a campaign with its join link, and an invitation link says when it was for a GM.
    • [repo] The in-world calendar engine (#126, step 1; nothing user-facing yet). A calendar definition format (months, festival days and leap days with Gregorian-style rules, an optional "outside the week" flag for festival and leap days, week, moons, era; every entry has a stable id), validated by app/schemas/world_calendar.py; pure arithmetic in app/services/world_calendar.py and, identically, webapp/frontend/src/calendar/engine.js (sort numbers, weekdays, moon phases, session ranges, formatting, and the check and date remapping for a calendar change). One shared file of test cases is run by pytest and vitest; scripts/calendar_cases.py regenerates it from brute-force enumeration (--check verifies it). Four generic presets.
    • [webapp] Date the story: the in-world calendar's screens (#126). Settings gains a Calendar section for GMs: start from one of four generic shapes, copy another campaign's calendar or load a file, then edit the months, festival and leap days (placed "after" a month, with leap rules and an "outside the week" tick), the week, the moons, the era and how a date reads, with a live preview and a check before saving that lists what a change would refuse or how many dates it would move. One date picker serves everything: a month grid with a column for each day of the week, festival days as their own cells, years before year 1 ("200 before DR"), the moons' phases as text, and the arrow, Home, End and Page keys; on the wiki it also offers Day, Month or Year with "About". A GM dates a session from Manage ("In the story…": the day it started, "The next day", hours or days covered, "Ran past midnight"), and the review offers one quiet row, "Where in the story?", with one press to save. Every member sees "In the story: 3–5 Mirtul, 1492 DR" under a dated session's head and on the Sessions list, and the campaign home says where the story has got to. A wiki timeline event can carry a date or a named age ("the Age of Ash", placed by choosing what it comes after), and the Storyline can show the timeline "As we played" or "In the world", with "9 days later" between sessions.
    • [webapp] The in-world calendar's server side (#126; the screens follow). A campaign can have one calendar (GET/PUT/DELETE /api/campaigns/{id}/calendar: every member reads it, only a GM writes), copied in from one of four generic presets (GET /api/calendar-presets), from another campaign the caller GMs, or from a JSON file (/calendar/file). A change is checked first (POST /calendar/check): removing a month or day that a session or wiki event is dated on is refused with the list of what uses it, and every other change moves the stored dates with their entries and recomputes the sort order in the same save. Sessions take an in-world start day and a span in hours or days (under 24 hours stays on the start day, plus "ran past midnight") through the ordinary session update, GM only; the session, the Sessions list and the campaign home carry the formatted range, the moons' phases on the start day, the campaign's "today", and a prefilled start for an undated session. Wiki timeline events take an in-world date at day, month or year precision (with "about"), or a named age with no year, and the Storyline's timeline read model carries each line's in-world label and sort number. The campaign export is now schema 21 and carries the calendar and every date; a version 20 file imports with no calendar. Additive migration a126ca1e0d01.
    • [bot] The approved summary's Discord post says where it was in the story. When the session has in-world dates, the post gains a line "In the story: 3–5 Mirtul, 1492 DR" (additive: extra.world_range, no contract bump). The text comes from names the GM typed into the calendar, so it is reduced to letters, digits, spaces and a few punctuation marks before it is posted.

    Changed

    • [webapp] Fewer read-only pages change anything when they are opened. Making a ready-made name set is now a POST (the GET only shows what is cached), the secrets health report no longer upgrades secrets (that happens at startup), an out-of-date ownership offer is expired by a background task rather than by looking at it, and the sheet pages no longer seed the built-in definitions on read (#1178).
    • [webapp] The last gaps in stars and wishes (#122) and downtime (#124) are
      closed.
      A GM sees the players' stars and wishes on the session's Review
      tab, named only where the player signed. The star-and-wish ask goes only to
      players marked as having attended, once any attendance is recorded. On the
      party page a player edits or removes their own waiting downtime and a GM any
      entry (and can approve it as they save it), with Undo after a removal. Downtime
      totals now count approved days only, show "Days since Session N" per
      character, and keep days still waiting apart as "(+3 waiting)". The downtime
      list's totals gain a pending_days figure, and their days no longer
      includes entries waiting for a GM.
    • [repo] Renovate moves the Playwright image and @playwright/test together, in one PR, so a bump can no longer leave the journeys on an image without the browsers its version needs.

    Fixed

    • [webapp] The page-picture "from a web address" fetch now checks every redirect (#1181). It used to check only the address it was given, then follow redirects freely, so a public address could redirect the server to a private one. Each hop is now checked again, at most five are followed, a connection is made only to the address that was checked, and anything but a public address (carrier-grade NAT, 0.0.0.0, IPv4-mapped IPv6 and the like) is refused.
    • [webapp] Switching a campaign to "Voted by the table" no longer reopens voting on sessions already approved. They keep the GM's pick and show no vote; votes already cast are never deleted. "N still deciding" on This week counts players only, like the "Are you in?" line.
    • [repo] The end-to-end journeys checks are named once each ("End-to-end journeys (desktop)"), not "(desktop) (desktop)". The job name no longer spells the matrix value, which Forgejo appends itself (#1203).
    • [webapp] The hourly import sweep no longer breaks a slow "Start a new campaign from this one". It queued every waiting job as an import, which ended a clone with "uploaded file no longer on server". Jobs now carry a kind (import or clone) and a clone keeps its choices, so the sweep queues the right task. GET /api/campaigns/import/jobs/latest and the job responses say which kind a job is, so the import page can tell them apart (#125).
    • [webapp] Six screen fixes from the 4.6.0-rc1 render check. Settings → Files shows each file as a stacked card on a phone and tells files on one page apart by size and date; the party page's Downtime and "What we carry" stack on a phone; the clone dialog keeps its buttons together in a footer that stays in view; the calendar date picker shows just the year when the era has no label; the review tab no longer treats "no summarisation run yet" as an error.
    • [repo] A prerelease's image job no longer reports a failure after pushing every image. The :latest push was skipped with [ … ] && …, which made the step exit 1 for every prerelease.
    • [webapp] This week no longer counts the GM in "have answered". Under "Are you in?" it now reads "4 of 5 players have answered", "No players have answered yet." when only the GM has, and nothing at a table with no players, the same as the session page. Both pages print one shared line, so they cannot drift apart again. (#1202)

    Security

    • [webapp] The check on addresses the server fetches pictures from now refuses IPv6 forms that hide a private IPv4 address (#1181). NAT64 (64:ff9b::/96, 64:ff9b:1::/48), 6to4 (2002::/16) and Teredo (2001::/32) are blocked outright, and the IPv4 address inside each, and inside an IPv4-mapped one, is checked as well. 64:ff9b::7f00:1 used to pass. The same check serves the page picture's "from a web address" fetch.

    • [webapp] The server no longer decodes a compressed answer when it fetches a picture from a web address (#1181). It asks for the file uncompressed, refuses an answer that says it is compressed (a few KB of gzip could unpack to hundreds of MB before the size limit was checked), and counts the limit on the bytes that arrive. Multicast, site-local and IPv4-compatible addresses are refused as well, and the fetch ignores proxy settings in the environment.

    • [bot] multidict moves to 6.9.1, out of two advisories (PYSEC-2026-4244, CVE-2026-104874) that the daily dependency audit flagged on 15 and 16 Oct.

    Downloads
  • v4.6.0-rc1 5008efd646

    v4.6.0-rc1
    Some checks failed
    CI / Bot/backend version sync (push) Successful in 28s
    CI / Which journeys does this change need? (push) Successful in 16s
    CI / Does this change touch the recording path? (push) Successful in 17s
    CI / Docker image build (push) Successful in 19s
    CI / Backend lint (ruff) (push) Successful in 38s
    Release / Create Forgejo release (push) Successful in 15s
    CI / Feedback site tests (push) Successful in 56s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m10s
    Release / Build and push versioned images (push) Failing after 1m15s
    CI / Bot tests and audit (push) Successful in 2m15s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 9m30s
    CI / End-to-end journeys (phone) (push) Successful in 14m32s
    CI / End-to-end journeys (keyboard) (push) Successful in 15m24s
    CI / End-to-end journeys (desktop) (push) Successful in 15m29s
    CI / Frontend (shard 2/2) (push) Successful in 20m23s
    CI / Frontend (shard 1/2) (push) Successful in 22m40s
    CI / Backend migration, tests, and audit (push) Successful in 1h5m33s
    Pre-release

    claude-bot released this 2026-10-17 17:48:48 +00:00 | 392 commits to main since this release

    Prerelease of 4.6.0 for testing on the dev server; not for production.

    Added

    • [webapp] The screens for bringing a picture in, and for a campaign's files (#1181). The wiki editor offers "Bring it in" for a picture written as an outside address (and from the picture button, by address), rewriting the text to the stored copy, with the server's plain reason when a fetch is refused. Wiki health lists "Pictures still on other sites" with a "Bring it in" and an Undo. Settings → Wiki has "Who can add pictures and files", and the editor hides the picture controls the server would refuse. Settings → Files lists a campaign's stored files as a grid or a table, with previews, filters, the source address, files nothing uses, a viewer with zoom, and Remove (showing the text that would break). Admin → Files lists them across campaigns by size, and Preview and Open are separate, recorded steps; the Privacy page says so.
    • [webapp] Bring a picture in from a web address, and a Files page for each campaign and for admins (#1181). Anyone who can edit a page can have the server download a picture once and store it as an upload (JPEG, PNG, WebP or GIF, metadata stripped, never SVG); the source address and time are kept on the file and shown to GMs and admins only. Downloads count toward the campaign's storage, and are limited to 30 a person and 60 a campaign in ten minutes. A new campaign setting, "Who can add pictures and files" (everyone who can edit, or GMs only), is enforced on every upload route and on bring-it-in. Wiki health gains "Pictures still on other sites", with a "Bring it in" per page that rewrites the text as the GM's own edit. Campaign → Settings → Files lists every stored picture, map image, handout and attachment with previews, who added it, its address and whether anything still uses it, and removes them; orphans are listed apart. Admin → Files lists the same across campaigns by size, list only: opening or previewing another campaign's file is a deliberate act and is written to the audit log.
    • [webapp] A GM can change who was involved when rewording an event. Reword can now also set an event's people (a character, a member, or an NPC page; or nobody, for scenery or a narrated happening). The change is saved with the reword, so it is recorded like one (who and when), survives a regenerate, and the next summary is composed from the corrected people. The event log and the summary's anchors show the corrected people, and the GM still sees who the model named. A GM-only search lists the campaign's characters and members first, then NPC pages.
    • [webapp] The line of the night, on screen. Settings, General has "Line of the night: Chosen by the GM / Voted by the table". On a session's Review tab the GM marks, moves or clears the line of the night on an approved highlight, sees when voting closes, can close it now, and picks after a tie or a vote nobody cast. Players read the line of the night first with a small label, and while voting is open vote for a line (changing or withdrawing the vote), with counts hidden until it closes. The campaign Highlights page marks the lines of the night and can show only those.
    • [webapp] The line of the night. A session can have one highlight marked as its line of the night. A campaign setting (Settings, General) says who picks it: the GM (the default), or the table by vote. In GM mode the GM marks, moves or clears one approved highlight. In vote mode every member, GMs included, votes once and can change the vote; voting opens when the GM approves the summary and closes three days later, or when the GM closes it early. Counts stay hidden until it closes, then the most votes wins, and a tie or no votes falls to the GM. The approved-summary post in Discord names a chosen quote as the line of the night. Exports carry the setting and each session's line of the night; imports accept files without them.
    • [webapp] The Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 starter packs can be fetched from Admin → Content packs. Each release carries them as assets and the fetch address follows the running version, so a release candidate uses its own release. The reviewed SOURCES.md for each pack is in docs/content-packs/4.6.0/. (#1200)
    • [webapp] Start a new campaign from an existing one, and save a campaign's
      setup as a file (#125).
      A GM of the source campaign (an archived one is
      fine) can copy its settings, game system, calendar, wiki page templates and
      reputation levels, and the wiki pages and maps they tick, into a new campaign
      they own, as a background job like an import. The world starts ticked
      (places, organisations, deities, creatures, artifacts, concepts, historical
      events, documents, NPCs); player characters, quests and pages made from a
      session start unticked. GM-only text stays GM-only, personal notes are never
      copied, links to pages left behind become plain words, everything that points
      at a session is dropped (timeline events keep their in-world dates) and quests
      come back not started. The campaign's own content packs are copied only with
      the current affirmation, and the table's lines and veils only with "Invite the
      same people", which sends each other member an ordinary single-use invitation
      (a GM of the old campaign is invited as a GM); nobody is added directly. A
      setup file is a campaign file with "setup_only": true: "Start from a file"
      is the ordinary import. New routes: GET /api/campaigns/{id}/setup,
      POST /api/campaigns/{id}/setup-file, POST /api/campaigns/{id}/clone.
      One audit entry is written on each campaign.
    • [webapp] "A one-shot" accepts one_shot: true, and is stripped back
      further (#125).
      Its single reminder is now the day before (it was two
      hours), the "who hasn't answered" nudge is off, and once its session has been
      played the GM gets one "Needs you" suggestion to archive it
      (archive_one_shot), answered with
      POST /api/campaigns/{id}/archive-suggestion/dismiss and never repeated.
    • [webapp] "Invite the same people" now sends each person their invitation. Starting a campaign from another with that ticked puts " invited you to ." in each other member's bell, and emails it where their settings say so, with a link to join. Nobody is added until they accept, and a used, expired or withdrawn invitation says so instead of linking (#125).
    • [webapp] The screens for starting a campaign from another (#125). Settings → Archive has "Start a new campaign from this one…" and "Save the setup as a file…", one dialog for both: pick the wiki pages (the world starts ticked), maps, Lines & Veils, the campaign's own packs (after affirming them again), and whether to invite the same people. New campaign has "Start from a file" beside "Import a campaign". Needs you asks "Archive it?" for a played one-shot, the bell shows an invitation to a campaign with its join link, and an invitation link says when it was for a GM.
    • [repo] The in-world calendar engine (#126, step 1; nothing user-facing yet). A calendar definition format (months, festival days and leap days with Gregorian-style rules, an optional "outside the week" flag for festival and leap days, week, moons, era; every entry has a stable id), validated by app/schemas/world_calendar.py; pure arithmetic in app/services/world_calendar.py and, identically, webapp/frontend/src/calendar/engine.js (sort numbers, weekdays, moon phases, session ranges, formatting, and the check and date remapping for a calendar change). One shared file of test cases is run by pytest and vitest; scripts/calendar_cases.py regenerates it from brute-force enumeration (--check verifies it). Four generic presets.
    • [webapp] Date the story: the in-world calendar's screens (#126). Settings gains a Calendar section for GMs: start from one of four generic shapes, copy another campaign's calendar or load a file, then edit the months, festival and leap days (placed "after" a month, with leap rules and an "outside the week" tick), the week, the moons, the era and how a date reads, with a live preview and a check before saving that lists what a change would refuse or how many dates it would move. One date picker serves everything: a month grid with a column for each day of the week, festival days as their own cells, years before year 1 ("200 before DR"), the moons' phases as text, and the arrow, Home, End and Page keys; on the wiki it also offers Day, Month or Year with "About". A GM dates a session from Manage ("In the story…": the day it started, "The next day", hours or days covered, "Ran past midnight"), and the review offers one quiet row, "Where in the story?", with one press to save. Every member sees "In the story: 3–5 Mirtul, 1492 DR" under a dated session's head and on the Sessions list, and the campaign home says where the story has got to. A wiki timeline event can carry a date or a named age ("the Age of Ash", placed by choosing what it comes after), and the Storyline can show the timeline "As we played" or "In the world", with "9 days later" between sessions.
    • [webapp] The in-world calendar's server side (#126; the screens follow). A campaign can have one calendar (GET/PUT/DELETE /api/campaigns/{id}/calendar: every member reads it, only a GM writes), copied in from one of four generic presets (GET /api/calendar-presets), from another campaign the caller GMs, or from a JSON file (/calendar/file). A change is checked first (POST /calendar/check): removing a month or day that a session or wiki event is dated on is refused with the list of what uses it, and every other change moves the stored dates with their entries and recomputes the sort order in the same save. Sessions take an in-world start day and a span in hours or days (under 24 hours stays on the start day, plus "ran past midnight") through the ordinary session update, GM only; the session, the Sessions list and the campaign home carry the formatted range, the moons' phases on the start day, the campaign's "today", and a prefilled start for an undated session. Wiki timeline events take an in-world date at day, month or year precision (with "about"), or a named age with no year, and the Storyline's timeline read model carries each line's in-world label and sort number. The campaign export is now schema 21 and carries the calendar and every date; a version 20 file imports with no calendar. Additive migration a126ca1e0d01.
    • [bot] The approved summary's Discord post says where it was in the story. When the session has in-world dates, the post gains a line "In the story: 3–5 Mirtul, 1492 DR" (additive: extra.world_range, no contract bump). The text comes from names the GM typed into the calendar, so it is reduced to letters, digits, spaces and a few punctuation marks before it is posted.

    Changed

    • [webapp] Fewer read-only pages change anything when they are opened. Making a ready-made name set is now a POST (the GET only shows what is cached), the secrets health report no longer upgrades secrets (that happens at startup), an out-of-date ownership offer is expired by a background task rather than by looking at it, and the sheet pages no longer seed the built-in definitions on read (#1178).
    • [webapp] The last gaps in stars and wishes (#122) and downtime (#124) are
      closed.
      A GM sees the players' stars and wishes on the session's Review
      tab, named only where the player signed. The star-and-wish ask goes only to
      players marked as having attended, once any attendance is recorded. On the
      party page a player edits or removes their own waiting downtime and a GM any
      entry (and can approve it as they save it), with Undo after a removal. Downtime
      totals now count approved days only, show "Days since Session N" per
      character, and keep days still waiting apart as "(+3 waiting)". The downtime
      list's totals gain a pending_days figure, and their days no longer
      includes entries waiting for a GM.
    • [repo] Renovate moves the Playwright image and @playwright/test together, in one PR, so a bump can no longer leave the journeys on an image without the browsers its version needs.

    Fixed

    • [webapp] The page-picture "from a web address" fetch now checks every redirect (#1181). It used to check only the address it was given, then follow redirects freely, so a public address could redirect the server to a private one. Each hop is now checked again, at most five are followed, a connection is made only to the address that was checked, and anything but a public address (carrier-grade NAT, 0.0.0.0, IPv4-mapped IPv6 and the like) is refused.
    • [repo] The end-to-end journeys checks are named once each ("End-to-end journeys (desktop)"), not "(desktop) (desktop)". The job name no longer spells the matrix value, which Forgejo appends itself (#1203).
    • [webapp] The hourly import sweep no longer breaks a slow "Start a new campaign from this one". It queued every waiting job as an import, which ended a clone with "uploaded file no longer on server". Jobs now carry a kind (import or clone) and a clone keeps its choices, so the sweep queues the right task. GET /api/campaigns/import/jobs/latest and the job responses say which kind a job is, so the import page can tell them apart (#125).
    • [webapp] This week no longer counts the GM in "have answered". Under "Are you in?" it now reads "4 of 5 players have answered", "No players have answered yet." when only the GM has, and nothing at a table with no players, the same as the session page. Both pages print one shared line, so they cannot drift apart again. (#1202)

    Security

    • [webapp] The check on addresses the server fetches pictures from now refuses IPv6 forms that hide a private IPv4 address (#1181). NAT64 (64:ff9b::/96, 64:ff9b:1::/48), 6to4 (2002::/16) and Teredo (2001::/32) are blocked outright, and the IPv4 address inside each, and inside an IPv4-mapped one, is checked as well. 64:ff9b::7f00:1 used to pass. The same check serves the page picture's "from a web address" fetch.

    • [webapp] The server no longer decodes a compressed answer when it fetches a picture from a web address (#1181). It asks for the file uncompressed, refuses an answer that says it is compressed (a few KB of gzip could unpack to hundreds of MB before the size limit was checked), and counts the limit on the bytes that arrive. Multicast, site-local and IPv4-compatible addresses are refused as well, and the fetch ignores proxy settings in the environment.

    • [bot] multidict moves to 6.9.1, out of two advisories (PYSEC-2026-4244, CVE-2026-104874) that the daily dependency audit flagged on 15 and 16 Oct.

    Downloads
  • v4.5.3 5819eac7f0

    v4.5.3
    All checks were successful
    CI / Backend lint (ruff) (push) Successful in 36s
    CI / Bot/backend version sync (push) Successful in 27s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m6s
    Release / Create Forgejo release (push) Successful in 17s
    CI / Does this change touch the recording path? (push) Successful in 25s
    CI / Which journeys does this change need? (push) Successful in 24s
    CI / Feedback site tests (push) Successful in 1m30s
    CI / Bot tests and audit (push) Successful in 3m46s
    CI / Docker image build (push) Successful in 5m3s
    Release / Build and push versioned images (push) Successful in 5m43s
    CI / Frontend (shard 2/2) (push) Successful in 7m53s
    CI / Frontend (shard 1/2) (push) Successful in 9m22s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 8m50s
    CI / End-to-end journeys (phone) (phone) (push) Successful in 20m13s
    CI / End-to-end journeys (keyboard) (keyboard) (push) Successful in 22m5s
    CI / End-to-end journeys (desktop) (desktop) (push) Successful in 22m12s
    CI / Backend migration, tests, and audit (push) Successful in 46m49s
    Stable

    claude-bot released this 2026-10-14 21:59:22 +00:00 | 454 commits to main since this release

    A wiki fix: merging a session's page into an existing one merges again, and a
    draft says the page's GM-only text is kept. Tried on the dev server as
    4.5.3-rc1.

    Upgrading from 4.5.2

    • No database migration, and BOT_CONTRACT_VERSION is unchanged (2). Pull
      the new images and docker compose up -d.

    Changed

    • [webapp] A wiki draft says your GM-only text is kept (#1195). The draft shows the page as the table reads it and puts every GM-only block back where it was on approve, but never said so, so a GM-only note looked dropped. When the page has GM-only text, the draft now says: "Your GM-only text isn't shown here. It stays on the page when you approve."

    Fixed

    • [webapp] Merging a session's page into an existing wiki page merges again, or says it couldn't (#1195). The merge capped the AI's reply at 2,048 tokens, which a model that thinks before answering could spend entirely, and the empty answer fell back to pasting the new paragraph under the page while the draft read as merged. The reply is uncapped now, like every other wiki rewrite, and a merge draft whose AI call fails is marked failed with the page untouched, so it can be started over.
    Downloads
  • v4.5.3-rc1 d31903d9eb

    v4.5.3-rc1
    Some checks failed
    Release / Create Forgejo release (push) Successful in 16s
    CI / Does this change touch the recording path? (push) Successful in 20s
    CI / Which journeys does this change need? (push) Successful in 22s
    CI / Docker image build (push) Successful in 21s
    CI / Bot/backend version sync (push) Successful in 24s
    CI / Backend lint (ruff) (push) Successful in 33s
    CI / Feedback site tests (push) Successful in 49s
    Release / Build and push versioned images (push) Failing after 1m12s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 2m4s
    CI / Bot tests and audit (push) Successful in 3m26s
    CI / Synthetic session harness (no GPU, no LLM) (push) Successful in 11m13s
    CI / End-to-end journeys (phone) (phone) (push) Successful in 12m7s
    CI / End-to-end journeys (desktop) (desktop) (push) Successful in 13m14s
    CI / End-to-end journeys (keyboard) (keyboard) (push) Successful in 13m54s
    CI / Frontend (shard 2/2) (push) Successful in 15m9s
    CI / Frontend (shard 1/2) (push) Successful in 17m21s
    CI / Backend migration, tests, and audit (push) Has been cancelled
    Pre-release

    claude-bot released this 2026-10-14 21:25:16 +00:00 | 456 commits to main since this release

    Prerelease of 4.5.3 for testing on the dev server; not for production.

    Changed

    • [webapp] A wiki draft says your GM-only text is kept (#1195). The draft shows the page as the table reads it and puts every GM-only block back where it was on approve, but never said so, so a GM-only note looked dropped. When the page has GM-only text, the draft now says: "Your GM-only text isn't shown here. It stays on the page when you approve."

    Fixed

    • [webapp] Merging a session's page into an existing wiki page merges again, or says it couldn't (#1195). The merge capped the AI's reply at 2,048 tokens, which a model that thinks before answering could spend entirely, and the empty answer fell back to pasting the new paragraph under the page while the draft read as merged. The reply is uncapped now, like every other wiki rewrite, and a merge draft whose AI call fails is marked failed with the page untouched, so it can be started over.
    Downloads