Files
claude-config/memory/projects/projects-MacMem/forgerpg.md
T

45 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: forgerpg
description: "ForgeRPG project state — Phase 7 (chargen) + Character Export (MD/PDF/JPG/DOCX, per-system sheets) BOTH LIVE on prod forge.timevans.uk; export proven e2e, Mac app rebuilt with export menus"
metadata:
node_type: memory
type: project
originSessionId: 93f41ee8-6eaa-4889-8122-80a04dbc4dab
---
ForgeRPG = Tim's tabletop RPG tool: FastAPI backend (`~/projects/ForgeRPG-api`, github slaguru666/ForgeRPG-api, private) + SwiftUI Mac app (`~/projects/ForgeRPG-mac`, slaguru666/ForgeRPG-mac).
**CHARACTER EXPORT FEATURE LIVE ON PROD 2026-07-05** (api `721b6c2` 730 tests; mac `91937f1` 253 tests). Deployed aa85c9e→721b6c2 (ff, NO migration); the WeasyPrint-native-stack docker build (the flagged risk) SUCCEEDED cleanly; runtime imports verified in-container; e2e proven on forge.timevans.uk (temp NPC → MD/PDF/JPG/DOCX all HTTP 200 valid, bad-format 422, deleted). Mac app rebuilt + relaunched with export menus. Export any saved NPC/PC (or a generated draft via save-then-export) as **MD/PDF/JPG/DOCX**, single or bulk-per-campaign zip, with **per-system sheet layouts**. Architecture: pure `SheetModel` (app/export/model.py) built from a `LayoutDescriptor` (registry app/export/layout.py, keyed by campaign system name substring → per-system JSON+CSS, else universal fallback) → 4 renderers (render_md, render_docx=python-docx, render_html+render_pdf=**WeasyPrint**, render_jpg=**PyMuPDF** raster of the PDF). Endpoints app/api/character_export.py: `GET /library/npcs/{id}/export?format=`, `GET /campaigns/{cid}/pcs/{id}/export?format=`, `GET /campaigns/{cid}/export-characters?format=&kind=npc|pc|all` (zip). JPG of a character WITH a portrait returns a ZIP (sheet+portrait) — Mac ExportStore reads Content-Type/filename FROM HEADERS, never assumes. Portraits from MinIO (load_portrait, None-safe). 2 flagship layouts shipped (sla-zero, cthulhu — CoC parchment, SLA dark-crimson mono); universal covers the rest; Wave 2+ layouts = authored data (backlog). **Deploy notes:** NO migration (pure read). Carries Dockerfile apt system-libs (libpango/pangocairo/cairo/gdk-pixbuf/libffi8/shared-mime-info/fonts-dejavu-core) + pip weasyprint==63.1/pymupdf==1.24.14/python-docx==1.1.2. RISK: the python:3.12-slim + WeasyPrint native stack has NEVER been docker-built (deps proven only on the Mac host, where `brew reinstall pango gdk-pixbuf` was needed to fix broken dylibs) — watch the first `docker build` apt/pip resolution. In-app export also needs the Mac app rebuilt (swift run). LESSON: content-fidelity tests can't catch visual CSS bugs → render + eyeball flagship sheets (caught a float:right cell-overlap bug all 15 fidelity tests missed; fixed with flexbox). Spec/plan: docs/superpowers/{specs,plans}/2026-07-05-character-export*.md.
**PHASE 7 COMPLETE + DEPLOYED TO PROD 2026-07-05 — template-driven CHARACTER + NPC creation** (api `aa85c9e` 694 tests; mac `558d17a` 247 tests; both pushed). **LIVE on forge.timevans.uk**: prod fast-forwarded 7359632→aa85c9e (P7 additive only; P6 was already live via the cloud-art deploy — the old "P6 pending" note was stale), migrations `e3aa1ae31d9a`→`50da6ad01970`→`d6765ee850f1` applied, 11 chargen templates seeded via the standalone `python -m app.chargen.seed_templates` (created 11, three-way merge — used standalone NOT full `app.seed` to avoid re-touching prod's 16 campaigns/history). PROD-VERIFIED e2e 2026-07-05: login→generate PC+NPC across ALL 11 systems = 22/22 (Mothership reduced block + Traveller skill-package archetypes legitimately have empty `attributes` — that's the mode, not a failure); SLA-Zero `shiver` delta-scaling proven exact across all 6 threat tiers (mook Δ−1 →2/2/2/2 hp4 armor1 … apex Δ+4 →5/5/5/5 hp14 armor6, matching clamp(v+Δ,2,5)/max(1,hp+Δ*2)/clamp(armor,0,6)). Deploy verify harness: scratchpad/prod_verify.py (login regex from task-9-deploy-report.md, urllib, no-secret-print). Ops: standalone chargen seeder = surgical prod seeding path for template updates. Tim's ask: "template system... create characters and npcs, select the game template", "catch my sla-zero engine characters", survey vs public resources. Scoping: session-ready sheets (no lifepath minigames, Traveller compressed), editable in-app templates, optional AI flavor fill. Built: (1) safe expression evaluator `app/chargen/exprs.py` (hand-written recursive-descent, NO eval, MAX_NODES 10k iterative — closed a recursion-crash class); (2) template DSL `app/chargen/dsl.py` (TemplateSpec, evaluation-order contract = validator is engine-truth); (3) generation engine `engine.py` (dice/array/pointbuy, packages, skill-pool auto-spend, careers, provenance, seeded-deterministic); (4) NPC modes — archetype blocks + EXACT SLA-Zero threat/tier DELTA scaling + Mothership reduced blocks; (5) careers `careers.py` (Traveller qualify→terms→skills); (6) endpoint `POST /library/generate-character` {count 1-10, ai_fill claude|local, never 500s on AI failure→ai_error}; (7) templates CRUD `/library/chargen-templates` (400 detail is a structured `{message,path}` object — the editor's inline-error contract; builtin DELETE→409, builtin PUT allowed); (8) **11 seeded systems** from a 5-lane research corpus, deep-reading Tim's OWN canonical rulebooks where they exist (CoC Luck contradiction found IN his Ch03; Stormbringer house SAN=POW*5 + 19-row nationality table; RingWorld 55-skill list from his sheet; SLA-Zero verbatim from his zero-engine repo — 8 races/8 trainings/11 archetypes/scaling); (9) Mac ChargenStore + recursive ChargenJSON; (10) **Character Forge wizard** (spec-driven picks: dice/array/pointbuy editors, roll tables, NPC ARCHETYPE selector + threat tier → SLA-Zero delta-scaled blocks, dice-reveal, editable review grid w/ provenance chips, AI flavor, save as NPC/PC via existing stats JSONB); (11) template editor (JSON spec, inline 400 field errors, builtin badge, duplicate/delete-custom-only). SEEDER SAFETY: builtins are user-editable, so the seeder does a THREE-WAY merge keyed on a `seed_hash` baseline — creates missing, upgrades UNMODIFIED builtins to new shipped content, PRESERVES user-edited ones (never clobbers edits; "Reset built-ins" = the explicit restore path). Whole-phase 4-seam integration review: DSL round-trip / materialization / ai_fill all CLEAN; caught + FIXED the NPC archetype path being unreachable from the wizard (the SLA-Zero NPC feature Tim called out). SDD process: 10 tasks, every one implementer→adversarial-reviewer gated, controller closed test loops; 2 crash/data-loss classes closed (evaluator recursion T1, seeder overwrite T6). Backlog: custom-template camelCase pick/attr keys get snake_case-mangled on the wire (shipped seeds unaffected — all-caps/lowercase); client-side species-cap resolution for interactive pointbuy (currently auto-assigned); Grange Hill + Meta Alpha templates (Tim deferred); form-based editor v2. See [[tims-rpg-games]].
**As of 2026-07-04 BOTH are COMPLETE and pushed.** Backend (HEAD `98a6d4e`, 101/101 tests): full CRUD, JWT auth, MinIO assets, Claude streaming/structured generation, markdown importer, search, session journal, dice + random tables, entity backlinks, plot threads, AI recaps/preps, campaign export, pregen roster, procedural map system (BSP/CA/modern/scifi, top-down + isometric, Universal VTT export), art job queue (A1111 Stable Diffusion + Midjourney bridge) with /ws/jobs. Dev DB seeded with Tim's real gaming history (see [[tims-rpg-games]]).
**P6.1 CLOUD ART DEPLOYED 2026-07-05** — api `7359632` (380 tests) + mac `3503a88` (231), BOTH LIVE ON PROD (server @ 7359632, keys in prod .env): "openai" + "gemini" providers in the artgen registry (key-presence availability; keys header-only, leak-proof error paths); Mac picker now server-driven. BOTH PROVEN e2e on local AND prod (real PNGs via forge.timevans.uk; OpenAI ~4¢/img gpt-image-1-mini medium; Gemini ~4¢/img gemini-2.5-flash-image). Gemini gotcha: his first "AQ.-prefix" key wasn't a Gemini API key (429) — working key is classic AIza format from AI Studio. Local SD is PAUSED by choice (renders under 16GB memory pressure took 3-12 min and starved the Mac; artgen read-timeout widened to 1800s in d370d3b so slow renders aren't orphaned when he does run it via forge-sd.sh up). MJ: still no official API (2026-07) — subscription/manual only. iPad companion design exploration published (ForgeRPG Table artifact) — kit is iOS-portable (Foundation+Security only), P7 candidate alongside multi-floor maps.
**PHASE 6 COMPLETE 2026-07-05 (overnight autonomous night shift)** — api `031419b` 364 tests; mac `1982833` 231 tests; both pushed; **PROD REDEPLOY PENDING Tim's "redeploy"** (carries migration `e3aa1ae31d9a` + SSE framing fix — prod content/recap/prep streaming is silently lossy until then). Shipped: map EDITOR (paint cells/stamps/room-renames/annotations over a locked JSONB edits overlay, PATCH regenerates assets so saves are visible, stroke-level undo surviving missed onEnded via @GestureState latch, top-down-only gate), village generator (roads/buildings/wells, zero-rng-when-disabled goldens), library v2 (PC creation, system stat templates from /library/stat-templates, art→portrait flow via single-key PUT), local AI (LM Studio provider `llm:"claude"|"local"` on content/recap/prep + forge-step, /generate/llm-providers), Scenario Forge wizard (5-step: premise→outline→scenes→NPCs/hooks→materialize, all streams editable, offline-capable). CRITICAL CONTRACT: SSE framing is now spec-compliant multi-line — api `_sse_event` splits chunks into per-line `data:` fields; mac streamSSE reassembles events joining with `\n` (NEVER parse SSE via AsyncLineSequence — it swallows the blank-line event boundaries); any new streaming endpoint MUST use `_sse_event`, wire fixtures MUST contain newlines. Fable phase-final review streak now 4/4 real seam bugs (P2 identity bleed, P3 editor swap, P5 status vocab, P6 SSE framing — both suites green on single-line fictions). Backlog in progress.md PHASE 6 COMPLETE entry; multi-floor maps = P7 candidate.
**PHASE 5 COMPLETE 2026-07-05** (api `392cadf` 227 tests; mac `fe5ff44` 183 tests; prod redeploy PENDING): maps overhaul (preview fit/zoom rebuild — the "empty map" was a 2× viewport illusion; Decor system + furnish/windows/density/decor/water options behind byte-identical goldens; render v2 glyphs/plates/room-numbers+legend; form presets) + WORKING LOCAL ART: A1111 proved broken at core on this Mac (step-count-dependent corruption; diffusers control clean) → replaced by `scripts/forge_sd_server.py` (159-line FastAPI diffusers microserver, same /sdapi/v1/txt2img subset, port 7860, managed by forge-sd.sh; DreamShaper 8 checkpoint at ~/ForgeRPG-sd/webui/models/). Status-vocab seam bug fixed (API pending/processing/complete ≠ Mac queued/running/completed — ingest normalization; BOTH suites had mocked their own fiction). Providers endpoint + honest failure UI + retry. LESSONS: cross-repo contract fixtures must use WIRE values; A1111 is abandonware-fragile on new macOS — diffusers direct is the durable path. PHASE 6 SCOPED (Tim): maps EDITOR (paint/stamps/annotations/village generator), library v2 (rich NPC/PC editors — PC creation flow currently missing, portraits from art studio, system-aware stat templates), Scenario Forge wizard with LOCAL AI via LM Studio (offline con prep).
**PHASE 4 COMPLETE 2026-07-04** (mac `f16bf28` 168 tests; api `eed7d42` 127 tests): "Arcane Modern" design system (Theme.swift tokens — ember/arcane/ink/rust/wheel, one spring, reduceMotion-gated everywhere, DARK-PINNED — light mode is future work; app must launch via activation-policy fix, unbundled swift-run apps can't focus text fields otherwise), full restyle of chrome/dashboard/editor/tools, LOCAL MODE (api scripts/forge-local.sh + forge-checkout/checkin con scripts with hard size gates + type-PUSH confirm; runbook scripts/README.md), server switcher menu + login quick-picks (KnownServers) + sign-out. GOTCHAS LEARNED: Tim's Mac = light mode (design must pin dark); Tim confused foundry↔forge hostname (quick-picks fix); MJ pipeline scripts NOT on this Mac (RPGS/Stormbringer/scripts empty — stale memory in [[tims-rpg-games]]); prod art providers unconfigured so art jobs fail (Phase 5: local SD install + providers-availability endpoint; Tim chose MJ+SD, rejected OpenAI images).
**PHASE 3 COMPLETE + DEPLOYED 2026-07-04** (api `1768a14` LIVE on VPS — image rebuilt with reportlab+jinja2, share_links migration `b44e4a01f0c6` applied, foundry/pdf/portal smoke-verified through forge.timevans.uk; mac `ad30af7`, 168 tests). Adds: ⌘K deep-links into scenario/scene (NavigationCoordinator; two identity-reset fixes: `.id(campaign.id)` on dashboard, `.id(scenario.id)` on editor — SwiftUI stores capture ids at init, EVERY in-place swap needs an id reset); field clearing via `clears: Set<String>` on payloads (explicit JSON null; backend exclude_unset semantics); Foundry VTT export (zip of importable journals [GM pages ownership.default=0], actors, walled scenes); con-pack PDF (`/export/pdf`, reportlab); player portal (share tokens, `/portal/{token}` jinja2 pages, handout-tagged assets only, security-reviewed — GM content provably absent); Share menu in dashboard. Polish backlog (12 items) in progress.md ledger.
**Mac app — Phases 1 + 2 + Amendment A COMPLETE** (HEAD `9ca30b4`, 123/123 tests; backend companion `fa53fb9`, 105 tests, deployed to prod): pure SwiftPM (`swift run ForgeRPG`), zero deps. Full feature set: login/Keychain + server-URL switching (stable injected client, 401→login sheet), campaign browser, scenario/scene markdown editor (GM notes, AI drawer, backlinks Links pane), library (NPCs/locations/factions/pregens), campaign dashboard tabs Scenarios|Sessions|Threads|Prep (journal with AI recaps, plot-thread lifecycle, Lazy-DM prep streaming + save-as-sheet), JSON/Markdown campaign export, dice roller + random tables, maps studio (4 kinds, 3 styles, topdown/iso preview, rerender, PNG + .dd2vtt export), art studio (SD/MJ jobs, live /ws/jobs status, thumbnails), ⌘K search palette (named results). Phase 2 ledger + polish backlog in `.superpowers/sdd/progress.md` (11 recorded non-blocking items; biggest: field-clearing explicit-null amendment, SceneStore flush-on-back, palette deep-links = Phase 3).
**DEPLOYED 2026-07-04** to VPS root@77.68.99.134 (same box as graphiti/mailu/gitea/nextcloud — scope rule: only touch /opt/forgerpg + compose project `forgerpg`). SSH: this Mac's ed25519 key authorized for root AND tevans. Stack: `/opt/forgerpg/app-src` @ 98a6d4e, `docker compose -p forgerpg -f docker-compose.prod.yml` (server-side compose file; base compose's 9001 MinIO console port couldn't be suppressed by merge override). API on 0.0.0.0:8000, postgres/minio internal-only. Migrations+seed applied; on-box smoke all green. ADMIN_PASSWORD in `.superpowers/sdd/task-9-deploy-report.md` (MacMem).
**LIVE at https://forge.timevans.uk** (2026-07-04): Tim added the DNS A record and the Caddy vhost (`reverse_proxy 172.18.0.1:8000` — host-gateway style, his edit) himself; real ANTHROPIC_API_KEY loaded; E2E verified from outside (login, 16 campaigns, real Claude SSE generation). Provider firewall keeps 8000/2222 closed externally — all traffic via Caddy 443, which is the intended end state.
**Ops gotcha (bit us once):** `docker compose restart` does NOT reload `.env` — env is baked at container creation. To apply .env changes: `docker compose -p forgerpg -f /opt/forgerpg/app-src/docker-compose.prod.yml up -d --force-recreate api`.
**Mac Amendment A (client injection) DONE 2026-07-04** — app is production-ready: one stable SessionStore-owned APIClient (`updateBaseURL` retargets in place, `client` is `let`), zero ad-hoc APIClient() in views, 401 hook survives server switches; 27/27 tests. Commits a380771/f4c635c/6a0dcf4 (+ push once authorized). To use remotely: launch app, set Server URL to https://forge.timevans.uk in the login sheet, admin password from task-9-deploy-report.md. Toolchain gotcha: `swift test` prints two runner summaries — the "0 tests" line is swift-testing; XCTest's real line says "Executed 27 tests".
**Remaining optional/deferred:**
- Gitea mirror: needs Tim to register this Mac's pubkey in Gitea (gitea.timevans.uk) settings; then push via `-J root@77.68.99.134` jump (external 2222 firewalled).
- Art providers: SD_API_URL → Tailscale URL to his Mac's AUTOMATIC1111; MJ_BRIDGE_CMD → wrapper around `RPGS/Stormbringer/scripts/mj_bridge.py`.
- MinIO creds hardcoded `minioadmin` in compose (internal-network-only exposure; tidy in a future backend amendment).
- V2 sync design decision parked: soft-deletes don't bump sync_version (updated_at auto-bumps via onupdate; backfillable).
Plan/spec/ledger live in MacMem repo: `docs/superpowers/{plans,specs}/2026-06-29-forgerpg-*.md`, `.superpowers/sdd/progress.md`.