clay-borg/decisions/ADR-0002-game-kernel.md

3.8 KiB
Raw Blame History

ADR-0002: Game-state kernel — survey approval and implementation decision

Date: 2026-07-31 Status: accepted Capability: game.kernel.authoritative-state Inputs: research/CB-RES-0001-game-kernel.md (approved), history/260731-game-kernel-{research,challenge,response}.md

Approval (InnerLoop step 2)

The CB-RES-0001 survey is approved. It passed one round of adversarial review: the challenge found one blocking overclaim (degradation attribution) and three significant gaps (memory methodology, omitted candidates OpenSpiel/Ludii, unsourced D3 ceiling); all were resolved with new measurements or corrections, none overturned the comparative conclusions. The candidate set is judged adequate; the baselines are trustworthy within their stated provenance (measured vs cited vs estimated), with the event-sourcing ceiling explicitly parity-capped.

Benchmark-to-beat, named: boardgame.io 0.50.2 per the survey's baseline table (throughput ~1,1001,900 moves/s degrading with history in every configuration; 120 deps/37 MB; ~36-LOC declarative game definition), plus the stipulated engineering target of ≥100,000 applied events/s with flat scaling (@100k within 10% of @5k) and bit-identical replay.

Decision (InnerLoop step 3): reimplement, assimilating patterns

Build the Clay-Borg semantic kernel as our own Rust implementation (cb-kernel + cb-events + cb-game-runtime) — not a wrapper around any surveyed system. Per the Clay-Borg rule, we own the semantics; what we assimilate from the field is patterns, each with a named source:

Assimilated pattern From Into
Declarative game definition (setup/moves/phases as data + pure functions) boardgame.io game-package rules API
Tooling-enforced determinism (not convention) Rune seeded RNG service, denied ambient time/random, replay double-run check in the scenario runner
Command → validate → events → fold, snapshots bound replay event-sourcing (cqrs-es pattern) kernel mutation pipeline
Information-state framing for imperfect information OpenSpiel per-player projection model
Hand-zone / object-visibility semantics TTS (pattern only) tabletop layer, later

Rejected alternatives: assimilate boardgame.io (inactive, JS-bound, history-degrading in best config — fails D2/D3/D4); build on OpenSpiel (right semantics, wrong layer — research kernel without projection/ networking/snapshot surface, heavyweight C++/Python boundary); ECS as kernel (bevy_ecs stays in the world layer per the blueprint).

Expected advantage per dimension (honest trades)

  • D3 speed: better. Flat per-event cost with snapshots; the 100k/s floor. Declared caveat: part of the margin over boardgame.io is language choice; the commitment that matters is flat scaling, and the event-sourcing comparison stays at parity until a Rust comparator is measured.
  • D2 weight: better. Target ≤20 transitive crates for the headless kernel (vs 120 npm packages), measured by M-D2-DEP/M-D2-LOC/M-D2-BLD.
  • D1 specification: parity, aiming better. Match boardgame.io's declarative compactness on the synthetic workload (≤~40 LOC equivalent); surpass via rule-to-scenario traceability (M-D1-COV), which no candidate has. Declared trade: we do not attempt Ludii/GDL-class generality — one game family first, canon only after a second concrete use.
  • D4 optionality: better. Port pattern with null/reference impls from day one, capability boundary WIT-expressible later, zero foreign types in canonical interfaces (M-D4-LEAK=0). No surveyed candidate offers this.

Gate

Per InnerLoop, no kernel implementation code existed before this ADR. Next steps: T05 (GROUND rules spec, derived from ground-game), T06 (kernel spec with acceptance metrics tied to these baselines), T07 (workspace + harness), T08 (code loop).