From 18520fee224a49aeb54be1b7be53a45670525d7c Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Mon, 6 Jul 2026 15:36:21 -0500 Subject: [PATCH] docs: add Plan 3 design spec (persistence, editors, uploads) --- .../2026-07-06-persistence-editors-design.md | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-06-persistence-editors-design.md diff --git a/docs/superpowers/specs/2026-07-06-persistence-editors-design.md b/docs/superpowers/specs/2026-07-06-persistence-editors-design.md new file mode 100644 index 0000000..4eab54e --- /dev/null +++ b/docs/superpowers/specs/2026-07-06-persistence-editors-design.md @@ -0,0 +1,266 @@ +# Persistence, Secure Image Handling, and Scenario Editors Design + +## Purpose + +Plan 3 of the four-plan BattleForge build. Closes the gap between the pure-PHP `Domain` layer (Plans 1 and 2) and the in-browser experience by adding: + +- Anonymous-browser persistence of scenarios and in-progress matches (no user accounts, no cross-device, no server-side storage of game state). +- Server-rendered editor pages for teams and battlefields, with the domain's `Scenario` and `ScenarioValidator` enforcing every rule. +- A secure image-upload pipeline (content-sniffed, size-limited, dimension-limited, session-scoped). +- A minimal plain-PHP web stack with the cross-cutting security concerns the spec demands (CSRF, output escaping, content-validated uploads). + +## Success Criteria + +Plan 3 succeeds when a new user, without developer assistance and with no global state, can: + +1. Visit `http://localhost:8000/`, see a home page that lists their recent scenarios, and start a new one. +2. Use the team editor to build two teams of 3-6 units each, choosing archetypes, stats, abilities, names, and optional custom images. +3. Use the battlefield editor to choose dimensions, paint terrain, place deployment zones, and place an objective (when the victory condition requires it). +4. Validate the assembled scenario server-side; see clear errors when something is invalid. +5. Upload a custom image and have it appear in the team editor. +6. "Start a match" from a saved scenario and have the initial `MatchState` round-trip through the browser. +7. Refresh the page, lose the connection, or come back tomorrow and find their saved scenarios still in their browser (no data loss from the user's perspective). +8. Reject forged cross-site requests, accept only image types the spec allows, and never let a user access another user's uploads. + +## Out of Scope (deferred to Plan 4 or later) + +- Hot-seat battle interface: Plan 3 ships a placeholder page that reads the assembled `MatchState` from `localStorage` and renders a "Battle interface coming in Plan 4" stub. +- Bundled scenarios (the three ready-made scenarios in the spec): Plan 4. +- Real-time interaction, drag-and-drop, or any other JS surface beyond the single fetch-based grid editor and the small `localStorage` helper. +- Server-side storage of scenarios, matches, or any game state. +- User accounts, sessions beyond the CSRF cookie, or any per-user server-side state. +- Mobile-native, PWA, offline service workers, or any non-server-rendered path. +- Antivirus scanning or upload rate limiting. + +## In Scope + +### Persistence + +- Scenarios are JSON-serialized in the browser's `localStorage`, keyed as `scenario:{id}`. +- In-progress matches are JSON-serialized in `localStorage`, keyed as `match:current`. +- The server never sees a `Scenario` or `MatchState` after the editor POST completes. Each form submission is validated and acknowledged; the browser then writes to `localStorage` on its own. +- The home page lists scenarios by enumerating `localStorage` keys; no server call. +- "Resume in-progress match" reads `match:current` and shows a banner on the home page. + +### Team Editor + +- One form per scenario, with sections for: meta (id, name), team A, team B, victory condition. +- Each team section has 3-6 unit rows; rows are added and removed client-side (DOM manipulation, no fetch). +- Each unit row has: archetype dropdown, display name, image upload button + hidden URL field, four stat inputs (with `min`/`max` from the chosen archetype's template), and an abilities multi-select (limited to the chosen archetype's allowlist). +- The archetype JSON is served from `/assets/archetypes.json` and includes the four `ArchetypeTemplate` definitions. +- A new "Upload" form submits a single image to `POST /assets/upload` and writes the returned URL into the unit row's hidden `image` field. The unit-editor form is not submitted as part of the upload. +- Victory condition: a radio pair (`EliminateAll` / `HoldObjective`). When `HoldObjective` is selected, an input for `holdRoundsRequired` (1-10) appears. +- Submitting the form POSTs the full draft to `POST /scenarios/{id}/edit/team`. The server parses, builds a `ScenarioDraft`, runs `ScenarioValidator::validate()`, and on failure re-renders the form with errors inline. + +### Battlefield Editor + +- One page per scenario. Width/height inputs (8-16 each) at the top. +- Terrain palette: one button per `Terrain` enum value. The selected button is the "current paint." +- Grid: an HTML `` of `