Files
game-kit/README.md
T
Eric WagonerandClaude Fable 5.1 33ac0ec808 The credibility pass on Hnefatafl, ported to the template: the ops scripts count traffic through one parser, the reports digest is one program, the plaintext-token fallback and its migration are gone, and the stylesheet holds each shared rule once
From the hnefatafl repo's pass of the same day, everything that touched a
kit-shared file. The visitors digest and the nightly rollup share
deploy/traffic.py, installed to /usr/local/lib/<slug>; the rollup writes
the finished-games count it computed behind "and False". pull-reports.sh
and the reports skill both use deploy/report-digest.ts. The seat line
requires its token hash and the start line its rules revision; the
migration for ledgers written before hashing goes with them. The route
table in server/src/index.ts lists every route; Report, ReportLine and
Tally are declared once in view.ts for both sides; exports nobody
imported are exports no more.

In the client: .small, the × that dismisses, and the frame of the
reading pages are in app.css once; the preferences panel shares the
report slip's modal shape; the room store gains seatEmpty and
seatUnheld, and the lobby and the join page read those instead of
three spellings of their own. The room's moved and awaiting fields
stay: the demo board reads them, and simultaneous rounds are the
contract.

The deploy README no longer describes a browser-only game; the visitors
skill no longer names a /play route; the reports skill's replay call
carries the room's options. The Slack channel id is passed in the
environment rather than filled in as a placeholder.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwFKMuQnPAEHJ5yA1q4orh
2026-09-23 20:12:37 -04:00

79 lines
3.8 KiB
Markdown

# game-kit
The shared infrastructure of Wiz-War and Waving Hands, extracted so the next
game starts with all of it in place: the hall, room codes and tables, the
Peanut Gallery (and the host's leave for it to talk), table talk, rematches,
seats that travel between devices by phrase, the keeper's bell, append-only
ledgers replayed through a
deterministic engine, the rules revision, the reports desk, the deploy
script with its determinism gate, nightly rollup and backup, Sentry with
Slack alerts, and the visitors, pulse and reports skills for Claude.
The game itself is one file. `src/lib/game/index.ts` implements `GameSpec`
(see `src/lib/game/spec.ts`); the server, the client store, the hall and
the ops tools never look past it. The template ships with a demo game,
High Card, so everything runs before you have written a line.
## Start a game
./new-game.sh <slug> "<Name>" [port] [domain]
That copies `template/` to `~/projects/<slug>`, fills the name, port and
domain in, initialises git, installs dependencies, and prints the steps
that remain: write the game, fill in the hall and rules and guide, create
the droplet and the Sentry project, deploy. `CONVENTIONS.md` is copied to
the new project as `docs/conventions.md`; it is the house style.
Run the demo locally from a new project:
npm run server # the game server on its port
npm run dev -- --host
## What is where
template/
src/lib/game/spec.ts the contract a game implements
src/lib/game/index.ts the demo game; replace it
src/lib/net/ client.ts (calls, held seats, socket), room.svelte.ts (the room store), talk.ts, view.ts
src/lib/components/ Hall, Lobby, TableTalk, ReportSlip, Board (demo; replace it)
src/routes/ the hall, /join/[code], /rules, /guide
server/src/ index (routes), rooms (game-agnostic), store (ledger), reports (the desk), ratelimit
deploy/ deploy, setup, Caddy, systemd, cron, gate + replay, backup, rollup, pulse, visitors, Slack alert, reports tools
.claude/skills/ <slug>-visitors, <slug>-pulse, <slug>-reports
Placeholders: `__SLUG__`, `__NAME__`, `__PORT__`, `__DOMAIN__` are filled by
`new-game.sh`; `__IP__`, `__SENTRY_DSN__`, `__SENTRY_PROJECT_ID__`,
and `__SENTRY_SLACK_INTEGRATION__` wait until those things exist.
## What the kit is, and is not
The model is copy and diverge. A new game starts as a copy of the template
and then goes its own way; the two games that came before it are not
instances of the template and were not rewritten to be. The kit holds the
reference copy of the shared parts and the conventions they follow, so a
third game starts from the best current version of everything.
Drift is meant to be visible, not prevented:
./drift.sh ../waving-hands # how far each shared file has wandered
`drift-map/<slug>.txt` pairs files when a game's layout differs from the
template's. When a game grows something every game should have (the
gallery and the reports desk were both born in one game and ported to the
other), bring it back here, with placeholders, so the next game inherits
it, and read the drift before assuming the kit has it.
The contract is simultaneous rounds: every seat that needs input submits,
then the round resolves; turn-at-a-time games fit as the case where one
seat needs input at a time. A game of single commands with out-of-turn
interruptions, Wiz-War's shape, does not fit and keeps its own server.
## The family
`family.json` lists every game of Kestrel's Hall: slug, name, address,
one-line pitch, player count and origin. Each game ships a copy as
`static/family.json` (the deploy script takes the kit's latest when the kit
is beside the game) and its about panel lists the others under *More games
in Kestrel's Hall*. Add a game here, then deploy the games; nothing
else needs to know.