No description
  • Python 97.6%
  • Shell 2.4%
Find a file
Claudio Maradonna efb68deca2
docs(readme): landing page that delegates, demo outputs re-derived
The README had grown to 297 lines written before docs/ existed, so it
carried the log format, the seat wire protocol and the licensing
rationale that PLAYTEST.md, SESSION-LOG.md and LICENSES.md now own. It
also opened with a development-status paragraph and closed with a
roadmap table of five rows reading "done" — a changelog, not a README.

Restructured as a landing page, designer first then developer: what it
is, `init` and the nine-stage design loop (absent from the README
entirely, though docs/DESIGN-LOOP.md is the designer's whole story),
the replay demo, a compact how-it-works band, the six gates, then a
commands table and a documentation index that make the delegation work.

Three of the four demo outputs were wrong rather than merely stale, and
are now re-derived from the commands the README documents:

  ROUNDS 3->2         claimed DIVERGED, is ILLEGAL at decision 9
  TRADE_ORE_COST 3->1 claimed ILLEGAL, is OK — nobody traded
  final scores        claimed {p1: 8, p2: 3}, are {p1: 3, p2: 9}

The plain-DIVERGED case is now GATHER_ORE 2->3, a real state divergence
at decision 2. The TILE_POINTS 5->9 honesty beat is kept: verified
correct, no worker reaches `build` in that session. Every other figure
is re-derived too — 417 tests, six gates, 13 wiki entries, 64 files
from init.

The 40-line rulebook_check guarantee statement moves to
docs/ARTIFACTS.md, which previously ended by pointing back at the
README for it; that pointer is inverted and its gate list grows from
four to six. ARTIFACTS.md is vendored into scaffolded games, which
never ship examples/toy_game, so the moved text drops its toy-game
reference.

Verify: 417 tests OK; attribution, invariants, mechanics_check,
rulebook_check, lint, kb_lint all OK; verify-agent-config.sh valid.

Claude-Session: https://claude.ai/code/session_01CCzWbCN91q51DN92pXTuHw
2026-09-01 11:00:38 +02:00
.claude docs(agents): re-adopt upstream policy at b9207e9 with automatic memory 2026-08-31 20:49:08 +02:00
.codex/agents docs(agents): re-adopt upstream policy at b9207e9 with automatic memory 2026-08-31 20:49:08 +02:00
docs docs(readme): landing page that delegates, demo outputs re-derived 2026-09-01 11:00:38 +02:00
examples feat(detectors): the three detect: contracts, analyze, and the toy game's declared exposures 2026-09-01 08:24:35 +02:00
foundry feat(upgrade): wholesale re-vendoring with hash-detected conflicts and kb merge by id 2026-09-01 09:05:56 +02:00
kb fix(kb): rewrap the two remaining hyphen-split compounds in runaway-leader 2026-09-01 08:36:51 +02:00
scripts docs(agents): re-adopt upstream policy at b9207e9 with automatic memory 2026-08-31 20:49:08 +02:00
templates/game feat(init): scaffold a game repository, green from birth, never overwriting 2026-09-01 09:04:46 +02:00
tests feat(upgrade): wholesale re-vendoring with hash-detected conflicts and kb merge by id 2026-09-01 09:05:56 +02:00
.gitattributes docs(agents): adopt the redesigned agent-foundry skeleton 2026-08-30 10:45:36 +02:00
.gitignore feat(foundry): play, sim and replay commands 2026-08-26 14:37:41 +02:00
AGENTS.md docs(analysis): ANALYSIS.md, the two rules carried into AGENTS.md, spec amendments after Phase 4 2026-09-01 08:29:32 +02:00
CLAUDE.md Initial commit 2026-08-26 11:14:31 +02:00
FOUNDRY_VERSION feat(version): FOUNDRY_VERSION file is the single version source 2026-09-01 08:58:34 +02:00
LICENSE feat: license the project, and enforce the map instead of documenting it 2026-08-26 16:00:06 +02:00
LICENSE.code feat: license the project, and enforce the map instead of documenting it 2026-08-26 16:00:06 +02:00
LICENSE.vendor docs(agents): adopt the redesigned agent-foundry skeleton 2026-08-30 10:45:36 +02:00
LICENSES.md feat(templates): the scaffolded game's author-owned starter files 2026-09-01 09:01:13 +02:00
NOTICE.md feat: license the project, and enforce the map instead of documenting it 2026-08-26 16:00:06 +02:00
README.md docs(readme): landing page that delegates, demo outputs re-derived 2026-09-01 11:00:38 +02:00

Board Game Foundry

A precise instruction set plus a minimal runnable harness for designing board games with AI agents. It exists so that a playtest becomes a regression fixture instead of an anecdote: every session — played by a scripted policy, by a language model, or by a person at a terminal — is recorded, and any recorded session can be replayed against changed rules to answer one question honestly.

Would this rules change have altered that game?

Version 0.1.0. Python 3.11+ and a checkout; no installation, no dependencies.

Start a game

python3 -m foundry init ../my-game
cd ../my-game && git init && git add -A && python3 -m foundry gates

init writes 64 files: the vendored harness, docs and wiki, plus the starter files you own — game/engine.py, game/config.py, game/policies.py, mechanics/, rulebook/ and a passing test. All six gates are green from the first commit, so the first failure you ever see is one you caused. It never overwrites an existing file, and --adopt fits it around a repository that already exists. Later, upgrade re-vendors from a newer foundry and reports locally edited files instead of discarding them.

Then the design loop — nine stages, each with an exit criterion rather than a feeling, ordered so the cheap checks run before the expensive ones:

Stage Done when
1 Pitch One paragraph names the tension, the player count, the length target
2 Shortlist kb_refs chosen with knob values recorded, or novel declared
3 Core loop One turn written end to end in mechanics/, playable on paper
4 Numbers Every tunable in numbers.md with a first value and a rationale
5 First playable foundry gates green against a real engine
6 Simulate sim, sweep, search, analyze clean
7 LLM seats A model given only rulebook/ plays a legal game to completion
8 Human table A recorded session with note lines; divergences reviewed
9 Rulings Every ambiguity raised has a dated RULING-###

Stage 6 precedes stage 8 by rule: an evening at the table is the most expensive probe the loop has, and it should never be spent discovering what two hundred seeded sessions would have shown for free. docs/DESIGN-LOOP.md gives each stage in commands.

Why a playtest becomes a fixture

python3 -m foundry play --seed 42 --seat p1=policy:first --seat p2=policy:builder
python3 -m foundry replay runs/42.jsonl

Every output below is real, from the bundled toy game at seed 42.

42.jsonl: OK        12 decisions, hashes match

Now change a rule in examples/toy_game/config.py and replay that same recording. Raising TILE_POINTS from 5 to 9 still reports OK — and that is the honesty the tool exists for, not a bug. Nobody placed a worker on build in that session, so a change to what tiles are worth genuinely did not alter it. Changes that did alter it are named precisely:

GATHER_ORE 2 -> 3
42.jsonl: DIVERGED  decision 2 (p2): state differs from the recorded session

ROUNDS 3 -> 4
42.jsonl: DIVERGED  the recorded session ended after decision 12 (p2) with
                    scores {'p1': 3, 'p2': 9}, but under the current rules
                    the game is not over

ROUNDS 3 -> 2
42.jsonl: ILLEGAL   decision 9 (p1): {'kind': 'place', 'space': 'gather'} is
                    no longer a legal action
Verdict Meaning
OK The rules still produce the recorded game.
DIVERGED Your change altered this game. The report names the decision where it first differs.
ILLEGAL A recorded action is no longer a legal move — the change removed an option someone actually used.

replay exits non-zero on DIVERGED or ILLEGAL, so a corpus of recorded sessions works as a CI gate. Divergence is a feature: it tells you exactly which recorded sessions — including ones played by real people — your change would have altered.

How it works

A game is any module exposing nine functions. Duck-typed, no base class to inherit; examples/toy_game/engine.py is the executable reference.

initial_state(rng, setup) -> State      legal_actions(state) -> [Action]
to_move(state) -> seat                  apply(state, action, rng) -> State
is_terminal(state) -> bool              scores(state) -> {seat: int}
observation(state, seat) -> dict        serialize(state) -> dict   # canonical
render(state, seat) -> str

A seat is anything with choose(view) -> Decision. All three kinds share one code path and produce the same log, which is what makes a human-played session replayable at all:

--seat p1=policy:greedy     # a deterministic function in the game's policies.py
--seat p2=human             # a person at the terminal; 'note <text>' records a remark
--seat p3=cmd:./decider.py  # any external process — this is how an LLM plays

One rule matters more than the rest: game code must never import random, read the clock, or let unordered-set iteration reach state. The harness owns the only RNG and passes it in explicitly. Violating this silently destroys every session you have ever recorded, which is why the harness ships its own splitmix64 generator rather than using random — a recorded session must replay identically on any future interpreter.

The gates

mechanics/ is the source of truth — one file per system, every tunable in numbers.md with a stable id and a reason. rulebook/ is derived from it and never hand-edited. config.py transcribes the numbers by hand, citing each id. Six committed gates keep them and the wiki from drifting apart:

python3 -m foundry gates
Gate Asserts
attribution Every tracked file matches a licensing rule; every .py keeps its SPDX header
invariants Properties that must hold at every state, over generated games
mechanics_check numbers.mdconfig.py is a bijection with equal values
rulebook_check No numeral contradicts the id cited beside it, and no rule reaches the code without reaching the text a player reads
lint Bare numbers, missing front matter, dangling rulings, any reach for random or the clock
kb_lint Every wiki entry matches its schema, every kb_refs citation resolves, kb/INDEX.md is current

What a green run does not prove is worth stating, because it is easy to read more into a gate than it earned: these check that the three artifacts agree, not that the sentence around a number is true. The exact boundary — paragraph-scoped numerals, internal: tunables, the not-a-rule escape hatch — is in docs/ARTIFACTS.md.

Commands

Command Does More
play Run one session and log it PLAYTEST
replay Verify recorded sessions: OK / DIVERGED / ILLEGAL SESSION-LOG
sim Run a batch and summarise ANALYSIS
sweep One batch per value of one tunable, patched in memory ANALYSIS
search Optimal-line search for one seat — proven optimal vs best-found ANALYSIS
analyze Run the pitfall detectors the game's mechanics cite ANALYSIS
gates Run the six committed quality gates ARTIFACTS
kb Knowledge base tools KB
init Scaffold a game repository from this foundry UPGRADING
upgrade Re-vendor a game repository from a newer foundry UPGRADING

The analysis commands share two rules: honest labels — proven optimal only when the space was exhausted, a truncated search reports best-found with its bounds — and traceable numbers, every figure naming the committed command that produced it.

Documentation

Document Covers
DESIGN-LOOP The nine stages and their exit criteria, in commands
ARTIFACTS mechanics/, rulebook/, config.py and exactly what each gate asserts
PLAYTEST The engine protocol, the three seat kinds, the determinism rules
SESSION-LOG The log records, and the replay contract's edge cases
ANALYSIS sim, sweep, search, analyze and the two rules they share
KB The wiki's card schema, citations and derived index
UPGRADING What init vendors and how upgrade reports conflicts
LOCALRECALL The project-memory contract for coding agents

The full design is in docs/superpowers/specs/2026-08-26-boardgames-foundry-design.md.

Layout

FOUNDRY_VERSION   the version init stamps and upgrade rewrites
foundry/          generic harness — never game-specific
  rng.py          splitmix64; the only source of randomness
  hashing.py      canonical JSON + sha256 state hashing
  protocol.py     the engine contract and its validator
  seats.py        policy, human and external-command seats
  session.py      JSONL log writer and reader
  runner.py       drives one session
  replay.py       OK / DIVERGED / ILLEGAL
  cli.py          the ten subcommands above
  gates.py        the six committed gates and the enforced license map
  authoring.py    numbers/rulebook/config parsers behind the authoring gates
  invariants.py   protocol-level properties over generated games
  kb.py           entry schema, derived index, kb_lint
  analysis.py     in-memory config patching, batches, traced sessions
  search.py       budgeted exhaustive search; proven optimal vs best-found
  detectors.py    the pitfall detectors the wiki's detect: fields contract
  scaffold.py     init: vendoring, stamping, --adopt
  upgrade.py      re-vendoring with hash-detected conflicts
templates/game/   the author-owned starter files init copies
examples/toy_game/  a small worker-placement game: the reference and the fixture
kb/               the wiki: 13 entries — mechanics, patterns, pitfalls, teardowns
docs/superpowers/   the design spec and the implementation plan

Development

python3 -m unittest discover -s tests -t .              # 417 tests, under two seconds
python3 -m foundry gates --game-root examples/toy_game  # all six committed gates
scripts/verify-agent-config.sh                          # the agent policy is well-formed

The foundry's own root has no mechanics/, which is why its gate run passes --game-root; a scaffolded game repository runs foundry gates bare.

AGENTS.md is the runtime policy for coding agents working in this repository; read it first. Its shape comes from Agent Foundry, whose validator is vendored as scripts/verify-agent-config.sh. That is static validation only: it never contacts a memory service, so it proves the policy is well-formed, not that the deployment behind it is ready.

Licensing

Two halves, because a single instrument fits neither. Prose — docs/, kb/, README.md, AGENTS.md — under CC BY-NC-SA 4.0. Code — foundry/, examples/, tests/ — under GPL-3.0-or-later. Four files vendored from Agent Foundry keep their upstream MIT terms.

Two consequences stated plainly rather than buried: CC BY-NC-SA is not OSI open source — the non-commercial clause disqualifies it by definition, so what the prose half offers is source-available with share-alike — and the two licenses are mutually incompatible, so no file may be under both.

The map is enforced rather than merely documented: attribution fails when a tracked file matches no rule. Map, reasoning and vendored files are in LICENSES.md; the paste-ready attribution block is in NOTICE.md.

© 2026 Claudio Maradonna