P5 backend (the deferred items):
- conflict-aware cloud sync via optimistic concurrency: the server versions each
backup blob (file mtime) and rejects a push whose base version is stale (409),
so a second device can't silently overwrite a newer cloud copy. The client
tracks its last-synced version; the SyncStatusIndicator surfaces a "Sync
conflict" with one-click resolution (Use cloud / Keep this device), and the
Settings push offers the same choice.
- revocable invites: owner can rotate a campaign's invite code (kills the old
one) and remove a member (drops them + deletes their published characters).
(Skipped editor/player roles — no enforcement target in the current model.)
- image storage: deliberately deferred (live images already stream de-duped on a
separate channel; a backup blob-store is infra with no correctness gain).
P7 — subclass/feat/multiclass depth:
- feats are now first-class tracked records (name + effect text) in a Feats &
Features sheet section, not buried in notes. Dexie v15 migration.
- 5e multiclass spell-slot calculator: a pure multiclassSlots() (full ×1, half
÷2, third ÷3 → full-caster table) + a sheet control to fill the slot table for
multiclass casters. (Honest scope: tracking + slots, not per-subclass feature
automation.)
P8 — test hardening: v2-surfaces e2e (feats, multiclass calc, signals bell,
World nav) + server tests (blob versioning, invite rotation, member removal) +
multiclass unit tests.
Gate green: 283 unit + 35 e2e + 2 realtime. App + server build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- src/lib/cloud/campaigns.ts wraps the campaign/character API.
- Settings "Shared campaigns (cloud)" panel (signed-in only): publish a local
campaign → get an invite code; join a campaign by code; publish a local character
to a campaign (stays yours, re-publish to sync); owners "View party" to read all
published characters (read-all, per the View+suggest model).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Lean file-backed accounts (server/src/accounts.ts): scrypt-hashed passwords,
hashed bearer tokens, per-user backup blob; persisted under DATA_DIR. /api routes
(register/login/logout, PUT/GET /save) with per-IP throttle + 48MB body limit.
- Client cloud lib + Settings "Cloud sync": sign up / sign in, back up this device,
restore from cloud (whole-backup push/pull, last-write-wins). Local-first default;
the assistant key (localStorage) is never part of the synced Dexie backup.
- Pathbuilder 2e import: the existing character import now detects a Pathbuilder
build JSON and converts it to a PF2e character (abilities/HP/saves/skills).
- Dockerfile creates a node-owned /data; compose mounts a named ttrpg-data volume.
- (Spectator links = existing /play?room=CODE; PDF export = existing Print.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Bring-your-own-key assistant config in a dedicated persisted store
(src/stores/assistantStore.ts); partialize drops the key from localStorage
when 'remember' is off. Key lives in localStorage only, so it's auto-excluded
from Dexie backups.
- Provider-agnostic LLM client (src/lib/llm) over fetch, no SDK: Anthropic
Messages + OpenAI-compatible Chat Completions (covers OpenAI/OpenRouter/
Ollama/LM Studio via custom base URL). Structured JSON output parsed +
Zod-validated; timeouts; typed error kinds; key never leaked in errors.
- Settings 'Assistant (AI)' section with provider/model/baseUrl/key, remember
+ enabled toggles, and Test connection.
- CSP connect-src widened to any https + localhost for the BYO endpoint.
10 client unit tests; settings e2e asserts config persists and the key is
never present in an exported backup.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>