Repository layout¶
This page maps gorge's source tree for a Go developer who has just cloned it: what each top-level directory holds, which way imports are allowed to run, and where a given kind of change belongs. The per-package detail (every package with its purpose, size and importers) is generated from the source on Packages; this page is the guided tour.
Verified 2026-10-21 against develop @ 513a2a0c3: git ls-files,
go list ./... and go run ./cmd/pkgmap (99 Go packages).
The tree at a glance¶
go run ./cmd/pkgmap at 513a2a0c3.Top-level paths¶
| Path | What it holds | Go packages |
|---|---|---|
engine/ |
The deterministic rules core: cards (the Forge-script compiler and card registry), state, decision, events (the event log and events.Apply), effects, rules (the engine, with sub-packages chars, combat, cost, pay, resolve, scriptfacts, trigmatch), view, seat (the Seat contract only), replay, deck. |
19 |
bots/ |
The bot registry (bots, linked by bots/all), the one heuristic policy (botpolicy), the default bot (heuristic), the hosted bots (bot, lethalpressure, sbtactical, azredeal, forgeheuristic) and their inference runtime (azmcts, gonet, netsimd, policynet, searchprobe, searchseat, sanity, spellbench/*, traceboard, explain: the per-decision notes a match record carries). |
22 |
server/ |
The reference server: gorged (importable: Main, Serve, the Extensions seam), host (tables, sessions, persistence) with host/httpapi (JSON + server-sent events) and host/manabrewhttp, protocol (the wire types the web client is generated from), manabrew (an alternate wire), divergence (human-vs-bot divergence records), matchrecord (the portable one-file record of a played game that gorged review serves), spellbench/* (the SpellBench v2 environment and agent adapters). |
30 |
testkit/ |
Public test support: testutil (the corpus registry, repo decks, the acceptance decks, feedback snapshot loading), bench (a policy-agnostic head-to-head game runner), paymirror (an A/B check of payment plans). |
4 |
cli/ |
The command-line layer: GNU-style flags, environment variables and JSON config files on the standard library. | 1 |
internal/ |
Module-private ratchets and generators: archtest (import-graph and determinism tests), codeshape, corpusheap, testbudget, tsgen (Go → TypeScript). |
5 |
cmd/ |
Binaries. Product: gorged, forgec. Developer: mtgsim, repro, headdiff, cardfuzz, enginebench, pkgmap, gentypes, codeshape, testbudget, gcgate, deckimport, keywordbench, paymirror, kcensus, spellbench-gorge-env, spellbench-gorge-agent. |
18 |
web/ |
The TypeScript/Svelte browser client. web/src/protocol.ts is generated by make gentypes; never edit it by hand. Its own go.mod keeps Go tooling out of node_modules. |
– |
scripts/ |
Contributor and CI scripts: ci_shard.sh, smoke.sh, enginebench-*.sh, docs_sets.py, open-pr.sh (make pr). |
– |
docs/ |
site/ (this site), decisions/ (architecture decision records), design/ (living design documents), cli.md, and an internal guide for AI coding agents. |
– |
.githooks/, .github/ |
The opt-in git hooks (git config core.hooksPath .githooks) and the CI, docs, coverage and promote workflows. |
– |
AGENTS.md, CONTRIBUTING.md, README.md, Makefile, go.mod, mkdocs.yml, LICENSE, NOTICE |
Root files. go.mod has no require lines. |
– |
Untracked paths you will meet locally:
| Path | What |
|---|---|
.cards/ |
The Forge corpus (cardsfolder/, tokenscripts/, cards.lock) and the compiled IR cache (ir-<fingerprint>.gob.gz). GPL-3.0, gitignored. Without it, corpus tests skip. |
bin/ |
make build outputs (forgec, mtgsim, gorged). |
gorged-data/, feedback/ |
A local server's default persistence and player bug reports. |
cmd/repro/testdata/.tokens/ |
Token-script text stripped from committed replay fixtures. |
The research tree, draft events and the oracle pipelines are not here: see The three repositories.
Tiers and import direction¶
The classification file
docs/site/architecture/packages.tiers.json
assigns every package a tier and a status. go run ./cmd/pkgmap turns it and
go list into the tables on Packages, and
draws the tier graph from the real imports with any back-edge in red.
| Tier | Directories | May import |
|---|---|---|
| Engine | engine/ |
the standard library and other engine/ packages only |
| Bots | bots/ |
engine/, other bots/ packages |
| Server | server/ |
engine/, bots/, other server/ packages |
| Tooling | internal/, testkit/, cli/ |
engine/, bots/, other tooling packages (measured) |
| Commands | cmd/ |
anything; no package imports a command |
Measured on 2026-10-21, every arrow in the tier graph points down this table
except one: server/spellbench/internal/testcorpus (test support for the
SpellBench adapter) imports testkit/testutil. Tests may import anything;
these rules are about non-test code.
What is actually enforced is a list of edges that must never appear,
checked directly and transitively by internal/archtest
TestDependencyOrderHolds. The ones a contributor is most likely to hit:
| Never | Why |
|---|---|
engine/effects → engine/rules |
effects reach the engine only through the small effects.Host interface, so the effect library stays testable on its own |
engine/view → engine/rules, server/protocol → engine/rules |
a client-facing type can never be handed rules knowledge |
engine/cards → engine/state, engine/deck → engine/rules |
the compiler and deck reader sit below the game |
bots/botpolicy → engine/view, engine/rules, engine/seat |
the one policy reads a Board, never an engine or a view |
any bots/ package → server/host or testkit/testutil |
bots plug into a host; they never depend on one |
server/host, server/host/httpapi, server/gorged → testkit/testutil |
test scaffolding stays out of the server |
server/protocol → bots |
the wire is engine-only |
Inside engine/rules, the sub-packages (cost, chars, combat, trigmatch,
pay, resolve, scriptfacts) have their own layering tests
(TestCostVocabularyIsALeaf, TestCombatImportsStayBelowRules,
TestTrigmatchImportsStayBelowL5, TestCharsImportsStayBelowRules, …), and
each reads the engine through a narrow Board interface whose method count
only shrinks. A new package also needs a tier entry, or go run ./cmd/pkgmap
-check (the pre-commit hook, and TestClassificationIsComplete) fails.
Where does my change go?¶
| I want to… | Go to |
|---|---|
| Fix how a card plays | Start with Your first card fix. |
Implement an api: effect |
engine/effects/: effects.Register("Name", fn) in an init(). If it needs more than effects.Host offers, it is rules work. |
| Implement a keyword, trigger, static or replacement | engine/rules/, declared with effects.RegisterNonAPI("kw:X", …). A trigger mode's matcher goes in engine/rules/trigmatch/. A keyword that rewrites into ordinary script lines is an engine/cards/kw_*.go expander (registerKeyword) instead. |
| Change a characteristic rule (CR 613 layers) | engine/rules/chars/. |
| Change attack or block legality | engine/rules/combat/; the declaration flow stays in engine/rules/combat.go. |
Add a count: value head |
The evaluator in engine/effects/count*.go and modelledValueHeads in engine/effects/value_heads.go (TestValueHeadRegistryMatchesEvaluator holds them together). |
| Change how scripts parse | engine/cards/. Any edit there changes cards.CompilerFingerprint, so every IR cache recompiles (make compile-cards). engine/cards/oracletext is exempt. |
| Add an event kind | Append after the last constant in engine/events/event.go, add its entry to kindInfo in engine/events/kindinfo.go, and put the state change in events.Apply (engine/events/apply.go). See Invariants. |
| Add a decision kind | Don't, without a maintainer decision: the set is closed. See Invariants. |
| Change what a seat sees | engine/view/: card.go (cards, battlefield), stack.go (stack); view.go only for the assembler. |
| Change the wire | server/protocol/, then make gentypes (regenerates web/src/protocol.ts). |
| Change server routes or seat authorisation | server/host/httpapi/; every seat route goes through claimForTable (rest.go). |
| Change server flags or startup | server/gorged/ (cmd/gorged is a 14-line main). |
| Change the default bot's choices | bots/botpolicy/ (the policy), bots/heuristic/ (its seat adapter). |
| Add a bot | bots.Register in an init() (bots/registry.go), blank-imported from bots/all. Read the bots section first. |
| Add a repo deck | testkit/testutil/decks/*.json via go run ./cmd/deckimport; every card in it must be fully supported. |
| Turn a feedback report into a test | go run ./cmd/repro -emit-test <pkg> <feedback-dir> (see Contributing). |
| Add a package | Wherever it fits the tiers, plus an entry in packages.tiers.json in the same commit. |
| Change the web client | web/ (needs Node; see web/README.md). |
| Change this site | docs/site/, built by mkdocs.yml; see Contributing. |
Behaviour that looks like a bug but is deliberate is recorded in
docs/design/2026-09-22-engine-contracts.md;
read it before filing.