-
v4.7.0-rc2
Pre-releaseAll checks were successfulCI / Which journeys does this change need? (push) Successful in 30sCI / Does this change touch the recording path? (push) Successful in 33sCI / Bot/backend version sync (push) Successful in 50sCI / Backend lint (ruff) (push) Successful in 54sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 55sCI / Feedback site tests (push) Successful in 1m31sRelease / Create Forgejo release (push) Successful in 11sCI / Bot tests and audit (push) Successful in 2m7sRelease / Build and push versioned images (push) Successful in 3m12sCI / Docker image build (push) Successful in 7m3sCI / Frontend (shard 2/2) (push) Successful in 8m49sCI / Frontend (shard 1/2) (push) Successful in 10m7sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 9m36sCI / End-to-end journeys (desktop) (push) Successful in 13m42sCI / End-to-end journeys (phone) (push) Successful in 26m53sCI / End-to-end journeys (keyboard) (push) Successful in 29m33sCI / Backend migration, tests, and audit (push) Successful in 40m33sreleased this
2026-11-04 20:33:23 +00:00 | 112 commits to main since this releasePrerelease of 4.7.0 for testing on the dev server; not for production.
Added
- [webapp] Browse the rules: the reference content can be read through, not only searched (#1240).
The rules page lists the kinds of record a game system has (spells, creatures, feats, equipment and so on,
in the system's order, each with how many there are), and opens a kind as an A-Z list in numbered pages that
reaches every record, narrowed by level, by pack and by a search. Only what the reader may already see is
listed or counted: disabled packs, other campaigns' packs and, for players, GM-only records are left out. - [webapp] The rules library: anyone signed in can browse the rules loaded on this server, outside any campaign.
GET /api/reference/librarylists each game system that has an active server pack with records, and the existing kinds, page and entry routes, called with no campaign, give a server-wide view of server packs only (never a campaign's). GM-only records show to admins and to anyone who runs a live campaign of that game system; everyone else's counts leave them out. An admin can browse one pack, switched off or not, GM-only records included, at/api/admin/content-packs/{id}/browse/kinds,/pageand/{entry_id}. - [webapp] Browse the rules by each game system's own filters. A PF2e spell has tradition, rank, cantrip, focus and rarity; a creature size, traits and rarity; an item price (in gp), Bulk, category, traits and rarity; a feat type and class or ancestry. A 5e spell has school, class, ritual and concentration; a creature CR, type and size; an item category (rarity and attunement appear once a pack has them). Each choice shows how many records it would leave, counted with the other filters applied, and a kind can sort by level, rank, price or CR as well as A-Z. The filters are declared per system in
app/sheets/builtin/<system>/browse.json, not in code.GET /api/game-systems/{key}/reference/filters(and the admin pack's/browse/filters) returns them with their counts, and/reference/pagetakesf_<key>andsort. A newreference_entries.facetscolumn holds the filter values; the worker fills it at start after the upgrade, with no manual step (python -m app.cli refill_facetsdoes the same by hand). Migrationb1242c0d1e2f. - [webapp] Table mode's Find can browse the rules (#1243). "Browse the rules ›" in Find opens the kinds and a kind's list inside the same dialog; choosing a row opens it in the table's preview, where it can be added to the shelf. "‹ Back to the search" and Escape return to the search as it was.
- [webapp] Prep's Rules & bestiary drawer can browse. A Search | Browse switch shows the campaign's kinds with counts, then a kind as a filtered, paged list; each row previews and inserts like a search result. Creatures open at the party's range (PF2e level -2 to +3, 5e CR a quarter of the level to level + 2), taken from the active player characters' sheet levels. (#1243)
- [bot] The "Played" post in the campaign's Discord channel now asks the
players for one star and one wish (#1255). When a session completes and
Quest Board is asking anyone about it, the post the bot already makes gains a
line asking for a star and a wish, saying when the ask closes, and its link
goes to Home, where they are answered. It is one line in a post that goes out
anyway, once: the channel is asked, not each player, and nothing brings it
back. Nothing about the session's content reaches Discord. - [webapp] The Names drawer in Prep draws names from a built-in generator instead of asking an AI. Forty-four styles (the peoples, places by size and kind, groups, ships, items, books, battles and festivals), ten names at a time, free and instant, in a campaign with no AI as well. A campaign is never offered the same name twice, and never a name its wiki already holds; a style that names a place can use one you give it. Describe, the one line under a name, still asks the campaign's AI and is offered only when there is one. The ready-made name prefetch and its Redis cache are gone. (#1257)
- [webapp] A wiki page's margin is one box with suggested facts. A page starts with the facts its type usually has in the campaign's system (Pathfinder 2e, D&D 5e or Generic), empty and never shown until filled; where Quest Board already knows a value (First met and Last seen from the sessions, a character's player and class from the campaign and the sheet, a place's region from its map, a creature's level from the rules entry it is based on) readers see it until a GM types their own. A page can show a map as its picture, with a caption, and a GM can remove a suggested fact for good. New columns
lore_entries.image_caption,.image_map_idand.facts_dismissed(one migration);GET /api/campaigns/{id}/wiki/suggested-facts; the article read gainsimage_caption,image_mapandfacts. The campaign file carries the caption, the removed facts and the page's map. - [webapp] Facts the session may have changed. After a session is summarised, the campaign's own LLM is asked, page by page, whether a status, ruler, leader, owner or allegiance changed. Each suggestion carries a quote found in the transcript, with the speaker and time of the line it came from, and waits for a GM to use it, change it or turn it down; nothing is shown to players. New table
fact_suggestions(migrationf1280a0b1c2d) and a task to run once after the upgrade for sessions already processed:celery -A app.tasks.celery_app call app.tasks.fact_tasks.suggest_fact_changes_backfill. - [webapp] Two of the same creature can be on a session's shelf (#1321). Adding a rules entry that is already there with
another: truemakes a numbered copy with its own HP; a pin carries the first card forward and copies stay with their session. Cards carrycopy_number, and a rules cardcopies. - [repo] The synthetic-session harness runs leg 3 in one command, and can
send its vocabulary ashotwordsas well asinitial_prompt(#506).
python -m evals.synthetic_session summary-leggoes audio, ASR, merged
transcript, LLM summary, scores through the same summarisation and scoring
code leg 2 uses, and reports leg 3 beside leg 2 (--baseline);--provider stubruns it offline.asr-legandsummary-legtake
--vocabulary-mechanism {initial_prompt,hotwords,both}, which sets the
product's ownQB_ASR_VOCABULARY_STYLEswitch for the run without touching
the provider code. The README records the measured matrix.
Changed
- [webapp] New campaign → "The setup of one of yours" now opens the full copy dialog.
It is the same one as Settings → Archive → "Start a new campaign from this one":
choose the pages, maps, lines and veils, packs and people to take, with the
scheduling and reminder settings always copied. The old settings-only copy
onPOST /api/campaigns(template: {"from_campaign_id": …}) is removed. - [webapp] The character sheet's pickers open on what fits the character.
GET /api/campaigns/{cid}/sheets/{sid}/pickerand.../optionsnow answer
fits(the browse filters that match the character at that step, declared as
fitsrules in each system'sbrowse.json),fits_label,filters,sorts
andtotal, and take stage 3'sf_<key>,sort,page,per_pageand
all=1; with any of those the "belongs to this character" narrowing is
skipped..../optionsnow answers{results, …}instead of a bare list. - [repo] PostgreSQL moves from 16 to 18, and an existing install must run
make pg-upgradebefore it starts the new release. The 18 image keeps its
data in a new place, so the database lives in a new volume
(postgres18_data) and the old one cannot simply be reused: pointing 18 at
it would start an empty database.make pg-upgrade(from the new release's
checkout, beforedocker compose up -d) backs up, dumps the old database
with its own PostgreSQL 16 tools, restores it into the new volume, and checks
every table's row count and the migration revision. It never removes the old
volume, which stays as the rollback and archive; the pre-upgrade dump is
written to./backups. A newpg-upgrade-guardservice stops the stack, with
a message namingmake pg-upgrade, if the old volume holds a database and the
new one is empty, so a plaindocker compose up -dcannot give you an empty
database. A fresh install and an already-upgraded install start as usual. The
backend image'spg_dumpclient moves to 18 with it, somake migrate-safe
refuses to back up against a 16 server until the upgrade is done. Renovate
now sees thePG_MAJORin theDockerfileand moves every PostgreSQL pin in
one pull request. Seedocs/OPERATIONS.md, "Upgrading PostgreSQL 16 to 18"
(#1245, supersedes #627). - [webapp] Campaign names reach the transcriber as hotwords by default. On the bundled WhisperX server, the campaign's vocabulary list now goes in
hotwordsrather thaninitial_promptwhen the server declares support; on a 100-name list this got 89–90% of names right against 80–82% (#506). A server without hotwords gets the prompt, as before, quietly. SetQB_ASR_VOCABULARY_STYLE=initial_promptto keep the old behaviour. - [webapp] [bot] "GM only" is now "GMs only" everywhere it is shown. The
wiki's front page and recent changes, the campaign search, a hidden page's
screen-reader label, the table's Find, character sheets, Prep's lookups,
the copy dialog, content-pack visibility and the bot's/helpall say it
the way the map and the rules already did (owner's decision, 30 Oct).
Fixed
- [webapp] Two reads no longer write. The character-creations list
("Carry on with ...") only hides creations left untouched for 30 days instead
of deleting them while it lists, and a page's picture stored publicly before
#894 is served where it is instead of being moved on read. A daily
background task now deletes the stale creations, and another moves the old
pictures into the private store; both are safe to repeat. The only
remaining GETs that write are the two sign-in callbacks and the content-pack
exports (#1206). - [webapp] Fetching a starter content pack no longer freezes the server, and a busy server no longer signs you out. Reading and normalising a pack ran on the web server's event loop, so fetching two packs at once could miss the health check and get both workers restarted; the work now runs in a thread. The sign-in check also treats only a 401 as "signed out": an error or dropped connection from
/api/mekeeps you waiting and retries instead of showing the sign-in page. (#1239) - [webapp] Admin → Content packs reads properly. A pack's contents say "9 ancestries", not "ancestrys"; its version no longer gains a stray "v" ("pf2e-8.6.1", not "vpf2e-8.6.1"); the licence's terms link is underlined, not told apart by colour alone; and on a phone the app bar's home link is named by the "QB" it shows (#1302).
- [webapp] A moment without Redis now answers "try again" (503), not a server error (#1326). Found by the live checks (#1246): a Redis restart turned signed-in requests into 500s for a fraction of a second. Nobody was signed out then, and nobody is now. The manual healthcheck checklist in OPERATIONS.md also had a worker step that couldn't work; it now stops the process from the host.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- [webapp] Browse the rules: the reference content can be read through, not only searched (#1240).