Changelog
All shipped releases for purple-cat-games/platform. Each entry links to the full per-version notes beside it. The in-progress release (whatever's on the version/x.y.z branch) lives inline at the top until it ships, then moves into its own file.
v0.146.0: Misc enhancements and the phone's Mini-Me bar (in progress)
Renumbered from v0.139.0 on 2026-11-03, when main reached v0.144.0 (reserved in the inbox as versions/0.146.0).
The TODOs in TODO.md ("Misc Enhancements", "Mobile", "Mobile Mini-Me"). Built as v0.136.0 on version/0.136.0; renumbered when it was merged with main, after v0.137.0 shipped and v0.138.0 went to Procedural maps (PR #130).
- [x] The footer no longer shows the version and build time. They are in Help > About, where someone looking for which build they are on goes. The footer keeps the copyright, the legal links and Help.
- [x] Notifications are in the account menu, and the count is on your avatar. The account menu at the foot of the left rail (Edit Profile, Log Out) has a Notifications submenu between them: hover it for your latest three, click it for /notifications. The avatar wears a red badge with the unread count, and still shakes and glows when one arrives. The footer's Notifications link is gone. On a phone, the menu has a Notifications row with the count, and the Menu button shows it too. A new notification is still announced on every page: the listener that did it moved out of the footer into the app shell.
- [x] Drawing Mode says so. While the Drawing palette is open, a mode banner at the top of the map view (where Measure, Targeting and Delete Mode show theirs) names the tool and how to use it — the same lines as the hint beside the cursor — with Cancel Drawing, which leaves Drawing Mode and drops any shape half drawn. It works on a phone too. The Cancel Drawing button that stood beside End Turn is gone. The wall tool's banner is the same banner: it still explains click-per-corner and right-click to finish, keeps its Done (leave the wall tool), and adds Cancel Drawing. (
WallDrawingBannerbecameDrawingModeBanner.) - [x] On a phone, the Actions and Palette sheets open over the map's tools. The drawing, fog and vision palettes along the bottom of the map, and the snap bar, were drawn over the sheets; the sheets now sit above them (a new z band,
MOBILE_SHEET_OVER_RING, between the drawing toolbar and its popovers; the deadMOBILE_TOP_NAVband went, so the count holds). - [x] On a phone, a mode's banner is a strip across the top of the map. Measure, Targeting, Delete, Drawing, Heal/Harm, Impersonate, Place and Pick are a full-width strip with no margin, starting where the map's name (the nub) started; the nub moves down to sit under it, and so do the snap bar, the chat bubbles and the installed app's back button. Each strip is one line — the mode's name and its main button — with an expando (⌄) at the end that shows the instructions and any other controls. (The strip is the inner ring's top edge,
useRingSlot('mode-indicators', 'top'), so whatever stands below the ring moves with it; banners mark their namedata-mode-titleand the restdata-mode-detail.) The whole strip takes taps, so a tap on it never reaches the map beneath — Drawing Mode's banner passes its own through, which on the first build left its expando dead. - [x] The phone's Mini-Me bar. While you play a token on a phone, a second bar stands on the bottom bar, in its colours and at its height: the token's avatar; its name (12 characters, then …) with up to two of its bars under it, labelled inside ("HP: 35/50"); its conditions and effects as one glyph each, as many as fit; then Actions, Expand and ×. Tap the glyphs for a sheet listing every condition, timed effect and buff by name, with Mini-Me's Remove, Edit and Add. Actions opens the actions sheet, now in tabs by the same groups as desktop Mini-Me's menus (Ability Scores, Attacks, Powers for System 8; one list when the system has no groups), with the same rows and ⋯ menus. Expand opens the character sheet, growing up out of the bar; its back button (now in the browser too, not only the installed app) collapses it back down. × stops playing the token. The sheets, toasts and the map's bottom edge stand above both bars while it shows.
- [x] The phone's bottom bar on a map: Menu, Map, Palette, Chat, More. Me, its Change and Stop, and Actions are gone from it — the Mini-Me bar has them. Map always shows the map, closing whatever is over it. More (⋯, furthest right) opens the left sidebar over the whole screen from the bottom: its panels as icons across the top (Me, System, Library, Edit Map, Edit Campaign, Wiki, the Initiative Tracker, Edit Object while inside an Object, then the modules' panels), the chosen one beneath with its own title row, or the list of panels, each with its icon and what it holds, when none is chosen (the desktop list hides its icons until hover, and a phone cannot hover). Choosing a character to play when you have none is More → Me. The phone help page and Core Concepts describe the new bar.
Phone play shell, round 2
From checking the phone layout on a device.
- [x] The bar's sheets stand on the Mini-Me bar, over everything on the map. Chat, More, Menu, Me and the bottom sheets start above the Mini-Me bar instead of covering it, so it shows whenever you play a token on /play (Me now grows out of its top edge, and its Expand closes Me again); and they cover the whole inner ring (the palettes, the snap bar, the mode strip), which leaked through. The takeovers and sheets moved up the z scale, above the bars they stop at and the map's overlays but below every popup band, so nothing opened inside one is ever beneath it; on a phone the inner ring and the mode strip stand below them (
innerRingZ). Two bands went (MOBILE_NAV_DRAWER,MOBILE_SHEET_OVER_RING). - [x] The bar works as tabs: Menu, Map, Chat, More. Equal segments with no rules between them, icon and label on each, the open one marked by a soft pill behind its icon and a brighter label (Map only while nothing covers it). Menu is a takeover from the bottom like Chat and More (
MenuTab,tab: 'menu';MobileNavDrawerwent), with the same back button and no shadow; on a map it also holds the Chat bubble debug switch. Switching between takeovers keeps the sheet up and fades its contents in (the store'spreviousTab); tapping the open one again, tapping Map, or swiping the sheet down by its header (a grip at its top) closes it, and it leaves the way it came — down, or (Me) back into the Mini-Me bar (useLeavingScreen). Takeovers and sheets slide from the bars' top edge, not over the bars. - [x] On a phone the palette is pinned to the map's upper right, collapsed. The Palette button and its sheet are gone. The palette is the desktop's ring strip on the right edge instead of the left (clear of the installed app's back button), with the same collapse button when open, starting below the mode strip as the nub does (
RingStrip side="right"). The snap bar's row moves to the left edge, below the drawing tools (rowEdgeFor); the drawing, fog and vision tools no longer start under the mode strip, and their properties are always the bar at the bottom on a phone. The nub is never wider than the screen less twice the wider of the ring's sides. The layer is changed in the nub, as on the desktop: the sheet's list of layers (VerticalPalette) went with it; its Chat bubble debug switch moved to the Menu. - [x] Help is a sidebar tab. On the desktop's left sidebar and in the phone's More, a Help tab shows the help the footer's Help opens on a map.
- [x] Mini-Me bar: End Turn, the sheet icon, and taps. End Turn is part of the bar on your turn, flush with its right edge, in the map End Turn's colours and the bar's height, square, with the other buttons moved left for it; the floating button (
MapEndTurnButton, the phone's alone) is gone. Open Character Sheet uses desktop Mini-Me's sheet icon without the caret; tapping the avatar, the name or the bars opens the sheet too. An open sheet publishes its height (--vtt-mobile-sheet-height), and the toasts stand above it — the conditions sheet and "Your turn!" collided. - [x] Installed, the map runs up behind the status bar. The band past the Dynamic Island is gone: the map starts at the top of the screen, under the status bar. The fade there is iOS's own — an installed app with a translucent status bar gets it over whatever is beneath, and a page cannot turn it off — so the nub, the mode strip and the ring's controls still start below it (
--vtt-standalone-top-inset, unchanged); it now falls on the map instead of on a band. (StandaloneTopBandonly publishes the inset.) - [x] On a phone the footer's Help goes to /help. It opened the help in a sidebar the phone does not have, leaving the campaign page showing but dead.
Unreleased: GCP M3b, prod's move to Google Cloud
M3a (v0.160.0) put production's stack and deploy on main. M3b is the move: a full rehearsal with a copy of prod's data, then one announced window of about an hour, at the same addresses. Rolling back is switching DNS back, for 2–4 weeks. Details and TODOs: docs/programs/gcp/m3-prod-on-gcp.md.
v0.132.1: Hotfix, v0.132.0's CI run (2026-10-27)
v0.132.0's CI run on main was red, so it never deployed. Two failures, neither in what v0.132.0 changed for players:
- [x] The service worker's API-path guard follows the dev proxy.
apps/web/src/sw/apiNeverCached.guard.test.tsread the proxy table out ofvite.config.ts's source, and v0.132.0 moved it intodevProxy()invite-dev-server.ts(the second dev stack). The guard now callsdevProxy()and checks its paths againstAPI_PATHS, and checks thatvite.config.tsstill proxies through it. - [x] The procedural-map e2e waits for the scene, not the clock. On a slow runner
e2e/mapgenRender.spec.ts's R3.2 test read the room labels 2 s after the canvas was created and saw one of three, and the M2 contour gate read a forest before its trees were drawn. The labels test now waits for the number of labels each scene should draw; the pixel-reading scenes are read once two frames a second apart agree; the tests that load three or four pages get longer timeouts.
v0.132.0: Playtest fixes (2026-10-26)
- [x] The Timeline opens wider. The default zoom is doubled: a round is 480 px across instead of 240 (16 px per tick), so a turn's ticks read at a glance. Pinch and wheel zoom still reach the same limits.
- [x] No more red overlays for Dying and Dead. The old token overlays (a red bottom half with a downward arrow for Dying, a red tint with an X for Dead) are removed. The condition badges drawn under the token since v0.120.0 already show both conditions, so a dying token was marked twice.
- [x] The top-bar breadcrumb is centred in the room it has. On /play the breadcrumb (the former nub) sat off to the right, because the bar kept space for the left sidebar's tabs even while they were hidden. Each side of the bar now takes room for its tabs only while that sidebar is open, so the breadcrumb is centred between what the bar really shows, and slides right as the left sidebar opens (and back as it closes).
- [x] A second dev stack on one machine. Vite's dev server takes its port from
PCAT_DEV_PORT(else 5173) and proxies the API atPCAT_DEV_API_PORT(else 3001), and refuses to start on a port someone else holds instead of moving to the next one. With the API's ownPORTandapps/help'sHELP_PORT, two programs run side by side on ariadne (planning PH.23:programs/hosts/<program>.jsonnames each program's ports andsetup-program.shpasses them). Nothing changes for a single checkout.
Near Term Priorities
- [ ] AGENTS.md gates to prevent changes that would invalidate our stated policies
- [ ] A better way to track near term priorities (in general, it's time to move beyond CHANGELOG.md). Quick thoughts:
- Problems:
- Can't always safely edit docs in this repo; it might be in the middle of a change, an experimental branch, etc.
- Individual docs are readable but no high-level view. Could make docs to track higher-level stuff, but that's duplication of data and it drifts out of sync fast (case in point: the various near-term roadmaps)
- Potential Solutions:
- Move docs/help content that doesn't reflect the current reality to a new repo. Help docs describing the shipped platform should remain tightly bound to the the platform, but roadmap items and project planning should be decoupled.
- Build our own lightweight PM tool. In the modern vibecoding landscape, that may actually be easier than adapting to someone else's. (Although Github Projects is worth looking at, since Claude can use gh cli to manage it)
- Problems:
Released
Newest first. Click a version for the full notes.
-
v0.162.0 — .cat archives M4: archives of any size — an archive past the request limit uploads straight to storage with a progress bar and imports from there; a big export runs in the background and arrives as a notification with a day-long download link; files you already hold are linked, not uploaded again; imported things show in the Library, with Also add to my Library; a map archive carries its doors' Object types and conditions; the old map ZIP import is gone; migration 0086.
-
v0.160.0 — Prod on Google Cloud: the preparation — the Google Cloud program's M3a: nothing changes for players; production's stack, its deploy (off until the move, by
PROD_HOST), help served from Google Cloud, and bug reports there, ready on main for the rehearsal and the move. -
v0.159.0 — Issues B2: Report a problem, one AI key per request, and Remove Background in production — Report a problem from Help → About and the map menu, with an optional picture of the map, and a private queue on GitHub for self-hosters and production to opt into; AI requests carry only the key they use (#121); server-side Remove Background works in production (#143); report limits are per person (#145).
-
v0.158.0 — Misc enhancements: the 5e sheet on the character page, and the conversation list — a D&D 5e character's page is the 5e sheet, with its portrait on page 1, its validation issues marked on each panel and listed beside Build; the conversation list wears avatars, iMessage-style, with dividers between sections and a compose icon; New Retro's headings are Fraunces; on the desktop the palette runs down the map's right edge and the footer shows the version; Bert's avatar is a photo of the real Bert.
-
v0.157.0 — Nonprod on Google Cloud — the Google Cloud program's M2:
nextand alpha run on Google Cloud, deployed and applied from CI, and alpha moved there with its real data; on every deployment, a file over 30 MiB goes straight from the browser to storage on a signed address, with the same checks and limits as before. -
v0.154.1 — Hotfix: pnpm install on a standalone pnpm —
pnpm installno longer fails withnode-gyp: command not foundon a standalone pnpm, such as on a new Mac. -
v0.154.0 — Map building: Inherited roles — a campaign's Access grid says what each role gets on its maps, tokens, characters and the rest, from presets (Player, Co-GM, Spectator), with per-player overrides in Share and a map's own Sharing; the Effective tab names the row that decided; nothing changes until a GM changes it (every grant migrated to the same answers, checked on a copy of production).
-
v0.153.0 — Map building: Testing an Object — Test on an Object's page plays it in memory with a Player token: doors, keys, pick-lock checks and GM approval, PC and GM views, Edit and back without a reload, nothing saved; on maps, players with nothing selected see what their tokens see, lights on Objects light the map (hidden ones light nothing for players), a snuffed lantern goes dark, and Move to Layer no longer makes things vanish (migration 0085).
-
v0.151.0 — Bert M2: Bert does things — Bert makes small changes you ask for, as you, marked via Bert in History and undone together; deletes and generated monsters wait for Do it, and so does anything you didn't ask for in your own words; it finds tokens across a campaign's maps; links in chat; buttons into Settings, Help, Campaigns and the Library; the chat box is no longer a form field, for LastPass; sent messages no longer show twice; AI Policy 1.5.
-
v0.148.1 — History for characters and tokens — a History button on the Impersonated Token panel, System 8's new sheet and Mini-Me's … menu; in the panels it sits in the top bar and moves under that bar's … when narrow. A linked token opens its character's history, any other its map's.
-
v0.148.0 — Images without a public bucket — the Google Cloud program's M1: on Google Cloud, uploaded images and module assets live in a private bucket and Cloud CDN serves them only to signed-in users, through a signed cookie the app keeps fresh; a
nextenvironment there atnext-gcp.purplecat.games; on every deployment, the server reads its own images from storage rather than over HTTP. Production stays on AWS, with no change players can see. -
v0.147.1 — LastPass and the chat box — the chat's message box is a text area, so LastPass no longer treats it as a password field; a long message grows the box up to four lines.
-
v0.147.0 — Procedural maps: Revealing by sight — fog of war uncovers what a party token sees (its sight, the walls, secret doors included, and the light), worked out by the server from a token id so players can't reveal what they couldn't see; Players may reveal by sight and Automatically in the map's Fog of War section, with Reveal what (token) sees in the token menu; Auto-Reveal on Move carries over (migration 0084); Fog Edit restores fog with Shift, with a cursor and a hint per tool; the render e2e waits for the art.
-
v0.144.0 — .cat archives M2 and M3: a backup that loses nothing, and import — a campaign archive now carries everything a campaign owns (drawings, walls, fog, layers, Objects and their types, generated maps, every definition, roll tables, factions, parties, roles, the Treasury and the wiki), with ids kept so rows refer to each other and a guard that no table is forgotten; and import: a
.catdropped on the new Import page, opened from Files or shared to the app goes where it belongs, a campaign archive becomes a new campaign of yours, a map archive goes into a campaign you can edit, every file re-uploaded and every id minted fresh, nothing fetched from the web; the black-hole round trip on every build; the old map ZIP on its last release. -
v0.143.0 — Bert M1: Ask Bert — Bert, an assistant under Direct messages for every user: answers how-to questions from the help wiki with the page linked, and reads your campaigns, maps, tokens, characters, conditions, chat and Library as you, through the same routes and permissions; Show me buttons select tokens or open a map or character; a state line as it works; an AI icon in the footer opens it from anywhere; on your own provider key (Settings → AI → Assistant); AI Policy 1.4 and Help → Asking Bert.
-
v0.142.1 — Hotfix: chat messages are sanitised — a chat message's HTML goes through DOMPurify before it is drawn, so markup posted to the API can no longer run script in other members' browsers.
-
v0.142.0 — The Gemini model list — Settings → AI no longer falls back to Gemini 2.5 Pro, which Google refuses to new users; the fallback list offers Gemini 2.5 Flash, Gemini Flash (latest) and Gemini Pro (latest).
-
v0.137.0 — Map building: Library Objects — an Object's own page in the Library; editing an Object with no map, with undo and History; Insert into map in one step, and Remove that takes the whole copy; Make into object for a token or a picture; blueprints with the map generator's fields; its maker's hidden parts come along when they insert it; releases are tagged on main by a workflow (migration 0083).
-
v0.135.0 — Procedural maps: Towns and interiors — rooms dressed by their purpose (44 purposes; the dungeon's and crypt's too, generator version 2); seven single-floor interiors (tavern, house, shop, temple, manor, warehouse, barracks) with a purpose mix you can edit; villages and towns whose roofs come off, on their own Roofs layer, and lift over the token you play; camps, keeps and ruins with Decay; chests that open as Objects; doors and windows that read as such; rolling a roll table and writing game systems now check who you are; Module API 1.47.0.
-
v0.134.0 — Issues B1: a socket security fix, Create Map without a dialog, and more from the tracker — live events (cursors, drags, invites, transcription, dice) reach only campaigns their sender joined (#120); Create Map makes "Untitled Map N" at once and opens Edit Map with the name selected (#118); Bring all to this map says how many players it invited (#73); the nub's selects work on a phone (#113); campaign pages read over bright art (#70); and #68, #69, #71, #72, #74 closed as already fixed.
-
v0.133.0 — .cat archives M1: Export you can find and trust — Export… on the campaign page and card, the map list, the Library card and the character list, with Full quality or Smaller file; format-2 archives that carry every file their rows point at (content-hashed, at full quality, with a manifest and
cat.yml) and no link back to the server; a missing file fails loudly with Export anyway, without them; web links stay links; a guard test; character archives with skills or attributes read back again. -
v0.131.0 — Playtest fixes: a sitewide chat, Sort by everywhere, and the phone's Chat and Back buttons — the owner's 2026-10-21 playtest: Sort by on every list of maps, campaigns and characters (campaigns get created/updated times, migration 0081); unread counts kept on the server for every chat room (migration 0082), bubbles from the other rooms, All campaigns and All maps in the room list; the chat a sitewide right sidebar, with Edit Object and the Initiative Tracker on the left, Settings a window on /play and Search, Settings and the account at the foot of the rail; dropdown switchers in the nub; drawing options at the bottom with Cancel Drawing; the phone's Chat button on every page and a back button in the installed app; new icons.
-
v0.130.0 — Procedural maps: Dungeons and caverns — caverns, mines and lairs whose walls block sight and movement; dungeons and crypts with rooms in loops, doors that open and secret doors players never see; GM-only room labels and encounter slots on every template; secret doors no longer give their position away through their walls; Module API 1.46.0; and from v0.125.0's gate, clearer contour lines and a note under every setting a preset overrides.
-
v0.129.0 — Map building: Tools, layers and the nub — drawing tools that all work by clicks, a rebuilt arc and a better cone, shapes that close themselves; map layers you can fade and lock; the nub in the top bar with your layer, and its popover in sections; the main palette as a strip down the left edge; Object thumbnails in the Library; delete on all layers; Library search tolerant of typos on Postgres (migrations 0079, 0080).
-
v0.128.0 — New Retro: Retro's colours in the dark, a New Retro sign-in, and an account menu — the miscellaneous TODOs: New Retro's dark mode in the Retro skin's colours, the app's own sign-in page in the visitor's skin, and an account menu with Edit Profile and Log Out.
-
v0.125.1 — Share links from configuration; dependency security — share and invite links built from configuration, never the request's
Hostheader; the critical Dependabot alert and the high ones that reach production fixed by dependency bumps, the rest triaged with the reason. -
v0.125.0 — Procedural maps: Outdoor templates — ten more templates beside the Forest (plains, farmland, swamp, desert, tundra, jungle, mountain pass, coast, river valley, badlands), four seasons with winter snow, brooks, rivers and lakes with fords and bridges, four encounter-map presets, Library pools to use your own art for any role, and Module API 1.45.0.
-
v0.124.2 — CodeQL: the inpaint provider through a Map — The inpaint provider looked up through a Map, which closed #110: main's open code-scanning alerts are the 16 rate-limit alerts left to API rate limiting.
-
v0.124.1 — CodeQL: the last two backlog alerts — CodeQL's two leftovers after v0.122.1: the inpaint provider's call guarded beside it, and no
null(or opaque"null") origin can reach the CORS list (#89 closed). -
v0.124.0 — Procedural maps: Forest paths — the procedural map generator's first milestone: a Generate tab that previews a forest with roads from a seed and sliders that never move anything, hide/swap/nudge/bake on generated trees and rocks, and commit to plain map data the platform draws without the generator; art packs as modules, with AI-generated placeholder art (AI Policy 1.3) that is replaced before general availability; Module API 1.44.0 (sidebar tabs, map layer renderers, the map write facade, per-map module state); the v0.25.0 LLM map generator retired.
-
v0.122.1 — CodeQL security backlog — server-side request forgery guarded, typed request values, path injection closed, DOM sinks sanitized, polynomial regexes rewritten, fixed log format strings.
-
v0.122.0 — Map building: hidden means hidden, Object Edit Mode, and the Library as Assets — map building's first three milestones in one release: hidden means hidden, Objects with parts, stateful Objects and Interactions (M1); Object Edit Mode with Object layers, the nub's crumbs and the Edit Object sidebar (M2); the Library as Assets — Your Library, Campaign Libraries, the map sidebar, staged import (M6); and token writes authorized.
-
v0.121.0 — Drawing improvements — drawing cursors per tool, in the stroke and fill colours; Finish Line saves the line; Shift keeps a rectangle square; nib shapes and softness for the pencil and brush, with strokes that no longer grow stray wedges; multi-selection of drawings, every toolbar write applied to the selection, and a delete confirmation; grid-unit stroke widths up to 10 squares; CLAUDE.md separates finishing work from releasing it.
-
v0.120.0 — Chat UI improvements — chat UI improvements (channel title, clickable avatars, Combat Log and Transcript as sub-channels, token chat colours); condition glyphs and severity drawn in and under tokens (migration 0061); the Target UI (target Mini-Me, multi and area targets, T, Tab, Escape); mobile tap fixes; collapsible sidebars and palette; the roadmap moves to its own repository. Merged and pushed
-
v0.119.1 — AI Policy 1.2: say what the code does — the AI Policy corrected to what the code does (generative AI ships, the operator's key backs features without the user's, the in-browser language model generates and downloads its weights, the map generator keeps prompts); BlazeFace weights served by us; the sheet importer sends the user's own AI key; one invalid roadmap page no longer stops
pnpm dev; the chat-commands and generative-AI-removal roadmap pages. Merged and pushed -
v0.119.0 — Playtest fixes — System 8 trait buffs (Module API 1.43.0
traitBuffs, system8 0.20.0); generated-action and ability-check chat briefs; Mini-Me menus for AI-generated monsters and power grouping; Hide Grid and per-layer visibility (migration 0060); an End Turn button on the Timeline; the hover portrait above the strip; Chat Settings in Settings; Campaign Context (a top bar, campaign-scoped character links, Play resuming the last map). Merged and pushed -
v0.118.1 — v0.118.0's leftovers, and a web build that fits its heap — CI's web build and e2e rebuild get the deploy jobs' 6 GB heap (v0.118.0 died out of memory on
main); v0.118.0's notes filed and its tag placed; dnd5e 0.21.1 dropsplatformRef;scripts/secrets/retire-modules-tokens.shretires the module-checkout secrets. Pushed, CI green -
v0.118.0 — Library actions; modules in the platform — Library items get Delete (soft by default, with a permanent delete behind a 5-second cancellable countdown), Reclassify, and tags edited in place; a Bulk Edit mode on the list pages, and mass delete in a new /admin/library. The first-party modules (dnd5e, system8, scenes, system5) move into
modules/and every environment builds them from source, so nothing is published or pinned for them; vtt-assets and genai stay published. Merged and pushed (PR #79); CI failed on the web build's heap, fixed in v0.118.1 -
v0.117.0 — Buffs — one evaluator for every modified number (
set,add/percent,floor,ceiling, the module's stacking policy first, a breakdown with every result): equipment, feats, powers, conditions and timed effects all carry modifiers, linked tokens re-project with them and unlinked ones get an overlay; an effects editor, buffs on Mini-Me, the token menu and the 5e sheet; dnd5e 0.21.0 routing AC, speed, HP and ability scores through it. Merged; pushed with v0.118.0 (PR #79), whose squash contains it -
v0.116.0 — Timeline redesign — the Timeline as a strip across the bottom of the map: ticks on a canvas, 30 to a round; effect bars and token art; infinite scroll and zoom by trackpad, touch and mouse; the current turn framed in gold, turns gliding by their step; End Turn below Mini-Me, the Concentration banner off the desktop; double-click a token to impersonate it. Merged and pushed
-
v0.115.0 — Chat bubbles — the sender's colour on every bubble (yours filled, theirs edged), square avatar chits, the design's dice with the roll menu on the die, the Incoming damage tray, round separators,
api.chat.briefand actions as threads; Help and Notifications in the footer, Profile and Settings top right, Settings as two pages when narrow; Noticia Text and an ember orange for New Retro; Mini-Me's artwork and token menus. Merged and pushed -
v0.114.1 — Hotfix: what v0.114.0's squash missed — pages off the map on an installed phone clear the status bar and the Dynamic Island, the roadmap opened up past v0.114.0 (with a New Retro entry and v0.113.0's notes), and
sync-prod-to-local.shfinding the AWS region on any machine. Merged and pushed -
v0.114.0 — New Retro — a new theme in light and dark (parchment and dungeon stone; cardstock grain, hard shadows, Cinzel / Crimson Text / Courier Prime) with a System / Light / Dark setting, the chat redesign (message runs, a channel header with its description and who is connected), the Settings redesign (categories beside rows of settings, laid out by its own width), the installed phone app's band past the Dynamic Island and a slimmer footer, and Mini-Me fixes. Merged and pushed
-
v0.113.0 — Sidebar tabs — the play bar's side columns as tab strips and one context per sidebar (no context chrome under the bar; a title row whose actions fold into a … menu), Chat Settings as a tab, the Character context's actions in its row, Me as the impersonated sheet or a chooser, TimedEffect positivity, and Mini-Me — the impersonated character on the map with its bars, statuses and the module's action menus (Module API 1.41.0
actionMenus; system8 0.19.0, dnd5e 0.20.0). Merged and pushed -
v0.112.0 — PWA: update delivery — the push switch applies a waiting service-worker update before subscribing, and the phone's map shows that a new version is ready (a dot on the Menu button, Reload at the top of its drawer); PWU.0's device checks still owed. Merged; to production with v0.111.0
-
v0.111.0 — PWA: push notifications — every Message pushed to the recipient's devices when the app is open nowhere (VAPID keys per environment,
push_subscriptions, delivery fromcreateMessagewith gone endpoints cleaned up, a lock-screen-safe payload, the worker's notification and tap, one per-device switch in Settings); plus what testing found: a slow/api/resolvestranding a slug route, the people pickers looking like logins to LastPass, the Add-a-person chevron, an invite join landing on "Campaign not found", the phone's main menu on the map and its drawer landing where chosen. Merged; to production with v0.112.0 -
v0.110.0 — PWA: OS integration — a share target for images (into the Library, or the artwork of a character or token you can edit) and for
.cat/.yaml(staged for import, imported on a click), file handlers for the same throughlaunchQueue, shares and launches that survive sign-in and expire after an hour, manifest shortcuts (Resume last map, Characters, Library), the screen kept awake on a map (Settings: Auto / Always / Never, per device), and the unread count on the app icon -
v0.109.0 — PWA: foundations — the app installable per environment (a generated manifest named and tinted for each, icons, the iOS and Android head tags), a service worker in
injectManifestmode that precaches the hashed build, answers navigations network first and never touches the API (a guard holds it), the 95 MB segmentation runtime cached on first use, "a new version is ready" as a toast off the map and a play-bar badge on it, a kill switch thrown by a repository variable with its runbook, safe-area layout for standalone, Install app in the account menu, the deploys uploading the worker and manifestno-cache, and the smoke suite checking the installable app -
v0.108.1 — Hotfix: Start {name}’s wiki page (and Read more) on a character's own page opened a sidebar that only exists on
/play, so it did nothing; a character's wiki now has a full page at/characters/:ref/wiki[/<page>], which those links open off a map; ursa 0.102.0 across the repo (EW.4) -
v0.108.0 — Polish — the System 8 playtest's polish (the ability-score wizard's drag-anywhere rework, debounced score steps, proficiency points counting only declared components, Import Party skipping members it cannot load, an image dropped on the map straight to a token, the per-token render mode, chat avatars framed like their tokens) and the wiki's first two parts: foundations (roots and pages, access, history, ursa builds and publish, the coalescing queue, the local store) and in the app (the sandboxed viewer, the editor, the campaign page, the
/playsidebar, the character abstract, history), plus external ursa wikis (EW.1–EW.3) -
v0.107.2 — Hotfix: the frontend deploy builds with a 6 GB Node heap; the Sentry source maps took the Vite build past the default ~2 GB and v0.107.1's
deploy-frontendsdied out of memory -
v0.107.0 — Playtesting fixes — the playtest notebook as a release of its own: every dropdown above every modal (
dropdownLayer.guard.test.ts) and floating windows above the sidebars, slug uniqueness closed on six write paths with a boot backfill and an "Address adjusted" notice, renames that move a name-derived slug, custom-role grants in the impersonatable set, overlapping tokens that stack whole, exports that no longer carry the source's version group, concentration and save-ends as timed effects with metadata, and Sentry frontend monitoring -
v0.106.0 — New character sheet: the 5e sheet — the first real sheet on v0.105.0's seam:
dnd5e0.17.0 draws the fillable 5e sheet's three pages from@purple-cat/sheet-kit— the geometry extracted from the form (334 widgets into the 45 boxes it draws, the PDF uncommitted), the pages as grids that reflow three → two → one column, the module's own chrome in a Retro and a plain variant, the boxes the platform has no field for undermetadata.dnd5e.*, every field with its editor over a pure view model, rolls and acknowledgements, print; Module API 1.40.0 — the seam's corrections from building on it (SheetApi.rest/.equipment/.treasury.claim, theequipmentprop,openPickerfor an existing attack and for the ability-score generator,CharacterSheetSchema.metadataFieldswith its namespace, which the write path checks, history labels and the importer asks for), the kit's reflow and--sk-region-padding,_ownerNameon the character GET; and the playtest's own findings — the sheet's alignment and sizing, a button on the character page rather than a buried menu item, and an undocked sheet that drew nothing because only one of the two context switches knew it -
v0.105.1 — Hotfix: a character's owner on a token the GM placed is in the impersonatable set (resolved through
resolveCharacterAccess), Superuser Mode may transfer ownership including to itself, the Transfer Ownership modal throughauthFetch; CI prunes its own pre-deploy RDS snapshots before taking one -
v0.105.0 — New character sheet: foundations — the seam a system module renders a sheet through (Module API 1.39.0:
registerCharacterSheetRenderer,SheetRendererProps/SheetApi/SheetEditIntent/RecentChange),@purple-cat/sheet-kit(layout and editing primitives, the field registry, the geometry guard), the SheetHost with its Sheet context, route and print mode, the recent-changes feed and the field registry, proven against a fixture renderer; CS-15.12's dead sheet code retired (7,824 lines); the ability-score rule (NS-D10) fixed; the platform reads a campaign's conventional configuration keys -
v0.104.4 — Token stacks — the hover UI on a stacked token (Select one / Separate all), stacks inline in the multi-select panel with per-row select and delete, paste and duplicate spread their copies to free cells, ⌘D duplicates, the list's action stubs hydrated before any copy, the duplicate-key warning from
createToken's cache append -
v0.104.3 — a Timeline slice's card pins open on click, with an ×
-
v0.104.2 — Effects from powers — a derived System 8 power with a duration places one TimedEffect per target through
api.timeline(rounds count; concentration / encounter / sustained / permanent until ended; a save that ends asked on the bearer's own turn against the power roll);save:frontmatter read,halvesandpersonalrecognised; the generator's revision in the staleness stamp -
v0.104.1 — the Timeline strip a third narrower, reopenable from a click on the rail and from a toggle at the left of the chat header's icons
-
v0.104.0 — Timeline — initiative made the map's (
maps.timeline, committed through the Time Machine, the store a cache of it, theinitiative:*relays retired); ticks asround.initiativewithPOST /timeline/advance; TimedEffects (timed_effects, thetimedEffectgrant entity) with a duration in rounds, indefinite, or a sandboxed function evaluated every tick —whoseTurn.requestRolla pause through the roll-request door, a throwing function reported once; Action API 0.6.0'sapi.timelineand the distinct-colour palette; the strip beside the chat (rows per initiative count, round headers, the now-line at 15%, the past fading, tokens at their initiative, effects as slices stacking right-to-left, the + with both dialogues) and its drawer on the phone; Buffs' duration as a reference to an effect -
v0.103.1 — Docs: the new character sheet re-specified as an epic of three releases (foundations, the 5e sheet, the System 8 sheet) with the walkthrough's picture written in; no code
-
v0.103.0 — Treasure Manager — the Treasury's page and its
/playsidebar over v0.102.0's model; hauls (items by hand or from the module's catalogue, coins one stack per currency, edit / remove, every change a commit); loot from a defeated enemy and from a chest as views of their holders (alootbucket names its character or token;treasure.lootVisibilityand adjacency decide who sees it; a take is the transfer); distribution sessions by GM arbitration (drag or give), need / greed (the roll-off on the server, frames in the channel, ties re-rolled) and total value (the lowest total picks); merchants with prices, stock and the wallet (buy with the base currency first and change back, sell attreasure.sellRate, a shop at a token, stocked from the catalogue); party treasure carried, handed over and weighed on the carrier's sheet; earmarks visible and claimable wherever they sit; Action API 0.5.0's read-onlyapi.treasury; and the Rolls settings enforced at the post (a roll frame routed to the Secret channel) -
v0.102.0 — Campaign configuration: the settings and the Treasury model — each system module declares the tabletop settings a campaign decides at session zero (Module API 1.38.0:
campaignConfigurationSchema, seven kinds, defaults at read,getCampaignCatalogue,startingKitFor; dnd5e 0.16.0 with ~40 settings per edition, system8 0.17.0 with 23), the platform its Players / Rolls / Treasure groups (two Players keys are grant rows the resolver reads; three gate routes), oneconfigurationcolumn with strict validation and commits, Configuration sections on Edit Campaign rendered from the schema, a campaign-creation walkthrough over the same form, re-validation on a system change, Action API 0.4.0'sapi.campaign.configuration, the.catarchive carrying it all; and the Treasury's model —equipmentwith a holder (character / bucket / token / none), buckets with reveal and a carrier, one atomic transfer that cannot double-claim (stacks split, coins credit the wallet),treasuryas a versioned entity whose permissions are its campaign's, treasure mode enforced, starting equipment on link, and the Players gates (characters per player, pending links, sheets locked to the GM) -
v0.101.1 — Hotfix: history diffs a power, not the powers — every skill, feat, power, attack and attribute is stamped a UUID
idon first write; the diff engine matches a keyed array element by element (id, else slug, else name) intorow.powers[<key>]added / removed /.fieldchanged, the stamp itself not a change; the panel renders each element as an item of its section and blame resolves a sheet section through its elements' prefixes -
v0.101.0 — Time Machine: history UI + Error messaging: productive actions — one History panel for every versioned entity (commit list, diff between any two grouped by the entity's sections, blame on hover, restore behind a confirm showing the diff, cascade preview of linked entities unchecked by default, timelines with rename, character versions as tags,
/undeleteas the deletion commits) reached from the sheet, the campaign, the map palette and the token drawer; Request access on a permission error (access_requests, a level picked, owners notified, who-can-help inside a campaign only, the pending state, the grant arriving over the socket), Roll back and Preview previous version on a fault; the play bar's side columns at their minimum, the palette a row of the bar, the nub's Edit Current Map, the connectivity popover off the rail, Help / Profile / Notifications as routes with their entries beneath, the Edit Campaign nav in four sections and scrolling -
v0.100.0 — Time Machine: history core — campaigns, maps and characters get histories like git commits: every write is a commit (a content-addressed snapshot of the entity's contained set with a parent pointer, on a timeline; a token's is its map's),
versioned(...)one line on every write route with a guard that puts every write router in COMMITS or UNVERSIONED-with-a-reason, a structured diff with a one-line summary ("name, 2 tokens moved"),/api/history(commits with blame by username, diffs, one commit with its snapshot, restore as a forked timeline so nothing is lost, a cascade, the deleted query), character versions as tags, soft delete as a commit,undoStoreretired — the map's Undo/Redo are restores of its previous / next commit and survive a reload — and Action API 0.3's read-onlyapi.history -
v0.99.0 — Chat scope — chat gains channels: Campaign, Map, private messages (
/w username), Personal, the owner's Secret channel with per-message reveal, and custom channels, behind an iMessage-style conversation list in the sidebar, the phone's Chat tab and/chat/:id/:channelId; the built-ins virtual (an id naming the parent, no row), membership a grant with a cascade loader per kind (a PM has no parent, so the GM cannot see in), one socket room per channel joined through the Gatekeeper, every sender posting into the channel the user is looking at, achat-messagenotification for a PM or a custom channel, combat log and transcript per channel; a three-socket test holds the PM boundary -
v0.98.0 — Mobile play shell —
/playon a phone is three tabs — Map, Me, Chat — each owning the screen above a fixed bottom bar, with up to two slots per tab (Actions and a vertical, labelled Palette; Change and Stop; the chat View list), the model inmobileShellStoreand the bar its renderer; the mobile top nav retired (the nub names the map, the rail is the bar's Menu drawer off/play;NavSections,SideNav,TopNavand the invitations bell-menu went with it); sheets that stop at the bar, dismiss on outside tap and yield to the back gesture; the shell chosen by input, not width; every bottom anchor reading the published chrome; Style Guide 3.1.0 and a help page for playing on a phone. M.11's physical-device pass is still owed -
v0.97.3 — Hotfix: the play bar's Map and Campaign buttons open the editors in the sidebar — Edit Map (name, slug, foundation texture, scale) and Edit Campaign (the
/campaign/:id/editform, sections and all) — instead of the read-only panels -
v0.97.2 — Hotfix: the rail's foot controls are labelled rows when the rail is wide — Search, Help, Connected / Disconnected, Notifications, the username — via a
renderTargetseam onNotificationsBellandProfileMenu -
v0.97.1 — Hotfix: the rail polished from first use — one 44px icon column for items and foot, no rule between them, fit-content width up to 220px measured by a ResizeObserver into
railWidthStore, left-aligned children,mdlabels -
v0.97.0 — Navigation: left rail and play bar — the global top bar (v0.42.0) is gone and a left rail wide enough for labels is back: one route model (
navModel.ts) with the second level indented under the active item and collapsed to icons on/play, Search / Help / connectivity / Notifications / Profile in the rail's foot, Share in the page header's CTA slot as verb or noun,SectionNavas the named third level;/playgets a three-column play bar with the palette embedded in the centre and the map's name in a nub that is the only map switcher, sidebars opening from the bar; thirteen chrome components retired and held retired by a guard; Style Guide 3.0.0 -
v0.96.0 — Permissions UI and audit log — one
PermissionsEditorfor every entity replaces the campaign invite panel, the character access modal and the token ownership panel: add and remove roles by username, an effective-permissions tab that says where each user's access comes from (v0.95.0'swhoCan, rendered), preview before apply with the cascade consequences spelled out, custom roles withapplies_to, what a new member gets as arole-defaultgrant; apermission_eventsaudit log written by every grant write with its reason and the actor from an AsyncLocalStorage store, a History tab and an admin view, nothing deleting it but the admin purge; Q.12's guard tightened to usernames everywhere (firstNamegone from the display chain, known-users readsusers); plus the help site in the Retro theme, the near-term roadmap as the roadmap page's default tab withROADMAP.mda pointer, the mobile Actions modal grouped by power, and a bug-tracker spec -
v0.95.0 — Roles and the Gatekeeper — one
grantstable (four built-in roles in code, custom roles as rows, six closed verbs, no deny, Admin a flag) and one resolver (effectivePermissionsover a cascade with an un-overridable Owner floor and a Collaborator cap on the character edge;whoCanits inverse) replace three permission models, with the legacy tables backfilled at boot and mirrored on every write; the Gatekeeper walks every private read's body against the actor — 126GETroutes in exactly one of four declared sets, a guard that fails the build on an undeclared one, five routes that had no check at all closed, a per-request cache and batched prefetch for the hot paint path; a socket carries an actor or is refused and joins a campaign room only through the resolver;ADMIN_USER_IDis gone and Superuser Mode — a server-side, expiring, banner'd switch — is the one way past every check; Module API 1.37.0 (req.actor,req.effectivePermissionson module routes); and the route harness server-core never had -
v0.94.0 — Dev environment rename: next.purplecat.games —
dev.purplecat.gamesbecomesnext.purplecat.gamesandapi-devbecomesapi-next, freeing thedevname: one Terraform variable moves the CloudFront alias,FRONTEND_URL(and so CORS and invite links), the assets bucket CORS rule and the ALB host rule together; Deploy Dev targets the new hosts anddeployHostParity.test.tskeeps the workflow andmodule.devequal; every planned release from here moves down one number (Roles and the Gatekeeper is v0.95.0, through v0.105.0) -
v0.93.0 — Actor model and the users table — the platform's own
userstable (both dialects, pg 0030/0031) backfilled from the 30 identity-bearing columns read off the schema object,user_settingsfolded in behind a foreign key withis_adminmoved home; one server-sideresolveActor(req)settingreq.actor(158 reads ofreq.auth.userIdacross 29 files migrated, and the old convention's test becomes the guard) and one client-sideusePlatformAuth()undersrc/auth/wrapping the provider one-to-one (34 files migrated;usercarries only what any surface may show of anyone); two ratcheting guards keep@clerk/*inside the seam; andusers.username— validated, seeded at boot from the provider in batches with a derived handle only when the provider answered, editable in Settings, and what the roster, chat and every share picker now show -
v0.92.0 — Error messaging: the standard and the helper — every error a user sees is a system fault or a user error:
AppErrorin shared, a flat{ error, kind, code }envelope on every API error body,reportProblemwith a swappable transport behind a db-freePOST /api/reportand amailto:fallback, oneSystemFaultPanel(Retry / Reload / Report) behind the boundary, the error page and the query client, aUserErrorPanelwith the fix, backend-down detection that gates every fetch and re-opens a socket Socket.IO had given up on, three sweeps from the source outward (189 fetch throws, 51 error notifies, 97 inline error strings), a guard on the three erosions, and a help page on what Report sends -
v0.91.0 — Slugs and stable links — every campaign, map and character link prefers the slug, through four href helpers and a guard; a slug in the URL resolves to an id once (
/api/resolve+ a client slug index) before the id-keyed route readers see it; an id URL rewrites to the slug form;slug_history(both dialects, pg 0029) records every tenure so a stale link offers the way back; a live collision fails with one-click alternatives, a historical one asks before a take-over, and editing an existing slug warns first; the map's slug field, missing since v0.38.0, is back in the Edit Map panel -
v0.90.0 — Heuristic evaluation quick fixes — the first of the fifteen releases sequenced from the August 2026 heuristic evaluation: character links go to
/characters/:idand/is a home page (two v0.42.0 reversals), campaign strips always show prev / next / Show all, empty lists say "you have none", one New Character modal from the campaign page too, per-map default token / object / drawing / background layers with drawing mode switching to the default and saying so, three mobile fixes (notifications above the bar, no hover UI mid-drag, "your turn" once), collapsible S8 category groups,publicDisplayNameso no surface ever shows another user's email or full name, factions and system traits announce, and the group-A socket emits scoped to the campaign room -
v0.89.0 — Viewstate invalidation — one generic
viewstate:changedhint beneath the typed socket events, scoped by room, with one entity → query-key table on each side, so the ~40 write routes that told no other client anything (a GM's new condition, a sheet another user has open, a removed member's still-working session) now do; a source-text guard puts every write route in exactly one of three sets — announces, carries its own data, or silent with a written reason;refetchOnWindowFocuson for the hint-only caches and a resume check after a long hide; plus the campaign room rejoined on every reconnect, a live bug found on the way -
v0.88.1 — Hotfix: v0.88.0's CI run was red, so it never deployed;
requestRollInvocation.test.tsguarded the relay's enrichment spread with a fixed roster of two fields and v0.88.0 added a third (characterOwnerId). The guard now reads the shape —...payloadfirst, then any number of single-field conditional spreads, none of theminvocation— andCLAUDE.mdmakes a greenpnpm verifyon the squashed tree the gate for every commit to main -
v0.88.0 — UX and playtest bugs — nine UX items and nineteen bugs from a two-user playtest: a measure tool on the palette, the palette overflowing into a
⋯menu for a GM on a phone, every modal above every sidebar, read-only impersonation of a token you can only view, the Equipment filters as a slide-over, custom feats and powers in System 8, chat timestamps back as tooltips, initiative that neither crosses maps nor duplicates tokens,requestRoll()prompting the GM and doing the maths with the modifier rather than the score, and the S8 proficiency-points bug (two creation flows wroteBasewhere the schema saysbase); never deployed — see v0.88.1 -
v0.87.0 — Image Manipulation, phases 5–6: object removal and separation — click the boulder, get the boulder: SAM 2 runs the click in the browser (WebGPU, with WASM behind it) over a cached embedding, Replicate's
meta/sam-2runs the whole-image sweep behind aSegmentationProviderinterface with the operator's own GPU as an upgrade rung and the user's own token first; Extract Object cuts one object out and Extract Objects cuts every object out at once, leaving one hole that Fill Removed Area paints in one tiled call per cluster (OpenAI or Gemini on the same key ladder, a browser pull–push fill as the no-key tier); every side effect — the cutout, the hole — is offered a home by where you are (the map's objects layer, the library, a character) and a batch takes one choice; pick the object on the map itself with a live outline under the cursor and the bare map while you do; the deployment study restored and revised, D4/D5/D7 answered; plus Resize on Map as a modal and a System 8 feat's About showing the document rather than the brief -
v0.86.0 — Summoners + chat dice grouping — a character can own other characters (a directional relationship with a free type — minion, partner, ally — edited as pills beside Campaign & Party on the sheet), targeting mode can pick squares rather than tokens, and four Actions API functions put characters on the map where you click:
api.pickSquares,api.importCharacter,api.importParty,api.importRelatedCharacters(Shelly clicks two squares and her skeletons appear in them);roll(formula, { silent }); Action API 0.2 with the first version constant actions have had. Chat dice grouping: the dice token models many die types at once so2d20+4d6+10is a rich row with 3D dice, the row groups at render time, a stack draws as a stack with its total and its crit, and hovering it shows the dice — old messages improve for free; plus the dice tooltip that looked like the timestamp -
v0.85.0 — Character Import, phases 3–5 — the import stops being a toast and becomes a review: every catalogue match on its own row with a confidence tier, correctable before the character exists, validated by the system's own rules, and honest about what an import over an existing character would replace; extraction reads the WotC fillable 5e sheet exactly instead of describing it to a model, renders a scanned sheet to images when there is no text layer, reads a long sheet in pieces, and takes
.docx/.md/.txt/pasted text through one reader; a D&D Beyond character imports from its URL or JSON with no model in the loop, and a party document produces a party rather than one chimera; plus "Import Character" on the map now actually imports a character, context-menu submenus stop being clipped by their parent, and task 5.3 leaves for its own gated roadmap entry -
v0.83.0 — two ways to make the map bigger — upscaling raises resolution and resizing changes footprint, and the app did a blurry version of both: the Retouch factor is a control the host renders rather than a param nothing could set, an upscale stops reporting a size multiplier and so stops moving the battlemap it came from, and a background token can be scaled from any of nine anchors in one click; plus four GitHub issues, and three defects found on the way —
genai's "4×" producing a 16× image, half ofgenai's test suite being a stale worktree copy of itself, and a corner drag whose two PATCHes made resizing a mount drag its riders -
v0.82.1 — Hotfix: clicking a token to impersonate it froze the map;
useAutoArtworkheld theuseImageGenAuthFetchwrapper object instead of destructuring it, which both broke every image-generator call and made an effect dependency change on every render, spinning ~49,000 renders a trace; plus CI's Playwright install no longer failing when Google's Chrome apt repository serves an index that does not match its own hashes -
v0.82.0 — playtest & polish — all eighteen items from the v0.75.0 playtest, of which four turned out to be one fix each in a shared place —
searchableas a property of the option list rather than a per-call-site taste decision, every dropdown clamped to the viewport, every button label ellipsised, and awarningcolour family in both skins; plus the Create Token modal's ten cards with per-artist CC BY 3.0 attribution generated from a manifest, and System 8'sminRankon the seventeen actions that gate on character rank -
v0.81.0 — the roadmap becomes a machine — typed frontmatter on every planning document with three checks that fail the build, one generated manifest and a dashboard at
help.purplecat.games; then all 133 planning documents moved into it and the four trees they lived in deleted, the changelog's 132 per-version notes made authored pages, the four architecture trees collapsed into one published developer API tree, anddocs/reduced to the wiki with a CI guard that keeps it that way -
v0.80.0 — the ledger — the roadmap and the specs it pointed at had drifted apart: four untagged releases, three finished projects still filed as active, forty-four shipped tasks reading as backlog, and ten projects the roadmap never mentioned; plus ROADMAP.md as the one plan, twenty playtest notes rewritten as tasks, and image search consolidated behind a provider registry ahead of the Google API it runs on being retired
-
v0.79.0 — the generators reach the models you already chose —
genaigains a headless half so any surface can generate an image without rendering the module's panel, and System 8 monster generation moves out of the platform into the module that owns the rules, with Class and Subclass Preference -
v0.78.1 — Hotfix: every
/api/modules/system8route 404'd in production; an Express-5-invalid route string in system8 0.13.0 threw at import, so the loader skipped the whole module -
v0.78.0 — powers that roll damage, actions that stream, and a modal that navigates itself — one parameter list for the action sandbox, a
damage:convention the power generator reads, the four surfaces that never opted into streaming, and a document viewer with a stack -
v0.77.0 — the power row, the linked-token menu, and a player who can seat their own token — one row shape whatever the action count, a linkage badge that lists the tokens it counts, and "ask the GM" replaced by the characters you already hold
-
v0.76.0 — System 8 content comes from the module, not the wiki — the rules content ships inside
@purple-cat-games/system8; the server-side wiki fetch that bypassed the host allowlist is gone, and the area parser stops dropping 30 of 106 areas -
v0.75.1 — Hotfix: host system8 0.12.1 (0.12.0 never published)
-
v0.75.0 — the app on a phone — the desktop layout at a quarter of the width, fixed where it broke: the iOS zoom trap, list pages that fit a 375px screen, and thirteen character-sheet fixes from the same playtest; plus a party that only its creator could see, a
500on every logged-out API request, and a cleanup migration that had never run -
v0.74.0 — the changelog moves to the help site — the Changelog tab has never rendered the changelog; the file and everything it links to are hoisted into the help wiki at build time, and the tab renders that page
-
v0.73.0 — Chat Log Compactness — one button press produced up to 25 chat messages; an invocation is now a single
[ACTION_DOC:…]document that every writer patches in place, plus placeholder token art that finally renders and the end of the Edit Token modal's edit paths -
v0.72.0 — Image Manipulation, phases 3–4 — the Retouch dialogue, and a Retouch button on every surface that shows an image you own; the capabilities stop depending on which modal you opened
-
v0.70.0 — Image Manipulation — five capabilities in five unrelated homes become declared operations on an image; an artwork stops holding one processed image and starts holding a list, each knowing how it was made
-
v0.69.0 — Character Import — import stops producing a v0.20-era character under the wrong system: one declared shape, an envelope each module describes for itself, and traits matched against that module's own catalogue
-
v0.68.0 — Test Health, part 2 — source-text guards bound by the construct instead of a character count, a lint that stops swallowing its own errors, an end-to-end suite that exists, and coverage ratchets that can only rise
-
v0.67.0 — Test Health, part 1 —
apps/web's 8,635 tests had never run; they gate CI now, and the 106 failures behind that line get triaged rather than loosened -
v0.66.0 — Library Overhaul, phases 0–2 — the Library stops storing local disk paths as image URLs and gets an ownership model, client-side upload, and tagging v3
-
v0.65.0 — Client-Side Image Ingestion — the server stops decoding images a client is holding;
/api/image-proxystreams, and the remaining server decode paths are bounded -
v0.64.0 — Spell Chat Messages (round 2) — spells with no action get one, and the row stops pretending a multi-action spell has a single
⋯ -
v0.63.0 — Sharing Buttons —
share-2is the verb andshareis the noun; the global Share button becomes a route-aware status display -
v0.62.0 — Notifications & Messages — one API for everything the app tells you, and a durable
messagestable under the part worth keeping -
v0.61.0 — Character Permissions — one table and one three-valued access model for characters;
character_membersdropped -
v0.60.0 — Fixes & Polish — spells-section geometry, help documents, and a long tail of fixes; the Roadmap System did not ship with it
-
v0.59.0 — Security & Sync — three live data-exposure holes closed, four multiplayer sync bugs fixed
-
v0.58.0 — Playtest fixes
-
v0.57.0 — Early SaaS maturity
-
v0.56.4 — Hotfix: the alpha deploy gate counted every check run on the tagged commit, so a failed
resolvejob blocked that commit permanently -
v0.56.3 — Hotfix: host
system50.3.1 (0.2.0 never published) -
v0.56.1 — Hotfix: host
dnd5e0.8.2 (0.8.1 never published) -
v0.56.0 — Subclasses, proficiencies, and new infrastructure — the v0.55.0 production playtest
-
v0.55.0 — Module API 1.24.0: the host tells modules where its API is; hosts
dnd5e0.7.1 -
v0.54.1 — Hotfix: 5e feats and spells returned HTML in production (relative
/apipaths hit the SPA origin, not the API) -
v0.54.0 — 5e character sheet actions
-
v0.53.0 — 5e character sheet validation, part 2: spells and background
-
v0.52.0 — 5e character sheet validation, part 1
-
v0.51.0 — Polish for 5e character sheet
-
v0.50.2 — CI hotfix: GitHub Packages auth moves from the
PKG_TOKENPAT toGITHUB_TOKEN -
v0.50.1 — CI hotfix: the two lint errors v0.50.0 left in
server-core -
v0.50.0 — The new character sheet for D&D 5E — one sheet for both editions, plus map Layers and Drawings
-
v0.49.0 — Character Sheet Overhaul: System 8 character sheet validation
-
v0.48.0 — Character Sheet Overhaul: rollovers and corrections for System 8 character sheets; restored left nav
-
v0.47.3 — CI hotfix: cut the Terraform job back to
fmt+validate -
v0.47.2 — CI hotfix: unblock the Terraform job so v0.47.1 can apply
-
v0.47.1 — Fixed System 8 URL (never reached production)
-
v0.47.0 — Character sheet fixes — artwork modal, undelete UI,
/admin/data -
v0.46.1 — Host system8 0.6.0 and dnd5e 0.2.0
-
v0.44.1 — CI hotfix: install pnpm directly (Node 26 ships no Corepack)
-
v0.44.0 — System/Platform Separation, phase 2
-
v0.43.1 — CI hotfix: stale MODULE_API_VERSION test
-
v0.43.0 — System/Platform Separation, phase 1
-
v0.27.3 — Hotfix: postgres maps timestamps migration (2026-05-20)
Phase I — the AI settings panel picks a model, not a vendor
Playtest of the Settings page. Three findings, and the middle one is the whole shape of it: Task Preferences chooses a vendor and calls it a model. The row is one dropdown reading "Google Gemini", while Available Models — six inches further down the same panel — lists the forty models that vendor actually offers. Whichever one the server happens to default to is the one every task gets, and nothing in the UI says which. Picking a provider is not picking a model, and the panel has been claiming otherwise since the feature shipped.
- [x] I.1 — The Task Preference dropdowns open when Settings is in the sidebar. On
/playthe Settings page renders inside the left sidebar (SIDEBAR, 600) and the Select's dropdown portals to<body>at Mantine's default 300, so it opens behind the panel it belongs to and reads as a dead control. Every Select in the panel takes an explicitcomboboxProps.zIndex; nothing else in the panel changes. - [x] I.2 — Task Preference is a provider AND a model. Two dropdowns per task: the provider,
then that provider's models — the same list Available Models fetches, minus the ones the
user toggled off there (which is what that toggle has always promised and never
delivered, because nothing downstream read
disabledModels), and minus the non-text models a chat task cannot use. Above the full list sit up to three shortcuts — Fast, Balanced, Pro — resolved per provider. There is no modality or tier field in any provider's/modelsresponse, so the tiers are a hand-curated pattern table (utils/modelTiers.ts) with a version-preferring tiebreak, in the same spirit as the curatedIMAGE_MODELScatalog and for the same reason. A task may still say "Automatic", which is what every existing preference means today. - [x] I.2b — The tier picks the alias, not the dated snapshot. Found by opening the
dropdown against a live OpenAI account:
gpt-5-nano-2025-08-07outrankedgpt-5-nano.versionOfdropped numeric runs of four digits or more — enough for Anthropic's-20250514, useless against OpenAI's-2025-08-07, whose08and07survived and read as version 5.8. Hyphenated dates are stripped before the digits are read. The alias is also the better answer on the merits: it follows the provider's upgrades where a snapshot is frozen and eventually retired. - [x] I.3 — A preferred image-generation model. The panel could pick a model for four text
tasks and none for the one people notice most — token art. Provider + model, from the
platform's
GET /api/ai/image-modelscatalog, stored beside the task preferences and handed to artwork sources aspreferredImageModel(Module API bump). Platform-side it also seeds the map generator'sx-ai-model. - [x] I.4 —
genaiopens on the preferred model. The module auto-selectsmodels[0]— the first entry of a list the platform sorts, which is why token art has always come out of whatever provider happened to sort first. It honourspreferredImageModelwhen the host supplies one and that model is in the list, and falls back to today's behaviour when it is not. Lives in thegenairepo (v0.8.0, committed onmain, unpublished); listed here because I.3 is inert without it.
This page is generated from the repository's CHANGELOG.md at build time
(apps/help/scripts/hoistChangelog.mjs). Edit that file, not this one.