Online game system for FATE Core.
  • Python 83.5%
  • TypeScript 14.6%
  • Jinja 1.6%
  • Makefile 0.2%
Find a file
Joshua Renker e50c7171fe feat(session): campaign framing, opening narration, and session continuity
- Campaign framer runs at first session open, storing hidden GM knowledge
  (dramatic question, opening scene, starting aspects, recommended NPCs)
  on new Game.campaign_notes; applied to the opening session and injected
  into narration context, never exposed to players
- Opening narration on session open: fresh scene for new games, a
  "previously" recap when resuming; freeform/opening narration modes with
  optional acting character
- Session-close continuity: consequences reconciled to character sheets,
  scene snapshot resumed next session, AI story-so-far summary
- Session open resets stress and Fate Points per FATE rules while carrying
  consequences over from the sheet
- New difficulty and session_summary prompt templates; narration template
  gains campaign direction / story-so-far / mode
- ModelConfig.temperature now optional (provider default when unset)
- Frontend: surface new WS events and session flow in play view, lobby,
  and knowledge base

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 22:20:01 -04:00
.agents/skills/code-documenter docs: add docstrings across backend 2026-05-30 08:44:24 -04:00
.claude docs: reconcile requirements with implementation; update phase status 2026-07-08 11:03:24 -04:00
.vscode Add game_membership repository and tests 2026-04-27 16:56:23 -04:00
alembic Move alembic out of root folder 2026-04-29 15:12:17 -04:00
backend feat(session): campaign framing, opening narration, and session continuity 2026-07-08 22:20:01 -04:00
frontend feat(session): campaign framing, opening narration, and session continuity 2026-07-08 22:20:01 -04:00
.env.example feat(auth): add oidc handling 2026-05-01 11:37:52 -04:00
.gitignore docs: reconcile requirements with implementation; update phase status 2026-07-08 11:03:24 -04:00
.pre-commit-config.yaml Add pre-commit hook for linting and format checking 2026-04-24 10:48:20 -04:00
.python-version Add project scaffolding 2026-04-13 22:12:28 -04:00
CLAUDE.md docs: reconcile requirements with implementation; update phase status 2026-07-08 11:03:24 -04:00
docker-compose.yml feat(dev): one-command docker compose stack; README usage guide 2026-07-08 11:04:22 -04:00
Makefile Move alembic out of root folder 2026-04-29 15:12:17 -04:00
pyproject.toml docs: reconcile requirements with implementation; update phase status 2026-07-08 11:03:24 -04:00
README.md feat(dev): one-command docker compose stack; README usage guide 2026-07-08 11:04:22 -04:00
reconciliation.md docs: reconcile requirements with implementation; update phase status 2026-07-08 11:03:24 -04:00
requirements.md docs: reconcile requirements with implementation; update phase status 2026-07-08 11:03:24 -04:00
skills-lock.json docs: add docstrings across backend 2026-05-30 08:44:24 -04:00
uv.lock docs: reconcile requirements with implementation; update phase status 2026-07-08 11:03:24 -04:00

fate-game-system

An implementation of the FATE Core tabletop RPG system with a multiplayer web front end. FastAPI backend, React/TypeScript frontend, real-time play over WebSockets, and AI-driven GM narration.

Prerequisites

  • Python (managed via uv)
  • Node.js 18+ / npm
  • Docker (only if you want the PostgreSQL dev database; SQLite works without it)

Setup

uv sync                      # install Python dependencies
cd frontend && npm install   # install JS dependencies

Create your environment file:

cp .env.example .env

Then edit .env:

  • SECRET_KEY (required) — generate with openssl rand -hex 32
  • AI provider keys (optional) — set ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY, or run a local Ollama and set OLLAMA_BASE_URL. Without a configured provider everything works except AI narration (the game emits a "narration unavailable" notice instead).

Running (Docker — one command)

make docker-start   # postgres + backend (:8000) + frontend (:5173)

This starts all three services with the source bind-mounted and hot reload active on both backend and frontend. Database migrations are applied automatically when the backend container starts. Open http://localhost:5173 when docker compose ps shows all services up (the first start installs dependencies inside the containers, so give it a minute; docker compose logs -f backend frontend shows progress).

make docker-stop shuts everything down; data persists in the postgres volume. make docker-clean removes the volumes too.

Running (on the host, without Docker)

Alternative if you prefer separate terminals. Point DATABASE_URL in .env at SQLite (sqlite+aiosqlite:///./dev.db) or at a running postgres (docker compose up -d postgres), then:

make migrate        # apply database migrations
make dev-backend    # FastAPI on http://localhost:8000 (OpenAPI docs at /docs)
make dev-frontend   # Vite on http://localhost:5173 (proxies API + WS to :8000)

First-time walkthrough

  1. Register — the first account created automatically becomes the system admin.
  2. Enable AI (optional) — go to Admin (top nav):
    • enable the provider(s) you have keys for,
    • under AI Module Defaults, pick a provider + model id for at least narration (e.g. anthropic / claude-sonnet-4-20250514),
    • save.
  3. Create a character — Dashboard → New Character. The form validates the FATE Core skill pyramid and refresh/stunt budget live.
  4. Create a world (optional) — lore and locations show up in the play view's knowledge base panel.
  5. Create a game — choose Solo to play by yourself with the AI as narrator, or Multiplayer to invite others by username from the lobby.
  6. Lobby — assign your character to your seat, then Open Session & Play.
  7. Play — the three-panel view:
    • Left: knowledge base (scene, party sheets with live stress/fate points, NPCs, world lore, rules reference)
    • Center: game feed. Pick an action type + skill, set the opposition, toggle aspect invocations (green dot = free invoke, otherwise 1 fate point), describe your intent, and Roll. Rolls resolve through the rules engine, results and dice are shown inline, and the AI narrates.
    • Attacks against another character trigger the absorption flow on the target's screen: choose a stress box and/or consequence, or concede.
    • Right (multiplayer only): private player chat. Never sent to the AI.
    • GMs get a toolbar to prompt players, post rulings, change scenes (with scene aspects), and close the session.

Testing & code quality

make test          # backend test suite
make test-engine   # rules engine tests only
make cov           # coverage report
make check         # black + isort + flake8
cd frontend && npm run typecheck   # TypeScript
cd frontend && npm run build       # production build

Note: the WebSocket integration tests (backend/tests/websocket/test_ws_*.py) are currently skipped — the Starlette TestClient harness hangs intermittently in full-suite runs. The WS layer is exercised manually through the running app; reworking that harness is an open task.

Architecture notes

  • backend/app/engine/ — pure, synchronous FATE Core rules engine. No I/O.
  • backend/app/websocket/pipeline.py — the session pipeline: validate → engine → DB commit → broadcast → AI narration (streamed). State is always persisted before the AI is called.
  • Player chat is excluded from AI context structurally (the narration context builder only reads the game channel).
  • Transient per-session state (stress, consequences, fate points) lives on GameMembership.session_state; character sheets are never mutated by play.

See CLAUDE.md for conventions and requirements.md for the full specification.