Files
Dimension-47/.planning/ROADMAP.md

20 KiB
Raw Blame History

Roadmap: Dimension47 — Milestone "PWA-Companion"

Overview

Diese Roadmap baut auf der bestehenden Dimension47-Codebase auf (Auth, Kampagnen, Charakterbogen mit HP/Zuständen/Inventar/Talenten/Aktionen/Alchemie/HTML-Export, Equipment-DB mit 5.482 Items, WebSocket-Sync, Battle-MVP, GM-Library — alles bereits geliefert) und liefert die nächste Ausbaustufe: regelkonformes PF2e-Level-Up, installierbare PWA mit Offline-Read, Web-Push für GM-Pings, getrennter Battle-Display-Mode für den eingelassenen Tisch-Bildschirm, In-App-Würfler mit Chat, GM-Live-Cockpit und Obsidian-Vault-Browser. Sieben Phasen in Forschungs-empfohlener Reihenfolge: Level-Up beginnt unabhängig und etabliert Test-Disziplin und WebSocket-Kanal-Trennung als Querschnittsmuster, danach baut PWA-Foundation die Service-Worker-Basis für Push und Vault-Offline-Cache, anschließend laufen Push, Würfeln/Chat und Battle-Ausbau parallel-möglich, und am Ende konsolidieren GM-Live-Tools und Vault.

Phases

Phase Numbering:

  • Integer phases (1, 2, 3): Planned milestone work
  • Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)

Decimal phases appear between their surrounding integers in numeric order.

  • Phase 1: Level-Up (PF2e regelkonform) - Charakter steigt im Wizard regelkonform mit allen Wahlen auf, Werte stimmen automatisch
  • Phase 2: PWA Foundation - App ist installierbar, der eigene Charakterbogen ist offline lesbar, Updates blockieren keine Battle-Session
  • Phase 3: Web Push - Spieler erhalten gezielte Push-Benachrichtigungen vom GM auf installierten Geräten
  • Phase 4: Würfeln & Chat - Server-autoritatives PF2e-Würfeln und Echtzeit-Chat mit Roll-Embeds pro Kampagne und Battle
  • Phase 5: Battle Multi-Screen Ausbau - Eigener Display-Mode fürs Tisch-Display, Initiative-Tracker, Token-Effekte, sicher getrennt von GM-Sicht
  • Phase 6: GM Live-Tools - GM steuert HP/Zustände/Items/Geld der Spieler-Charaktere live aus einem Cockpit-Panel und sendet gezielte Pings
  • Phase 7: Obsidian Vault (Read-only) - Vault-Browser mit Markdown, Wikilinks, Bildern, Suche und Offline-Cache der zuletzt gelesenen Notes

Phase Details

Phase 1: Level-Up (PF2e regelkonform)

Goal: Spieler und GM heben Charaktere regelkonform und nachvollziehbar auf das nächste Level — alle PF2e-Wahlachsen werden im Wizard angeboten, Voraussetzungen werden geprüft, abgeleitete Werte werden automatisch neu berechnet, und ein bestätigter Aufstieg ist atomar persistiert. Depends on: Nothing (first phase) Requirements: LVL-01, LVL-02, LVL-03, LVL-04, LVL-05, LVL-06, LVL-07, LVL-08, LVL-09, LVL-10, LVL-11, LVL-12, LVL-13, LVL-14, LVL-15 Success Criteria (what must be TRUE):

  1. Spieler startet auf seinem Charakter "Stufe steigen", durchläuft einen Wizard mit allen für sein Level relevanten Wahlpunkten (Boost, Klassentalent, Fertigkeitstalent, Allgemein-Talent, Skill-Increase, Ancestry-Talent, Klassenmerkmal — je nach Level) und sieht in jedem Schritt nur Optionen, deren Voraussetzungen erfüllt sind, und Free-Archetype-Slots sind sichtbar, wenn aktiviert.
  2. Spieler kann den Wizard jederzeit abbrechen oder zurückgehen, ohne dass der Charakter bisher mutiert wurde — Charakterbogen außerhalb des Wizards zeigt unveränderten Vorher-Stand bis zur ausdrücklichen Bestätigung.
  3. Nach Bestätigung sind HP-Max, Save-Boni, AC, Klassen-DC und Wahrnehmung gemäß PF2e neu berechnet (Boost-Cap-bei-18 wird respektiert: bestehender Wert ≥18 → +1, sonst +2), und alle anderen Mitspieler sehen den neuen Level + neuen Stat-Block in Echtzeit über WebSocket.
  4. Spellcaster-Charaktere sehen nach dem Aufstieg korrekte Slot- und Cantrip-Progression gemäß ihrer Klasse/Tradition; spontane Caster sehen zusätzlich erhöhtes Repertoire-Limit.
  5. Bestätigtes Level-Up erzeugt einen nachvollziehbaren Historieneintrag (Snapshot-Vorher in JSON) und ist atomar persistiert — kein halbes Level-Up bei Fehler mitten im Commit. Plans: 5 plans

Plans:

  • 01-01-PLAN.md — Wave 0: Prisma schema + migration + partial unique index + Jest infrastructure proof
  • 01-02-PLAN.md — Wave 1: 5 pure-function lib modules (boost, skill-cap, prereq, recompute, applicable-steps) with full TDD
  • 01-03-PLAN.md — Wave 2: ClassProgression + ClassFeatureOption seed pipeline (Foundry pf2e + hand-curated overlays, 16 classes × 20 levels)
  • 01-04-PLAN.md — Wave 3: LevelingModule (REST + atomic commit transaction + WebSocket broadcast) + pathbuilder-import integration + integration tests
  • 01-05-PLAN.md — Wave 4: React wizard against UI-SPEC + character-sheet integration + human verification checkpoint

UI hint: yes

Spike Required: Voraussetzungs-DSL-Scope (LVL-09) und Free-Archetype-Slot-Restriktionen müssen vor der Implementierung geklärt werden. Die Discuss-Phase soll fragen: (a) Welche Voraussetzungs-Muster sind mechanisch evaluierbar (Skill-Rang, Talent-Besitz, Level, Klasse) versus Escape-Hatch-Warnung? Empfehlung: Warnung statt Hard-Block für nicht-evaluierbare Prereqs. (b) Welche Archetyp-Talent-Quellen sind nach der Dedication zulässig — strikt nur der gewählte Archetyp oder beliebige Archetyp-Talente (Pathbuilder-Verhalten)?

First-Phase Note: Phase 1 ist die ERSTE Phase des neuen Milestones. Hier werden zwei Querschnittsmuster etabliert, die alle nachfolgenden Phasen erben:

  • Test-Disziplin: Reine Funktionen (Boost-Cap-Logik, Skill-Increase-Cap, Voraussetzungs-Evaluator, Recompute-Formeln) werden mit Unit-Tests gesichert. Die existierende Codebase hat null Tests; Pitfall-Mapping zeigt Boost-Cap-bei-18 und Skill-Increase-History als typische "schleichende Korruption"-Bugs. Test-Net wird in dieser Phase aufgesetzt und in allen Folge-Phasen für reine Funktionen weitergeführt.
  • WebSocket-Channel-Trennung: level_up_committed ergänzt das bestehende CharacterUpdatePayload['type']-Union als sauberer neuer Kanal-Typ. Atomare Transaktion + einzelner Broadcast (statt mehrerer kleiner Updates während Recompute) ist das Muster, das in Phase 4 (Dice-Gateway), Phase 5 (Battle-Sub-Rooms) und Phase 6 (GM-Live-Tools auditiert bestehende Char-Events) angewendet wird.

Phase 2: PWA Foundation

Goal: Die App ist als installierbare PWA verfügbar, der eigene Charakterbogen mit allen Tabs ist auch ohne Internet lesbar, Service-Worker-Updates passieren kontrolliert per User-Bestätigung und niemals während einer aktiven Battle-Session, und es gibt keinen Cache-Leak zwischen User-Logins auf dem gleichen Gerät. Depends on: Phase 1 (Level-Up touch-points an CharactersService/CharactersGateway sind abgeschlossen, bevor Caching-Layer drüberkommt) Requirements: PWA-01, PWA-02, PWA-03, PWA-04, PWA-05, PWA-06, PWA-07, PWA-08, PWA-09, PWA-10 Success Criteria (what must be TRUE):

  1. Spieler installiert die App auf seinem Android-Handy via "Zum Startbildschirm" (automatischer Prompt) oder auf seinem iPhone via geführter Anleitung im App-Overlay (Safari-Schritte) und sieht danach das Dimension47-Icon auf dem Home-Screen mit korrektem Splash beim Öffnen.
  2. Spieler startet die App im Flugmodus oder ohne WLAN und sieht den zuletzt geladenen Charakterbogen vollständig — Inventar, Talente, Aktionen, Alchemie sind lesbar, ohne Login-Fehler oder kaputte Seite.
  3. Wenn ein zweiter User auf dem gleichen Browser/Gerät einloggt, sieht er nur seine eigenen Daten — keine Charakter- oder Kampagnen-Daten des Vorgänger-Users sind aus dem Cache abrufbar.
  4. Wenn während einer aktiven Battle-Session ein neues App-Update auf dem Server liegt, lädt der Tab nicht automatisch neu — der Update-Hinweis erscheint erst, nachdem die Battle-Session geschlossen ist, und der Spieler bestätigt das Update manuell.
  5. Wenn ein Charakter live über WebSocket aktualisiert wird (z.B. HP-Schaden), zeigt die App nach Reconnect/Reload sofort den frischen Wert — der gecachte alte Wert wird über postMessage-Invalidation aus dem Service-Worker-Cache entfernt. Plans: TBD UI hint: yes

Phase 3: Web Push

Goal: Eingeloggte Spieler abonnieren auf Wunsch Push-Benachrichtigungen pro Gerät, der GM kann gezielt einzelne Spieler oder die ganze Kampagne anpingen, das System sendet automatisch einen Battle-Turn-Alert, wenn ein Spieler dran ist, und abgelaufene Subscriptions werden serverseitig automatisch aufgeräumt. Depends on: Phase 2 (Service Worker muss existieren, damit der Push-Handler darin laufen kann) Requirements: PUSH-01, PUSH-02, PUSH-03, PUSH-04, PUSH-05, PUSH-06, PUSH-07, PUSH-08 Success Criteria (what must be TRUE):

  1. Spieler aktiviert in den Einstellungen "Benachrichtigungen aktivieren", erteilt im OS-Prompt die Erlaubnis, und das Gerät erscheint in seiner Subscription-Liste — er kann dieselbe Aktion auf einem zweiten Gerät wiederholen und beide Geräte werden separat abonniert.
  2. Wenn der GM "Ping an Spieler X" oder "Ping an Kampagne" auslöst, erhält der Spieler eine sichtbare Notification (auch wenn die App im Hintergrund oder geschlossen ist), und ein Tap auf die Notification öffnet die App auf dem Battle-, Charakter- oder Chat-Screen je nach Payload.
  3. Wenn die Initiative im Battle auf den Charakter eines Spielers wechselt, erhält dieser Spieler automatisch einen Push "Du bist dran" auf allen abonnierten Geräten, ohne dass der GM manuell auslösen muss.
  4. Wenn ein Spieler eine Subscription auf einem Gerät widerruft (Browser-Settings) oder das Gerät die Subscription rotiert, wird der zugehörige DB-Eintrag beim nächsten fehlgeschlagenen Send (410 Gone) automatisch gelöscht, ohne dass die Push-Pipeline für andere Subscriptions kaputtgeht.
  5. VAPID-Keys liegen persistiert in .env (VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT) und sind in .env.example als persistente App-Secrets dokumentiert mit klar formuliertem Rotations-Verfahren — bei Re-Deploy gehen Subscriptions nicht verloren. Plans: TBD UI hint: yes

Phase 4: Würfeln & Chat

Goal: Spieler und GM würfeln in PF2e-Notation server-autoritativ (kein Client-Tampering möglich), das Ergebnis ist live für alle Teilnehmer einer Kampagne und Battle sichtbar, PF2e-spezifische Crit-Mechaniken (Schaden-Würfel verdoppeln, Modifikatoren NICHT) und Degree-of-Success werden korrekt berechnet, und ein Echtzeit-Chat mit Markdown und Roll-Embeds läuft pro Kampagne und Battle. Depends on: Phase 2 (Offline-Read des Roll-Logs nutzt Service-Worker-Cache) Requirements: DICE-01, DICE-02, DICE-03, DICE-04, DICE-05, DICE-06, DICE-07, CHAT-01, CHAT-02, CHAT-03, CHAT-04, CHAT-05, CHAT-06 Success Criteria (what must be TRUE):

  1. Spieler tippt 1d20+7 (oder nutzt Quick-Buttons), das Ergebnis erscheint sofort in der Roll-Liste mit Wer/Was/Wann/optionaler DC, ist server-seitig persistiert und wird live an alle Mitspieler in derselben Kampagne und Battle-Session gepusht — eine manipulierte Client-Payload wird vom Server abgewiesen.
  2. Bei einem Schaden-Roll mit Crit (2d6+4 Crit) zeigt die App 4d6+4 als Berechnung — Würfel-Anzahl verdoppelt, Modifikator unverändert; bei einem Check gegen DC mit ±10-Regel zeigt die App den korrekten Degree-of-Success (Crit Success / Success / Failure / Crit Failure).
  3. Spieler postet eine Markdown-Nachricht in den Kampagnen- oder Battle-Chat, alle Mitspieler sehen sie sofort, und ein injizierter <img onerror=...> oder [click](javascript:...) wird vom Renderer neutralisiert (kein Skript-Auslösen, keine dangerouslySetInnerHTML-Lecks).
  4. Spieler postet einen Roll in den Chat, das Ergebnis erscheint inline als Roll-Card im Chat-Verlauf (FK embedRollId von ChatMessage auf DiceRoll), und der Chat-Verlauf lädt paginiert beim Hochscrollen via Cursor.
  5. Wenn ein Spieler 30 Sekunden Verbindungsverlust hat und in dieser Zeit Rolls/Nachrichten passieren, sieht er nach Reconnect die fehlenden Einträge über REST-Refetch (nicht nur live-WebSocket) — Roll-Log und Chat-History sind in Postgres mit Composite-Index (campaignId, createdAt) und (battleSessionId, createdAt) performant abrufbar. Plans: TBD UI hint: yes

Phase 5: Battle Multi-Screen Ausbau

Goal: Der Battle hat zwei sicher getrennte Sichten: ein GM-Steuerpanel auf dem Laptop und ein read-only Display-Mode für den eingelassenen Tisch-Bildschirm, wobei GM-only-Daten serverseitig gefiltert werden (nicht clientseitig versteckt). Initiative-Tracker zeigt sortiert "wer ist dran", Token-Effekte sind als Datenmodell modelliert und auf beiden Sichten sichtbar, und Token-Add/Remove laufen sauber als WebSocket-Events. Depends on: Phase 4 (Dice-Anbindung in Initiative-Card und Roll-Embeds in Chat sind nutzbar; Sub-Room-Pattern profitiert vom Test-Net aus Phase 1) Requirements: BAT-01, BAT-02, BAT-03, BAT-04, BAT-05, BAT-06, BAT-07, BAT-08, BAT-09, BAT-10, BAT-11 Success Criteria (what must be TRUE):

  1. GM klickt "Display starten", die Display-Route /battle/:id/display öffnet sich (auf zweitem Monitor / Tisch-Display) mit großschriftigem Querformat-Layout — keine Drag-Handles, keine GM-Sidebar, keine GM-Notizen sichtbar; bei socket.onAny(console.log) in den DevTools des Display-Clients erscheinen NULL GM-only-Felder im WebSocket-Frame.
  2. Display-Route ist mit einem kurzlebigen Display-Token authentifiziert, das vom GM ausgestellt wurde und an die Battle-Session gebunden ist — kopierte URL aus einem anderen Browser-Profil sieht nichts, abgelaufenes Token wird nach Battle-Ende vom Server abgewiesen.
  3. Initiative-Tracker zeigt eine sortierte Liste mit deutlichem "Wer ist dran"-Highlight (auch aus Tisch-Distanz lesbar), GM-"Nächster Zug"-Button rückt die Initiative weiter, und das Event broadcastet binnen ~1s an GM-Sicht, Display-Sicht und alle Spieler — keine inkonsistenten Zwischenzustände.
  4. GM kann pro Token Effekte hinzufügen, entfernen und aktualisieren (Name, Typ Buff/Debuff/Aura/Marker, optionale Dauer in Runden), und sowohl GM- als auch Display-Sicht zeigen sie als Pille/Badge am Token sowie in der Initiative-Karte; Effekte sind mit FK auf Token persistiert.
  5. GM fügt einen neuen Token hinzu oder entfernt einen — beide Aktionen broadcasten als saubere token:added/token:removed-WebSocket-Events (nicht als Query-Invalidate-Workaround), und alle Clients sehen das Update sofort. Plans: TBD UI hint: yes

Spike Required: Display-Token-Modell (BAT-03) muss vor Implementierung geklärt werden. Die Discuss-Phase soll fragen: (a) JWT-Variante mit battle:id-Claim, kurzem TTL und server-validierter Display-Route — wer stellt aus (GM-Klick "Display starten"), wo wird das Token transportiert (Query-Param, Header, Cookie), wann erlischt es (Battle-Ende-Hook, TTL-Default)? Empfehlung gemäß SUMMARY.md: Server stellt One-Shot-URL aus, GM klickt zum Generieren. (b) Tisch-Display-Hardware-Specs (Aspect-Ratio, Auflösung) müssen vor Phase-Ende vom User vorliegen, damit das Display-Layout korrekt skaliert (Pitfall #24).

Phase 6: GM Live-Tools

Goal: Der GM hat ein zentrales Cockpit-Panel, von dem aus er HP, Zustände, Items und Geld jedes Spieler-Charakters live setzen kann (über die bestehenden Char-WebSocket-Events, aber serverseitig auditiert auf GM-Rolle), und kann gezielt Push-/Chat-Nachrichten an einen Spieler oder alle Spieler senden. Destruktive Aktionen (HP=0, Item löschen) sind durch zweistufige Bestätigung abgesichert. Depends on: Phase 3 (Push für GM-Ping) + Phase 4 (Chat für gezielte Nachrichten) Requirements: GM-01, GM-02, GM-03, GM-04, GM-05, GM-06, GM-07, GM-08 Success Criteria (what must be TRUE):

  1. GM öffnet das Live-Tools-Panel im Kampagnen-Detail und sieht alle Spieler-Charaktere mit Quick-Actions — er klickt "Schaden 5" auf Charakter A, der HP-Wert geht live runter, der Spieler von Charakter A sieht den neuen Wert in Echtzeit auf seinem Charakterbogen.
  2. GM kann eine Condition (z.B. Frightened 2) auf einem Spieler-Charakter setzen, entfernen oder updaten — der Spieler sieht sie sofort im eigenen Conditions-Tab; gleicher Pfad gilt für Item-Übergabe (Suche aus Equipment-DB → Auswahl → live im Inventar des Spielers) und Geld-Anpassung (Credits live).
  3. GM klickt "Ping an Spieler X mit Nachricht 'Wirf Reflex DC 25'" — Spieler X sieht innerhalb von ~1s die Push-Notification (auch wenn App im Hintergrund) UND eine Chat-Nachricht im Kampagnen-Chat; Ping an "alle Spieler" erreicht alle gleichzeitig.
  4. Wenn GM eine destruktive Aktion auslöst (HP auf 0 setzen, Item löschen), erscheint ein Bestätigungs-Dialog, der den betroffenen Charakter und die Aktion explizit nennt — versehentlicher Klick ohne Bestätigung mutiert nichts.
  5. Wenn ein Nicht-GM-User versucht, einen GM-Live-Tool-Event über manuelle WebSocket-Payloads abzufeuern (z.B. fremden Charakter modifizieren), lehnt der Server ab — die bestehenden Character-WebSocket-Events sind serverseitig auditiert auf "Aktor ist GM dieser Kampagne" und Lücken (falls vorhanden) sind geschlossen. Plans: TBD UI hint: yes

Spike Required: Authorization-Audit der bestehenden Character-WebSocket-Events (GM-06) muss vor Implementierung der GM-UI passieren. Die Discuss-Phase soll fragen: Welche der existierenden Char-Events (character_update-Familie in characters.gateway.ts) prüfen heute schon "Aktor ist GM dieser Kampagne", welche prüfen nur "Aktor ist Owner"? FEATURES.md flaggt Lücken; vor dem Bauen einer GM-UI, die diese Events massenhaft auslöst, muss die Server-Seite konsistent sein.

Phase 7: Obsidian Vault (Read-only)

Goal: GM und Spieler lesen Markdown-Notes aus dem selbst-gehosteten Vault über einen In-App-Browser — Ordner-Navigation, Wikilinks zwischen Notes, Bild-Embeds, Volltextsuche, alles über die NestJS-Server-API geleitet (nie direkter Browser-Zugriff auf Vault-Credentials), und zuletzt gelesene Notes sind offline verfügbar. Depends on: Phase 2 (Service Worker für Offline-Note-Cache) Requirements: VAULT-01, VAULT-02, VAULT-03, VAULT-04, VAULT-05, VAULT-06, VAULT-07, VAULT-08, VAULT-09, VAULT-10 Success Criteria (what must be TRUE):

  1. Spieler navigiert im Vault-Browser durch eine Ordner-Tree-Struktur, öffnet eine Markdown-Datei und sieht sie gerendert mit GFM-Tabellen, Codeblöcken, Bildern (![[image.png]]) und Wikilinks ([[Note]] und [[Note|Alias]]); ein Klick auf einen Wikilink öffnet die verlinkte Note.
  2. Wenn zwei Notes denselben Basenamen haben, wird der mehrdeutige Wikilink dem User explizit als "mehrdeutig" angezeigt mit Auswahl-Liste — der Renderer sucht NICHT stillschweigend einen Treffer aus; ein zyklischer Embed (A → B → A) rendert einen Platzhalter "(Zyklus)" statt Stack-Overflow.
  3. Spieler nutzt die Volltextsuche und findet eine bekannte Note in einem Vault mit zumindest einem realistischen Inhalts-Volumen — Such-Endpoint ist server-seitig (Postgres FTS) und respektiert die JWT-Auth.
  4. Spieler liest eine Note online, geht offline (Flugmodus), öffnet die App erneut und kann dieselbe Note (und andere kürzlich gelesene Notes) weiterhin lesen — der Service-Worker-Cache (StaleWhileRevalidate, LRU) hat sie vorgehalten.
  5. Wenn ein User versucht, einen Pfad mit ../../etc/passwd über die Vault-API zu lesen, lehnt der Server ab (Path-Traversal-Guard); Vault-Credentials liegen ausschließlich in .env am NestJS-Server und sind im Browser-DevTools-Network-Tab nicht sichtbar. Plans: TBD UI hint: yes

Spike Required: Vault-Transport-Entscheidung (VAULT-01) muss vor Implementierung getroffen werden. Die Discuss-Phase soll fragen: Liegt der Obsidian-Vault auf derselben Maschine wie der NestJS-Server (Filesystem-Mount via fs/promises mit FsVaultProvider, einfacher und niedriger Latenz) oder auf einer separaten Maschine (Synology/Nextcloud, dann WebDAV-Proxy via webdav lib mit WebDAVVaultProvider)? Empfehlung gemäß SUMMARY.md: VaultProvider-DI-Interface, Default FsVaultProvider, WebDAVVaultProvider als swappable Alternative.

Progress

Execution Order: Phasen werden in numerischer Reihenfolge ausgeführt: 1 → 2 → 3 → 4 → 5 → 6 → 7. Negotiable parallelization (mit Bandwidth zur Hand): Phase 4 darf direkt nach Phase 2 starten (ohne Phase 3 abzuwarten); Phase 7 darf direkt nach Phase 2 starten (ohne Phase 5/6 abzuwarten). Hard dependencies bleiben: 2 vor 3, 2 vor 7, 3+4 vor 6.

Phase Plans Complete Status Completed
1. Level-Up (PF2e regelkonform) 0/5 Planned -
2. PWA Foundation 0/TBD Not started -
3. Web Push 0/TBD Not started -
4. Würfeln & Chat 0/TBD Not started -
5. Battle Multi-Screen Ausbau 0/TBD Not started -
6. GM Live-Tools 0/TBD Not started -
7. Obsidian Vault (Read-only) 0/TBD Not started -