Files
spelljammer-ships/App plan.md
Bas van Rossem 047d4e9776 docs: add comprehensive README with testing and deployment instructions
Includes development setup, Docker deployment, API reference, WebSocket events,
troubleshooting guide, and curl-based test script. Updates App plan.md with
implementation completion status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:31:42 +01:00

8.7 KiB
Raw Permalink Blame History

Heres a concrete, handoff-ready plan you can give to another AI to implement a first version (v1) of a Spelljammer Ship Tracker with multi-ship CRUD, constrained editing, SQLite persistence, and <1s live updates.


0) Product goal (v1)

A small phone-friendly web app where your group can:

  • Create/select/delete ships (delete requires confirm modal)
  • View & edit core ship stats + current weapons on the ship
  • See changes propagate to all connected clients within ~1s
  • Persist “real data” server-side in SQLite (ship data + weapons + optional reference content)
  • Show a read-only Rules/Reference screen for battle lookups

No authentication. Anyone can edit anything.


Option A (recommended): Node.js + Fastify + Socket.IO + SQLite (better-sqlite3) + React (Vite)

  • Fast to implement, great websocket support, easy Docker build.
  • Socket.IO simplifies reconnects & room-based updates (per ship).
  • better-sqlite3 is reliable and fast for low-concurrency apps.

Option B: Node.js + Express + ws + SQLite (also fine, but more manual reconnection & rooms logic)

Either way: single Docker Compose with one container; SQLite is a mounted file.


2) Architecture

Backend

  • REST API for CRUD and initial state fetch.

  • WebSocket (Socket.IO) for real-time updates:

    • Clients join a “ship room” when viewing a ship.

    • When any field changes, server:

      1. validates & persists to SQLite
      2. broadcasts an update event to that room

Frontend

Phone-first layout, 34 screens:

  1. Ship List (create/select/delete)
  2. Ship Dashboard (core stats + weapons list)
  3. Edit Weapon (or inline editing)
  4. Rules/Reference (read-only)

Use constrained controls:

  • numeric steppers/sliders for numbers
  • dropdowns for enums (size category, maneuverability class, etc.)
  • toggles for booleans
  • “Add weapon” form with select + numeric inputs

3) Data model (SQLite)

Implement migrations on startup.

Tables

ships

  • id TEXT (uuid, PK)

  • name TEXT (unique-ish, not required but recommended)

  • Core stats (examples; adjust to your desired fields):

    • hull_current INTEGER NOT NULL
    • hull_max INTEGER NOT NULL
    • armor_current INTEGER NOT NULL
    • armor_max INTEGER NOT NULL
    • ac INTEGER NOT NULL
    • con_save INTEGER NULL (or con_score)
    • speed INTEGER NULL
    • maneuver_class TEXT NULL (enum: A/B/C/D/E/F/S per your rules)
    • size_category TEXT NULL
  • notes TEXT NULL

  • updated_at INTEGER (unix ms)

weapons

  • id TEXT (uuid, PK)
  • ship_id TEXT (FK -> ships.id, indexed)
  • name TEXT NOT NULL
  • type TEXT NULL (optional enum: siege/arcane/etc.)
  • attack_mod INTEGER NULL
  • damage TEXT NULL (freeform like “3d10”)
  • range TEXT NULL
  • ammo_current INTEGER NULL
  • ammo_max INTEGER NULL
  • status TEXT NULL (enum: ok/damaged/disabled)
  • notes TEXT NULL
  • sort_order INTEGER NOT NULL default 0
  • updated_at INTEGER

rules_pages (optional; for read-only reference)

  • id TEXT PK (e.g., “battle”, “ship-stats”)
  • title TEXT
  • content_markdown TEXT
  • updated_at INTEGER

Keep rules content as markdown stored in DB or ship it as static markdown files in the frontend. For v1, static markdown is simplest.


4) API design (REST)

Ships

  • GET /api/ships → list ships {id, name, updated_at}
  • POST /api/ships → create {name, initialStats...} → returns full ship
  • GET /api/ships/:id → full ship + weapons
  • PATCH /api/ships/:id → update core stats (validated + constrained)
  • DELETE /api/ships/:id → delete ship + weapons

Weapons

  • POST /api/ships/:id/weapons → add weapon
  • PATCH /api/weapons/:weaponId → update weapon fields
  • DELETE /api/weapons/:weaponId → delete weapon

Validation rules (server-enforced):

  • current values must be 0 <= current <= max
  • max values must be >= 0
  • enums must be in allowed set
  • name length limits

5) WebSocket events (Socket.IO)

Client → Server

  • ship:join {shipId}
  • ship:leave {shipId}
  • ship:update {shipId, patch} // core stats patch
  • weapon:create {shipId, weapon}
  • weapon:update {weaponId, patch}
  • weapon:delete {weaponId}

Server → Client (broadcast to ship room)

  • ship:state {ship, weapons} // on join, or after major changes
  • ship:patched {shipId, patch, updated_at}
  • weapon:created {shipId, weapon}
  • weapon:patched {shipId, weaponId, patch, updated_at}
  • weapon:deleted {shipId, weaponId}

Implementation approach:

  • On any mutation, server writes to DB then emits event.
  • On join, server emits current ship:state.
  • Clients apply patches locally; on reconnect, re-join and refresh state.

Conflict model:

  • Last write wins is fine (your requirement). Keep it simple.

6) Frontend UI details (phone-first)

Screen 1: Ship List

  • List cards: name + “last updated”

  • Buttons:

    • “Create ship” (modal with name + optional initial stats)
    • Tap card to open
    • Delete icon → confirm modal (“Delete ship X?”)

Screen 2: Ship Dashboard

Sections:

  1. Vitals

    • Hull: current/max (stepper)
    • Armor: current/max
    • AC (stepper)
  2. Mobility

    • Speed (stepper)
    • Maneuver class (dropdown)
    • Size category (dropdown)
  3. Weapons

    • List weapons with quick fields (ammo, status)
    • Tap weapon to open “Weapon details”
    • “Add weapon” button
  4. Notes

    • Multi-line text

Include a top bar:

  • Ship name
  • “Rules” link/button
  • “Back to ships”

Screen 3: Weapon Details (or modal)

Constrained inputs for ammo/status/attack_mod. Freeform for damage/range/notes.

Screen 4: Rules/Reference

  • Static markdown viewer with headings + quick links
  • Optional “search within rules” (client-side filter)

7) Persistence + backups

  • SQLite file at /data/spelljammer.sqlite
  • Docker volume mount that path.
  • Optional: periodic backup script/cron outside app (copy DB file nightly).

8) Docker Compose (single service)

  • Build a single image that serves both:

    • backend API + websockets
    • frontend static files
  • Expose one port (e.g., 3000) to Caddy reverse proxy.

Example structure (for the implementing AI):

  • /server Fastify app
  • /web React app build output served by server (or Caddy, but keep simple)

9) Implementation steps (what the AI should do in order)

  1. Create repo scaffold with /server and /web

  2. Implement SQLite schema + migrations

  3. Implement REST endpoints + validation

  4. Add Socket.IO with rooms:

    • join/leave
    • mutation events that persist + broadcast
  5. Build React UI:

    • routing for screens
    • state store (zustand or simple context)
    • socket connection + reconnection handling
  6. Wire CRUD flows:

    • list → select → join room → render state
    • edit fields → emit update → optimistic UI
  7. Add delete confirmation modal

  8. Add rules markdown page (static)

  9. Dockerize + compose + test behind Caddy


10) Acceptance criteria checklist

  • Create ship, appears in list, persists after restart
  • Select ship, see dashboard with core stats + weapons
  • Delete ship requires confirm, removes weapons too
  • All edits propagate to another browser within <1s
  • App is usable on phone (single column layout, large touch targets)
  • SQLite file persists via mounted volume
  • WebSocket reconnect works (refresh state on reconnect)
  • No auth, no user accounts

11) “Reference guide” integration (v1)

Since you want read-only rules “for during battle”: Start with a curated markdown page that copies only the snippets you frequently need (e.g., maneuverability classes, hull/armor notes, weapon usage, combat steps). You can refine later.

(If you want, I can extract a concise “Battle Reference” page from your PDF and format it as markdown for the AI to drop into /web/src/rules/battle.md.)


12) v1 Implementation Status

Status: v1 COMPLETE (implemented 2026-02-19)

All 14 commits have been implemented:

  1. Project scaffold (Fastify + Vite + TypeScript)
  2. SQLite connection + migration system
  3. Ship REST API with Zod validation
  4. Weapon REST API with validation
  5. Socket.IO with rooms + real-time events
  6. React routing + layout (mobile-first dark theme)
  7. Zustand stores + Socket.IO client
  8. Ship List page with create modal
  9. Ship Dashboard (vitals + mobility)
  10. Weapons section (add/edit/detail modals + notes)
  11. Delete confirmation polish
  12. Rules/Reference page (collapsible battle reference)
  13. Dockerfile + docker-compose
  14. README with testing + deployment docs

See README.md for full setup, testing, and deployment instructions.