The AI Architecture — "The Staff"
A design proposal for how an Every Last City AI opponent actually decides: the layers, the search, the evaluation function, what it believes about what it cannot see, and what it costs on a phone.
Status: Design proposal v1 · Subsystem: AI opponents · Intended spec home: 15-ai.md,
requirement prefix AI (numbering is the owner's call; the document map in the brief stops at 14)
· Depends on: 00-direction.md, 01-decision-turn-model.md, 10-turn-model.md,
13-command.md (written; cited throughout, never edited), 11-combat.md, 12-economy.md,
14-victory.md, 02-units-and-industry.md, 03-architecture.md (determinism, budgets, PRNG).
Clean-room: every value, name and structure here is designed for this game on its own terms. No number, table or behaviour is derived from any existing product. Where I borrow a technique from the public literature — portfolio search, deficit round-robin, receding-horizon planning — I name it as a technique, which is what techniques are for.
A note on the numbers. Every microsecond figure in this document is a budget I am proposing, not a measurement, with two exceptions that are measured and belong to
11-combat.md: an exchange resolves in 0.016 µs and an exact combat preview in 0.45 µs. Everything else is engineering estimate and is labelled as such where it matters. An AI document that quotes invented timings as if they were data is exactly the over-promise the brief warns against.
0. Glossary
Four named objects and one named function, so the rest of the document can be terse.
| Term | What it is |
|---|---|
| the Staff | The whole AI system. Named for a general staff, because that is the shape: a small number of people deciding what the army is for, and a much larger machinery executing it. |
| the Slate | The AI's belief state — contacts, ages, threat field, force accounting, opponent hypotheses. Everything the Staff thinks it knows. |
| the Atlas | A graph of ≤ 256 named places (cities, Landmarks, chokepoints, Depots, formation anchors) with abstract inter-node distances per movement class. The map, reduced to something you can plan on. |
| the Docket | The turn's candidate Operations — dated, located, valued things worth doing. |
| the Sandtable | A cheap abstract forward model that advances the game a few turns at formation granularity, to rank plans. |
| the Assay | The evaluation function. An integer score for a position, denominated so that 100 points = one city. |
1. The bet this document is making
The brief names advanced AI as a headline feature and asks for rigorous honesty about feasibility. Here is the bet, stated before the machinery so it can be judged on its own:
This design does not try to out-search a human. It tries to out-administer one, never make a stupid mistake, and reason about the same handful of objectives a human reasons about — in the same vocabulary the human uses.
That is a smaller claim than "superhuman 4X AI" and it is a much more achievable one, for a reason specific to this game. Look at where 4X and wargame AIs actually lose:
| Failure | Is it a search problem? |
|---|---|
| Leaves cities ungarrisoned and loses them to a scout | No. Arithmetic. |
| Strands transports, runs aircraft out of endurance | No. Constraint satisfaction. |
| Cities idle, production mistargeted, reinforcements never arrive | No. Scheduling. |
| Walks a column into a garrison it could have inferred | No. Belief. |
| Fights at bad odds because it cannot compute them | No. 11-combat.md gives an exact predictor. |
| Never notices an opponent is three turns from winning | No. 14-victory.md publishes it. |
| Picks the wrong axis of advance | Yes. |
| Mistimes a concentration | Yes. |
Seven of eight are not search. They are not being sloppy, and this game's specification has already done most of the work: the command layer removes the clerical failures by construction, the economy is closed-form integer arithmetic, combat has an exact monotone predictor, and the victory system publishes dated, located objectives to everyone.
So the plan is: be perfect at the seven, and spend the entire compute budget on the eighth. §16 is honest about how strong that makes us, and about the one place it could ship weak.
1.1 The four properties, and what each one is actually worth
The brief names four properties to exploit rather than rediscover. Here is what each buys, priced.
1 · The command abstraction collapses the branching factor. Partly true and it is the load-bearing one, but not in the way it first appears — §2 counts it honestly. The real gift is not a smaller number; it is a better-shaped landscape. In a raw action space one move is "a unit steps one tile," which changes the position by almost nothing, so hill-climbing is meaningless. In posture-and-objective space one move is "III Corps stops screening the river and marches on Ash Ford," which changes six units' behaviour for five turns by a computable amount. Local search only works when a single move means something. That is what the command layer provides.
2 · An AI turn is a clean bounded function. view(P, t) → orderDelta. Pure, no mid-turn input,
no interleaving with opponents. This is worth more than it sounds: it makes the AI trivially
parallelisable across seats and across farm games, trivially testable (feed a recorded view, assert
the order set), and it means the AI can be budgeted in work units rather than seconds (§11), which
is the entire answer to "phone versus desktop versus farm."
3 · Deterministic, headless, fast core. Self-play is a compute problem. §14 does the arithmetic: tuning the shipped weights is ~1,000 core-hours, which is a weekend on one machine. Learning a policy from scratch is 30–100× that plus an unsolved representation problem, and I am not promising it.
4 · Event-sourced games. Every game is a replayable log, so evaluation data is free and every AI regression has a reproducer. It also means replay never re-invokes AI code (already settled in the existing spec set), which decouples AI determinism from replay exactness — see §12.
And a fifth the brief does not list, which I think is the biggest one:
5 · The victory system hands the AI dated, located, valued objectives. Warrants are announced five turns ahead with a fifteen-turn deadline. Ascendancy Tallies are published twenty-four turns ahead. The Ebb schedule is published from turn 1. The Alarm grants live vision of the exact sites that constitute a threat. Landmarks are public from turn 1. This is extraordinary for an AI: the single hardest thing in strategic AI is deciding what matters, and this game publishes a calendar of it. A design that gives the AI "capture Sable Reach by turn 90, worth 200 Tenure" has done more for AI quality than any search algorithm will.
2. The shape of the problem, counted honestly
2.1 The raw space is not merely large, it is irrelevant
Late-large case: a 1,000 × 1,000 map, 8 players, ~300 units for our seat.
A unit with movement 4 on mixed terrain reaches roughly 25–45 tiles; add up to 4 attack targets and 4 Stances. Call it 160 distinct per-unit commitments. Joint over 300 units:
160^300 ≈ 10^661
Even restricting to the ~30 units a human actually re-orders in a turn, 160^30 ≈ 10^66. This number
is not interesting. What is interesting is that under Orders and the Cascade the raw space is not
the game's action space at all. A player does not choose destinations; they choose order policies
(CM-340: a Posture is a policy evaluated at activation, never a recorded sequence of steps). The raw
space is what the engine explores while executing a policy, not what the player searches.
2.2 The command space, counted properly
The player's actual action space per turn is a change to some subset of their command objects:
| Object | Count, reference midgame | Count, late large |
|---|---|---|
Groups (CM-860, ≤ 5 Formations each) |
4 | 10 |
Formations (CM-850, ≤ 8 units each) |
15 | 50 |
| Unattached units (garrisons, Cadre, Depots, scouts) | ~25 | ~90 |
Postings (CM-1130; client warns at 13) |
5 | 12 |
| Cities (Consignment, Template, Industry) | 20 | 60 |
(Reference midgame = 200 × 200 map, 48 cities, 4 players, turn ~120, ~90 units. This is 14-victory.md's
reference configuration and I use it throughout so the numbers are comparable to that document's.)
A Posture carries a geometry parameter, and read literally the geometry is unbounded — a Screen line is an ordered list of 2–64 tiles, which is astronomically many. The Staff never searches raw geometry. It searches over the Atlas: a Screen line is "the frontage between node A and node B," a March is "to node N," a Survey is "region R," a Garrison is "city C." The Atlas has ≤ 256 nodes, ~400 edges and ≤ 16 regions, of which only ~8 are feasible for any one formation within the planning horizon.
So per command object: 6 Postures (3 for a Group) × ~8 feasible geometries × 3 Sanction presets, minus illegal combinations ≈ 60 legal assignments. Joint:
| Scale | Command objects | Joint assignments |
|---|---|---|
| Reference midgame | 15 formations + 4 groups | 60^19 ≈ 6 × 10^33 |
| Late large | 50 formations + 10 groups | 60^60 ≈ 5 × 10^106 |
Be clear about what that means: the command abstraction does not make the space searchable. It
takes 10^661 to 10^106, and both are infinite for our purposes. Anyone claiming the abstraction
"makes search tractable" in the sense of "now you can search it" is wrong, and I would rather say so
than repeat a pleasing line from the brief.
What it does is the thing in §1.1: it makes the space hill-climbable, because moves are meaningful
and because operations are mostly separable — two formations' assignments interact only when they
compete for the same objective or share a theatre. A space of 10^106 with a smooth, sparse-interaction
neighbourhood structure is a space local search does well in. A space of 10^661 with a meaningless
neighbourhood is not.
2.3 The layered branching factors, which are the ones that matter
Because the layers have different granularities, each layer's own branching factor is small:
| Layer | Decision | Options | Method |
|---|---|---|---|
| Strategy | Empire Stance × Track emphasis × Industry policy | 5 × 5 ≈ 25, filtered to ~8 | Exhaustive, every turn |
| Operations | Which formations execute which Operations | 60^F |
Portfolio + local search, 2k–40k evaluated moves |
| Execution | Where each unit actually goes | 160^N |
Not searched. Delegated to Postures and Sanctions in core |
| Constraint floor | Garrisons, Cadre, Depots, lift, endurance, production | — | Solved, not searched |
That table is the architecture in miniature. Exhaustive at the top because the top is small.
Hill-climbing in the middle because the middle is shaped for it. Zero search at the bottom because
13-command.md already specifies the bottom, in core, identically for every seat (CM-100).
That last point deserves emphasis, because it is the cleanest structural win in the whole design:
the tactical layer is not the AI's code. It is the game's code. A unit ordered Screen by the
Staff runs the identical CM-460 candidate-scoring loop as a unit ordered Screen by a human. There
is no AI movement engine to keep in sync with the rules, no class of bug where the AI's units path
differently, and no way for an AI's units to be better at executing than a human's — which is
CM-050's fairness contract, obtained for free rather than promised.
3. The architecture
Five components, four of them layers and one of them a floor that cuts across all of them.
┌──────────────────────────────────────────────┐
view(P,t) ──────►│ L0 PERCEPTION builds the Slate, │
(fog-filtered) │ the Atlas, the Docket │
└───────────────────┬──────────────────────────┘
│ belief, places, candidate operations
┌───────────────────▼──────────────────────────┐
│ L1 STRATEGY Track portfolio, │ exhaustive
│ Empire Stance, │ over ~8
│ theatre & industry │ candidates
│ budget │
└───────────────────┬──────────────────────────┘
│ objective weights, force budget
┌───────────────────▼──────────────────────────┐
│ L2 OPERATIONS assign formations to │ portfolio
│ Operations; set │ + local
│ Postures, Sanctions, │ search,
│ Postings, Requisitions │ Sandtable
└───────────────────┬──────────────────────────┘
│ order delta
┌───────────────────▼──────────────────────────┐
│ L3 EXECUTION Postures & Sanctions, │ no search
│ in core, identical for │
│ every seat │
└──────────────────────────────────────────────┘
══════════ THE CONSTRAINT FLOOR ══════════════════════════════════
garrison sufficiency · Cadre routing · Depot chains · transport lift
· air endurance · production continuity · no idle city, no idle unit
Runs at full quality at EVERY difficulty tier. Never degraded.
The floor is drawn across the bottom deliberately. Difficulty degrades L1 and L2. It never degrades
the floor. A Recruit-tier AI plans shallowly and picks the wrong axis; it does not leave a city
empty or strand a bomber. That is the difference between an easy opponent and a broken one, and it
maps exactly onto 13-command.md's ledger split: Execution-ledger work is arithmetic and is always
performed correctly; Commitment-ledger judgement is where an AI is allowed to be worse.
4. L0 — Perception: what the Staff believes
The hard constraint is absolute and is the design's most important guarantee: the Staff sees only
the fog-filtered view a human at that seat sees. 13-command.md CM-130 already binds the
command layer to the knowledge view; the same rule binds the AI, and §12.3 makes it a mechanically
checkable CI gate rather than a promise.
4.1 The Slate — the contact ledger
Per believed enemy unit:
Contact {
lastTile, lastTurnObserved, observationLevel ∈ {L1 spotted, L2 adjacent, L3 recon}
class, believedStrength, believedDisorder, believedGrade, believedPosture
believedMove -- last-known movement allowance, or class default
provenance -- how we know: direct sight / inference / production accounting
}
Observation levels come from 11-combat.md §10, which already ties them to what the preview can
compute and — importantly — to the pivot analysis: "they break unless they are Veteran or better."
The Staff consumes the pivot directly. When a decisive attack's outcome pivots on one unobserved
field, the Staff's correct response is not to guess; it is to generate a reconnaissance Operation
against that specific unknown, valued at the swing in the pivot. That is a concrete, buildable
behaviour that reads to a player as intelligence, and the combat design has already done the
analytical work for it.
Ageing. A contact's weight decays:
decay(age) = clamp(1000 − 120 × age, 200, 1000) per-mille
Four turns old counts at 52%, six at 28%, floored at 20% — because a unit seen six turns ago is probably not there, but it certainly still exists somewhere. Contacts are never deleted, only aged; deletion is how AIs forget an army into oblivion.
4.2 The threat field
A per-chunk integer grid (64 × 64-tile chunks, so a Large map is 256 chunks), one plane per horizon
h ∈ {1, 2, 4, 8} turns:
threat[c][h] = Σ over contacts u reachable(u → c within h turns)
combatValue(u) × decay(age(u)) / 1000
+ spread(unaccountedForce, c, h)
Reachability uses the contact's believed movement allowance against the Atlas's chunk-level distances. Memory: 256 chunks × 4 horizons × 4 bytes = 4 KB, plus one plane per opposing player if we want attribution — still under 32 KB on the largest map. This is the whole reason the Staff's memory footprint is independent of unit count and nearly independent of map size (§11.3).
For city-level decisions the chunk grid is too coarse, so garrison and defence reasoning uses an exact
per-city query instead: for each of my cities, which believed contacts can reach it in h turns via
the Atlas. At 40 cities × 30 contacts that is 1,200 lookups per turn — negligible.
4.3 Force accounting — reasoning about what has never been seen
This is the part I am most pleased with, because it is fog-honest and it is exactly what a strong human does.
14-victory.md makes each player's progress publicly visible in bands: Rumour (a word), Report
(rounded to 5%), Alarm (exact, plus the sites). MAP_CITIES is public from turn 1. So a player's
approximate city count is legitimately derivable from the scoreboard, and from a city count you can
estimate a production history, and from a production history minus what you have seen you get the size
of the army you have not seen.
believedCities(P) from the Watch band, midpoint of the band, ± band width
believedWorks(P, t) = believedCities(P,t) × myObservedWorksPerCity -- my own empire as the yardstick
believedProduction(P) = Σ_{turns} believedWorks(P, turn) -- carried incrementally
militarySpend(P) = believedProduction(P) × 700‰ − believedCadreSpend(P) − believedIndustrySpend(P)
unaccounted(P) = max(0, militarySpend(P) − observedAliveValue(P) − confirmedDestroyedValue(P))
Worked example. Turn 120, 14-victory.md's reference map (48 cities, Dominion threshold 625‰).
The Watch shows "Red — Dominion 70%", which is Report band, rounded down to the nearest 5%.
- 70% of a 625‰ bar ⇒ Red's raw progress lies in
[437‰, 468‰)⇒ 21–22 cities. - I hold 14 cities producing 168 Works/turn ⇒ 12 Works per city. Red ≈ 21 × 12 ≈ 252 Works/turn now.
- Integrating my running estimate of Red's holdings over 120 turns gives ≈ 14,000 cumulative Works.
- Military share 700‰, less ~1,200 Works of believed Cadre and industry spend ⇒ ≈ 8,600 Works of army built.
- I have observed contacts totalling 2,100 Works and confirmed 3,400 Works destroyed.
- Unaccounted ≈ 3,100 Works — at 40 Works an Infantry, roughly 78 Infantry-equivalents, which is half again what I have actually seen.
That number is wrong — of course it is wrong; every term is an estimate, and the band on the Watch alone is worth ±1 city. But it is directionally right and it is actionable in a way a shrug is not: it says "there is more Red army I cannot see than Red army I can, do not leave the second line empty," and it feeds two places directly:
- The Assay, as feature A25 (§8), a penalty — uncertainty is worth real points.
- The Sanctions, as a policy: high unaccounted force pushes the Staff toward
Carefulpresets, larger reserves,ScreenoverMarch, and shorter Leashes. The AI becomes cautious because it is ignorant, which is the correct relationship and is legible to a player watching it play.
A hostile note I will make against myself: this estimator is a lever an opponent can pull. A human who understands it can inflate the AI's fear by holding cities they cannot defend, or deflate it by hiding production. That is fine — it is a bluffing surface, it is symmetric (a human can reason the same way about the AI), and it is far better than an AI that has no model of the unseen at all. §16 lists it as a risk to measure.
4.4 The Atlas
Built once at map load, maintained incrementally.
Nodes (≤ 256): every known city; every Landmark (public from turn 1); every Region capital; every chokepoint; every friendly Depot; each Formation's current anchor.
Chokepoints are supplied by map generation where it can (14-victory.md already has map-gen
publishing Landmarks and Regions, so this is a small addition to an existing contract). Where it
cannot, derive them once: on the chunk graph, a tile is a chokepoint if making its 8-neighbourhood
impassable increases the abstract distance between two chunk regions by more than 3×. Cheap, done
once, cached, and it gives the Staff a genuine notion of terrain that most opponents in this genre
lack entirely.
Edges: abstract path cost per movement class, from the hierarchical chunk-boundary pathfinder the
architecture already requires (AR-750). Distances between the ~40 live nodes are refreshed by
Dijkstra from each live node — ≈ 40 × 2,000 operations ≈ 80k ops, well under a millisecond — and
cached, invalidated by terrain change, city capture, Depot change, or a supply-grade flip.
Through fog, the Atlas uses CM-140's rule verbatim: an unrevealed tile is passable at the cheapest
cost the movement class pays. The Staff plans optimistically and discovers the truth, exactly as a
human does, and Blocked handling suspends rather than destroys the order.
4.5 The Docket — candidate Operations
An Operation is a dated, located, valued thing worth doing:
Operation {
kind ∈ { Seize, Relieve, Screen, Raid, Interdict, Survey, Escort,
Establish (Depot), Integrate (Cadre), Besiege, Deny, Claim (Warrant/Tally) }
target Atlas node, edge (a frontage), or region
requiredForce integer combat value, from the exact predictor against believed defence
frontage how many units can engage at once, from the map (§7.3)
window (earliest, latest) turn — real deadlines exist: Warrants, Tallies, the Ebb, a Vigil
value Assay delta if it succeeds
risk Assay delta if it fails
template the Posture + Sanction tuple to project onto whoever is assigned
}
Generation is rule-based, bounded, and deterministic, with hard caps per kind so the Docket cannot blow up on a huge map:
| Kind | Generated from | Cap |
|---|---|---|
| Seize | enemy/neutral cities within horizon × pace of any formation |
64 |
| Relieve | own cities with threat[city][2] > garrisonValue |
32 |
| Screen | Atlas edges crossing a believed approach corridor | 24 |
| Deny | any opponent in Alarm — sites are published with live vision | 16 |
| Claim | open and announced Warrants; upcoming Ascendancy Tallies | 8 |
| Raid / Interdict | enemy Depots, Cadre contacts, high-Industry rear cities | 24 |
| Survey | fog frontier regions with unrevealed area | 12 |
| Establish / Integrate / Escort | Depot sites; un-integrated cities; lift routes | 24 |
| Total before filtering | ≤ 204 |
Filtered to the top ~40 by value × 1000 / (cost + 1), cross-multiplied, ties by lowest Atlas node
index. That filtered Docket is what L2 searches over.
Two entries in that table are the §1.1 point made concrete. Deny exists because the Alarm hands every player the address of the fight — when a leader crosses 90% on a Track, the game grants everyone live vision of the contributing sites, and the Staff's correct response is not subtle: generate Deny Operations against those exact sites at a value that dominates everything else on the Docket, because the alternative is losing. Claim exists because Warrants and Tallies are announced with dates. An AI that arrives at the Iron Gate on turn 96 because the Tally calendar said so, having started its march on turn 84, will read to a player as genuinely intelligent — and it is nine lines of code and an ETA lookup.
5. L1 — Strategy
Re-evaluated every turn, but designed to change slowly. Exhaustive over ~8 surviving candidates.
5.1 The three strategic variables
Track portfolio. Which enabled Tracks am I racing, and which am I denying? Output: an integer
weight w_T per Track, feeding Assay features A4/A5. The default schedule ties the weight to the
game's own visibility bands, which is both legible and correct:
| Band on that Track, by any player | w_T |
|---|---|
| Nobody above Rumour | 400‰ |
| Any player in Report | 1000‰ |
| Any player in Alarm, or any Vigil running | 2000‰ |
So the AI's strategic attention sharpens exactly when the game says it should, and a human watching can predict when the AI will start caring about a Track. That is worth having.
Empire Stance. Five values, and this is the economy design's central dilemma expressed as one enum:
| Stance | What it does to the Docket and the weights |
|---|---|
| Expand | Seize value ×1400‰; accepts un-integrated cities; Cadre purchases deferred |
| Consolidate | Integrate/Establish value ×1600‰; Seize ×600‰; Cadre purchases prioritised |
| Strike | one theatre gets 70% of uncommitted force; Deny and Raid ×1500‰; Careful presets dropped |
| Defend | Relieve ×1800‰; Screen ×1400‰; Leashes halved; reserve fraction raised to 30% |
| Endure | play for Horizon/Tenure: hold, garrison, deny, refuse engagements below 850‰ |
12-economy.md's worked example is the reason this matters: twenty digested cities out-produce forty
raw ones, but forty raw ones out-produce everything once digested. Blitzing is a mortgage. The
Staff must be able to take that mortgage and must be able to decline it, and §9.2 shows the
mechanism that makes the choice come out correctly — it is not the Assay level, it is the spread
across opponent hypotheses.
Industry and Cadre policy. Both are exact arithmetic, not search:
- Industry (
02-units-and-industry.md): upgrade citycwhenupgradeCost × 2 < (newOutput − oldOutput) × remainingHorizonandthreat[c][8] = 0and the empire has no acute force shortfall (any Requisition row short by ≥ 2 for ≥ 3 turns). The payback computation is already worked in that document; the Staff just performs it. - Cadre (
12-economy.md): cumulative cost forNCadre ≈37.5 N², so the marginal price of the next one is known exactly. Buy when the marginal Cadre's discounted integration yield exceeds the marginal military unit's Assay contribution. Both sides of that inequality are Assay features (A8 against A14), so the comparison is in one currency and needs no hand-tuned constant.
5.2 Hysteresis, because a thrashing strategy is worse than a mediocre one
Strategic variables carry a switching cost in the Assay (feature A27, −25 per retasked command
object) and the Empire Stance additionally carries a flat −300 penalty for changing at all. A Stance
therefore only flips when the new one is decisively better, not marginally. This is not a hack: it is
the same reason CM-470 caches routes and CM-1230 gives Posting anchors a 5-tile hysteresis. An
opponent whose army reverses direction every turn is both weaker and infuriating to play against.
5.3 Re-planning triggers
Running L1 and a full L2 search every turn on every seat is affordable (§11) but wasteful, and
because orders persist (10-turn-model.md §3.2) most turns need only a small delta. The Staff
therefore runs a full re-plan only when a trigger fires, and a cheap refinement otherwise.
The elegant part: the triggers already exist. 13-command.md CM-1610 requires the core to emit
Attention Events deterministically, per player, from that player's fog-filtered view. The AI does not
need the Dispatch (CM-1630 guarantees automation is independent of it) — but Attention Events are
a free, deterministic, fog-honest change detector, and reusing them costs nothing:
| Trigger | Source |
|---|---|
| City taken or lost | CityTaken, CityThreatened |
| A Track enters Report or Alarm; a Vigil starts or breaks | 14-victory.md Watch |
| A formation is destroyed or falls under-strength | FormationUnderStrength, CommanderLost |
| Unaccounted force estimate moves by ≥ 15% | the Slate |
| A Warrant opens or a Tally is 6 turns out | 14-victory.md calendar |
An operation completes or becomes Impossible |
CM-1340 |
| Every 8 turns regardless | anti-staleness floor |
Median cost saving from trigger-based planning: I estimate 5–8× on quiet turns. That is the difference between "affordable" and "invisible" on a phone.
6. L2 — Operations: the search
This is the layer that does real work, and it is where the compute budget goes.
6.1 The algorithm
for each of K strategic seeds (K = 3..12 by tier):
plan ← GREEDY_ASSIGN(Docket, Formations, seed weights)
plan ← LOCAL_SEARCH(plan, step budget / K)
for each opponent hypothesis H (1..3 by tier):
outcome[plan][H] ← SANDTABLE(plan, H, depth d) → Assay at leaf
score[plan] ← RISK_WEIGHT(outcome[plan][*])
commit argmax score, ties by lowest plan seed index
Strategic seeds are the portfolio. Each seed is the same Docket with a different emphasis vector — Seize-heavy, Defend-heavy, Deny-heavy, Economy-heavy, Tempo-heavy, and the L1 Stance's own weighting. Different seeds converge to genuinely different local optima, which is the point: a portfolio of qualitatively different plans, each locally optimised, beats one plan optimised harder. This is portfolio search, and it is the right technique here for the same reason it is right in RTS research — the space is too big to search but candidate strategies are few and enumerable.
6.2 Greedy assignment
Fitness of formation F for operation O, all integer, all cross-multiplied:
sufficiency = clamp(forceValue(F) × 1000 / requiredForce(O), 0, 1500)
timeliness = clamp(1000 − 80 × max(0, eta(F,O) − window.earliest(O)), 100, 1000)
fit(F,O) = value(O) × sufficiency × timeliness / 1_000_000
sufficiency is capped at 1500‰ so that over-committing to an easy objective scores no better than
committing adequately — the anti-doom-stack term. timeliness decays 80‰ per turn late, so a
formation four turns from a Warrant deadline is worth 68% of one already there.
- Sort the Docket by
valuedescending, ties by lowest Atlas node index. - For each operation, take the best unassigned formation by
fit; skip iffit < 200. - Operations still short of
requiredForcemay take a second formation, up to the operation's frontage (§7.3) — never more, because a fourth formation at a two-tile defile contributes nothing and the Sandtable must not pretend otherwise. - Leftover formations:
Reserve(Hold at the theatre's Atlas node, EngagementDefend) up to the reserve fraction, thenScreenon the nearest uncovered frontage, thenSurvey.
Cost: F × O marginal evaluations. Reference midgame 15 × 40 = 600; late-large 50 × 40 = 2,000. Each
marginal is a few hundred integer operations. Estimated 0.2–0.7 ms.
6.3 Local search
Five move types, steepest ascent, deterministic tie-breaks (lowest formation id, then lowest operation id):
| Move | Neighbourhood size, midgame | late-large |
|---|---|---|
| Reassign one formation to a different feasible operation | 15 × 8 = 120 | 50 × 10 = 500 |
| Swap two formations' operations | C(15,2) = 105 | C(50,2) = 1,225 |
| Reinforce an under-strength operation from the reserve | ≤ 40 | ≤ 40 |
| Abandon an operation, freeing its formations | ≤ 40 | ≤ 40 |
| Escalate/de-escalate one assignment's Sanction preset | 15 × 2 = 30 | 50 × 2 = 100 |
| Per pass | ~335 | ~1,905 |
Each move is scored by an Assay delta, not a full Assay: reassigning one formation touches that formation's force terms, the two operations' opportunity terms, and the threat terms of at most a few cities. Estimated 1–3 µs per delta. Four passes:
- reference midgame: 1,340 deltas ≈ 2–4 ms
- late-large: 7,620 deltas ≈ 10–20 ms
Multiplied by the seed count K, this is the bulk of the budget and it is the knob difficulty turns.
6.4 Risk weighting
Each plan is evaluated by the Sandtable under 1–3 opponent hypotheses (§9.1). Combine:
mean = Σ outcome[H] × prior[H] / 1000
worst = min over H of outcome[H]
score = mean − k × (mean − worst) / 1000
k is the caution coefficient, an integer per-mille, and it is the single most character-defining
number in the Staff. Defaults: 500‰ (Balanced), 300‰ (Vanguard), 750‰ (Steward). At k = 0 the AI is
an expected-value maximiser that will happily bet the game; at k = 1000 it is a pure minimaxer that
never attacks. §9.2 shows this term doing the strategic work the economy design wants it to do.
6.5 Setting the reinforcement machinery
The plan's output is not only formation orders. It also writes, in the same commit:
- Posting anchors and weights. Each active theatre gets a Posting; weight ∝ committed force ×
operation value, normalised to
[1, 10]. The deficit round-robin scheduler ofCM-1190then interleaves production without further AI involvement — the Staff sets a ratio and the core delivers it. This is a genuinely large amount of AI work that simply does not have to exist. - Requisition rows. Force structure per theatre, derived from the operations assigned to it: a
Seize-heavy theatre requisitions capture-capable units and artillery; a Screen theatre requisitions
cheap mobile units; a besieging theatre requisitions siege.
CM-1250's shortfall arithmetic already counts in-transit and in-production units, so the Staff cannot thrash production by over-ordering. - Consignments and Templates. Cities near a theatre are consigned to it; the rest go
Auto. - Doctrines. One Doctrine per role archetype, set once and reused, so per-unit-type defaults
produce sensible units without further orders (
CM-790,CM-080).
7. The Sandtable
The abstract forward model. It exists to rank plans, not to predict the game, and every design choice in it follows from that.
7.1 State
Per formation: Atlas node (or an edge with a progress fraction), aggregate attack value, aggregate defence value, pace (slowest member's movement), supply grade, current operation, ETA. Per city: owner, base Works, Integration, Unrest, garrison value, incoming threat. Per player: Works/turn, Manpower/turn, Standing, Tenure, per-Track progress.
Nothing is per-tile and nothing is per-unit. That is what makes it cheap.
7.2 Step
- Move. Each formation advances
pacealong its Atlas route. Arrival is a threshold on accumulated abstract cost. - Clash. Where opposing formations contest a node or an edge, resolve one aggregate exchange (§7.3).
- Cities. A city flips when the attacking aggregate exceeds the defending aggregate by the break
margin from
11-combat.md§7 and no relief arrives within 2 abstract turns. Capture applies12-economy.md's consequences exactly: Integration reset tofloor(Loyalty/2), Unrest by class, Industry degraded one tier, garrison requirement raised. - Economy. Closed form, straight from
12-economy.md: effective Worksfloor(base × (20 + floor(4 × Int / 5)) / 100); Levyfloor(pop × Int / 1000); Integration by the Cadre/Unrest/Reach table; Cadre costfloor(base × (2+N) / 2). Every one of these is already integer and already closed-form, so the Sandtable's economy is not an approximation at all — it is exact. This is a real and unusual advantage. - Victory. Recompute Track progress, Standing, Tenure at the abstract Seal.
Cost estimate: O(formations + cities) with a small constant plus Atlas lookups. At late-large (50 formations, 60 cities) I budget ≤ 250 µs per Sandtable turn; at reference midgame ≤ 80 µs.
7.3 Aggregate combat, and frontage
The obvious way to fight two formations is A = Σ attack, D = Σ defence, one exchange. That is
wrong in a specific and dangerous way: it makes concentration free, so the AI learns to build a
doom-stack — precisely the behaviour CM-840 and 11-combat.md §6 are designed to prevent.
The fix is frontage limitation. An operation against target node n has a frontage φ(n) = the
number of tiles from which the target may legally be engaged, derived once from the map and stored on
the Atlas node (a city in open ground: 8; across a river: 2–3; a mountain pass: 1–2). Then:
engaged = min(members(F), φ(n) × stackLimit)
A_raw = lead.atk + 400‰ × Σ (engaged − 1 next-best attackers) -- 11-combat.md §5 support shape
D_raw = defender.def + stack support, same shape
and the exchange is resolved at r = 500 (the median draw) for the mean case, and at r = 0 and
r = 999 for the risk band. No PRNG is consumed anywhere — the Sandtable is a deterministic function
of the plan and the hypothesis, which §12 requires.
Frontage does two jobs at once: it makes the model honest about concentration, and it gives the Staff a reason to value chokepoints that falls out of the arithmetic rather than being asserted as a heuristic. An AI that defends a defile because the defile makes its numbers count is reasoning, not following a rule.
7.4 Depth, and the honest caveat
Depth 2–5 turns by tier. Beyond that the model's errors compound past usefulness; a receding-horizon planner that re-plans every turn does not need more.
The Sandtable will be wrong. It has no terrain below the Atlas, no per-unit losses, no reactions,
no Opportunity interruptions, no knowledge of what the opponent actually ordered. Its output is a
ranking signal, and the Staff must never treat it as knowledge — which is exactly why plans are
re-derived from scratch every trigger and why the switching cost (A27) is the only thing carried
between turns.
§16 names this as the design's largest single risk and gives the falsification test.
8. The Assay — the evaluation function
8.1 Scale and discipline
One city = 100 points. Denominating against 14-victory.md's Standing means every weight is
interpretable ("a Cadre on station is worth 0.9 cities") and means the Assay and the game's own score
do not drift apart.
All arithmetic is integer. Weights are per-mille multipliers on integer quantities, default 1000‰ so that the untuned origin is neutral and self-play tuning starts from a defensible position rather than from someone's guesses. Ratio comparisons are cross-multiplied, never divided. No Q16.16 is needed anywhere in the Assay — this is a sum of products of small integers.
8.2 The features
27 features in five groups. press(p) is the shared convexity function that makes progress near a
threshold worth disproportionately more:
press(p) = p for p < 500
500 + 2 × (p − 500) for 500 ≤ p < 900 (p in per-mille of threshold)
1300 + 6 × (p − 900) for p ≥ 900
| # | Feature | Exact definition | Weight |
|---|---|---|---|
| Territory and victory | |||
| A1 | Cities held | 100 × cities |
1000‰ |
| A2 | Landmarks | 150 × landmarks |
1000‰ |
| A3 | Seats | 200 × seats held (mine or captured) |
1000‰ |
| A4 | Track pressure | Σ_T w_T × press(p_T) / 1000 |
1000‰ |
| A5 | Track denial | − Σ_T Σ_opp 3 × w_T × press(p_opp,T) / 4000 |
1000‰ |
| A6 | Vigil equity | +400 × held/required (mine) − 600 × held/required (theirs) |
1000‰ |
| Economy | |||
| A7 | Integration stock | 3 × Σ_cities (baseWorks × Int / 100) |
1000‰ |
| A8 | Integration slope | 2 × Σ_cities (projectedΔInt over 10 turns × baseWorks) / 100 |
1000‰ |
| A9 | Levy rate | 20 × manpowerPerTurn |
1000‰ |
| A10 | Production rate | 8 × worksPerTurn |
1000‰ |
| A11 | Industry depth | 40 × Σ_cities (industryTier − 1) |
600‰ |
| A12 | Cadre on station | 90 × cadre stationed where Unrest < 50 |
1000‰ |
| A13 | Occupation drag | − Σ_foreign cities (100 − Int) × baseWorks / 100 |
1000‰ |
| Force | |||
| A14 | Force value | Σ_units cv(u) × mobility‰ / 1000 where cv(u) = cost × max(0, 100×strength − 50×disorder) / 40000; mobility 1000‰ mobile, 500‰ immobile |
1000‰ |
| A15 | Supply state | − Σ_units cost × penalty‰ / 400000, penalty 0 / 250 / 600 for Supplied / Extended / Isolated |
1000‰ |
| A16 | Veterancy | + Σ_units gradeIndex × cost / 200 |
600‰ |
| A17 | Lift capacity | + 30 × min(transportSlots, unitsNeedingLift) |
800‰ |
| Position | |||
| A18 | Garrison shortfall | − 200 × Σ_cities max(0, required − present) |
1000‰ |
| A19 | City threat | − Σ_cities threat[city][2] × cityValue / 4000 |
1000‰ |
| A20 | Seat exposure | − 800 if threat[Seat][3] ≥ Seat garrison value |
1000‰ |
| A21 | Frontier opportunity | + Σ_takeable enemy cities cityValue × takeProb‰ / 4000 |
800‰ |
| A22 | Reach coverage | + 2 × supplied tiles within 6 of the contested band |
400‰ |
| A23 | Chokepoint control | + 60 × held chokepoints on an active axis |
1000‰ |
| A24 | Observation | + 1 × currently-observed tiles in the contested band |
200‰ |
| A25 | Unaccounted enemy force | − unaccounted Works / 8 |
1000‰ |
| Hygiene | |||
| A26 | Idle or stalled | − 40 × (idle formations + stalled formations) |
1000‰ |
| A27 | Switching cost | − 25 × command objects retasked this turn |
1000‰ |
cv(u) deserves a note: at full strength and no disorder it equals cost / 4, exactly matching
14-victory.md's military Standing term, so a healthy army is worth the same in the Assay as on the
scoreboard. Disorder costs half as much as missing strength, which mirrors 11-combat.md's resolve
arithmetic (disorder subtracts point-for-point, missing strength subtracts half a point per point) —
inverted, correctly, because their disorder helps me and mine hurts me.
8.3 Worked example — Blue at turn 118
14-victory.md §7's reference endgame, turn 118: 48-city map, Dominion 625‰ Vigil 3, Keys 3-of-5
Vigil 6, Horizon 200. Red 22 cities, Blue 14, Green 8, Gold 4. Blue holds 2 Keys and their own
Seat. Blue: 168 Works/turn, ~55 Manpower/turn, 40 units averaging cost 60, strength 80, disorder 20 —
the same position §4.3's force accounting was run from. No Track is above Report, so w_T = 1000‰.
| Feature | Working | Points |
|---|---|---|
| A1 cities | 14 × 100 | +1400 |
| A2 Landmarks | 2 × 150 | +300 |
| A3 Seat | 1 × 200 | +200 |
| A4 Track pressure | Dominion p = 291/625 = 466‰ → 466; Keys 2/3 = 666‰ → 832; Horizon 7020/9140 = 768‰ → 1036. Σ = 2334, ×1000‰ |
+2334 |
| A5 Track denial | Red: Dominion 733‰→966, Keys 666‰→832, Horizon 1000‰→1900. Σ 3698 × ¾ | −2774 |
| A6 Vigil | none running | 0 |
| A7 integration | 3 × 130 |
+390 |
| A8 slope | near-integrated homeland | +40 |
| A9 levy | 55 × 20 | +1100 |
| A10 works | 168 × 8 | +1344 |
| A14 force | cv = 60 × (8000 − 1000)/40000 = 10.5 × 40 units, mobile |
+420 |
| A15 supply | mostly supplied | −20 |
| A18 garrison | 3 points short | −600 |
| A19 city threat | Red pressing two cities | −400 |
| A21 opportunity | 2 takeable Green cities at ~600‰ | +150 |
| A22–A24 position | reach 80, chokepoints 120, observation 40 | +240 |
| A25 unaccounted | 3,100 Works ÷ 8 | −388 |
| Assay(Blue, t118) | ≈ 3,736 |
Read the two Track rows. Blue's own progress is worth +2334; Red's is worth −2774. The Staff is
already, at turn 118, more worried about Red winning than pleased about its own position — which is
correct, and which is why at turn 131 (Red completes Keys, Vigil 6 begins, Alarm) this AI does exactly
what Blue did in the worked endgame: it drops everything and lands at Kestrel Bay. The Alarm's free
vision hands it the garrison count, the Deny operation's value is enormous because press(1000) = 1900
at w_T = 2000‰, and the plan writes itself. That is the victory system and the AI design meshing.
8.4 Cost
The Assay is a sum of independently-computable term groups keyed by entity, which is what makes delta evaluation possible. Estimated budgets: full Assay ≤ 15 µs at reference midgame, ≤ 60 µs at late-large; delta ≤ 3 µs. Full Assays happen only at Sandtable leaves; everything in local search is a delta. At Veteran tier that is roughly 100 full Assays and 8,000 deltas per turn ≈ 30 ms of evaluation, which is the dominant cost and is where tuning effort belongs.
8.5 What the Assay deliberately is not
It is not a win-probability estimate, and the Staff never compares its Assay to an opponent's Assay. Opponent standing enters only through A5 and A25, in my own currency. Cross-player evaluation comparison is a classic source of nonsense in this genre — my 4,000 and your 4,000 are not commensurable when we hold different things under different fog — and refusing to do it costs nothing.
9. Commit before you see
The Staff issues orders blind, against the board as it stood at the end of the last Cascade, exactly
like every other seat. 01-decision-turn-model.md calls the conditional vocabulary the design's
largest execution risk. For the AI it is also the largest opportunity, because unlike a human the
Staff can afford to actually evaluate the branches.
9.1 Opponent hypotheses
Per opposing player, 1–3 hypotheses by tier:
| Hypothesis | Construction | Prior |
|---|---|---|
| Passive | every contact holds position; production continues | 250‰ |
| Continuation | every contact keeps its last-observed heading and pace; unaccounted force appears on the shortest axis toward my weakest city | 500‰ |
| Best reply | run a shallow Staff (greedy assignment only, no local search, depth 2) for them, over a model of their view | 250‰ |
The Best-reply hypothesis has a fog trap in it that must be named explicitly, because getting it wrong is a silent wallhack: modelling an opponent's decisions requires modelling their view, and their view is not ours to read. The rule is that the opponent's modelled view is constructed from our own Slate: they are assumed to see whatever our units are within the vision radius of their believed contacts, to know their own forces (which we substitute with our unaccounted-force estimate), and to know the same public information we do. We reason about our estimate of them. We never read the truth to build it. §12.3 makes this testable.
Best-reply is Elite-and-above only, for cost and for a subtler reason: at low tiers a Continuation hypothesis produces more human-plausible misjudgement, and an AI that is wrong the way a person is wrong is a better opponent than one that is wrong the way a bug is.
9.2 The hedge is the Sanctions, and the mortgage shows up as spread
Because plans are scored by mean − k × (mean − worst) across hypotheses, the Staff is naturally
pushed toward assignments whose Sanction settings are least bad across the branches it cannot
distinguish. That is not an add-on; it is what the risk term does.
Take 12-economy.md's central dilemma. The Blitzer position — 40 cities, 22 of them at Integration
20, 47 militia garrisoning them, a manpower crisis, a long amber supply frontier — evaluates
higher on the Assay than the Consolidator position, and that is correct: it is worth more, and the
economy document says so ("he is clearly winning, and he will win if nothing changes"). The mortgage
does not show up as a lower level. It shows up as spread. Under the Passive hypothesis the
Blitzer's "keep expanding" plan scores brilliantly. Under Continuation with a large unaccounted force
it scores badly, because un-integrated cities have thin garrisons, a broken Reach chain isolates a
whole axis, and Loyalty memory means every one of those cities snaps back to full value the moment its
original owner walks in.
So:
- A Staff with
k = 300‰(Vanguard) keeps expanding and sometimes gets away with it. - A Staff with
k = 750‰(Steward) consolidates. - A Staff with
k = 500‰(Balanced) expands while the unaccounted-force estimate is low and consolidates when it rises.
That third behaviour is the one I want, it is emergent rather than scripted, and it is legible: a player can watch the AI stop expanding after a scouting raid reveals a rival's army and understand exactly why. One integer coefficient, evaluated over three cheap hypotheses, produces the strategic texture the economy design was built to create.
9.3 Exploiting initiative, which nobody does
10-turn-model.md §3.1 contains a lovely property that the Staff can use and most AIs would never
notice: high initiative buys the first strike; low initiative buys knowing where everyone went.
A late-activating unit resolves its order against a board where most of the turn has already happened.
Three concrete Staff behaviours follow:
Contingent objectives go to low-initiative formations. An operation whose value depends on what an opponent does this turn ("take the ford if it is uncontested") should be assigned to a formation whose members activate late, because their conditional orders will be evaluated against more information. A race for a fixed objective goes to a high-initiative formation. The Staff can compute its own frozen activation order exactly (initiative is a pure function of committed state) and opponents' coarsely, and the assignment fitness gets one more integer term.
Commander selection for responsiveness.
CM-960's Intent Freshness means a Formation whose commander activates before the majority of its members executes this turn's intent, and one whose commander activates after them is a turn behind. The Staff should promote commanders to make formations on contingent operations Responsive — an old, veteran, low-Forming-Up unit — and is free to leave a sluggish commander on a formation with a standing job. This is free operational competence extracted from a rule most players will not exploit for fifty hours.Reaction screens. Hold and Entrench each carry one reaction live for the entire Cascade, which is how a low-initiative defender affects a high-initiative attacker. The Staff should therefore solve a small set-cover: for each believed approach corridor into my territory, place at least one unit in Hold or Entrench adjacent to it. Candidates bounded to 64 corridors and 256 tiles, greedy cover, deterministic tie-breaks. This costs almost nothing and produces the single most "how did it know" behaviour available in this turn model — the AI's picket line stopping a cavalry raid it could not have seen coming, because it was covering the corridor rather than the unit.
10. The Constraint Floor
Everything here is solved, not searched; runs at every difficulty tier; and never yields to the step budget. This is the section that decides whether the AI reads as competent.
| Obligation | Method | Bound |
|---|---|---|
| Garrison sufficiency | 12-economy.md gives required garrison points by class and Integration. Compute required − present per city; deficits become Requisition shortfalls (CM-1250) and Relieve operations. |
O(cities) |
| Cadre routing | Assignment problem: value of Cadre c at city k = discounted integration gain × base Works over the payback window, minus travel turns at move 1. Greedy on the exact formulas; escort required whenever the route's threat field is non-zero. |
O(cadre × cities), ≤ 20 × 60 |
| Depot chains | Set-cover: maximise supplied tiles along the intended axis subject to every Depot's own path home remaining intact. Candidates capped at 64 tiles per axis. | ≤ 64 per axis |
| Transport lift | The Ferry loop modes (CM-530, CM-535) already execute shuttles with zero player input. The Staff only picks route endpoints and capacity. |
O(routes) |
| Air endurance | A hard filter, never a search decision: no sortie is generated whose return leg exceeds remaining endurance against a reachable base or carrier. An aircraft is never "considered" for a one-way trip. | O(air × bases) |
| Production continuity | Every city always has a Consignment or a Template (CM-1160, CM-1310). A newly captured city defaults to Auto and is therefore never idle. |
O(1) per city |
| No idle unit | CM-540 makes Idle deliberately uncomfortable and raises an event every turn. The Staff treats any Idle event as a hard defect: assign a Posture in the same turn, always. |
O(events) |
| No stranded formation | CM-1440's Stalled detection is core-side and fog-honest. Two consecutive non-progressing turns forces the operation to be abandoned and re-planned. |
O(events) |
The unifying claim: most of what makes an opponent look intelligent is not intelligence. It is never doing the eight things in that table wrong. A shipped AI that gets all eight right at every difficulty tier will read as far stronger than its search budget suggests, and one that gets any of them wrong will read as broken regardless of how good its planning is.
11. Budgets — work units, not wall clock
11.1 The rule that makes everything else possible
The Staff's internal budget is an integer step counter, never a clock. A "step" is one unit of declared work: one Assay delta, one Sandtable turn, one greedy marginal, one exact combat preview, each with a declared integer cost. The search is anytime with deterministic checkpoints: after greedy assignment a complete legal plan exists, and every subsequent local-search move is optional. The budget simply truncates the improvement loop at a fixed step count.
Three things fall out, and they are the answers to three separate questions the brief asks:
- Phone versus desktop versus farm is one knob. Same code, same algorithm, different integer step budgets. There is no mobile AI and no farm AI; there is one AI with a number.
- Determinism survives time-boxing. If the budget were wall clock, a faster machine would produce different orders — which would make the self-play farm irreproducible and would make AI strength a function of hardware. With a step counter, truncation happens at the same step on every machine.
- Difficulty is honest. Difficulty is the step budget plus knowledge discipline, never a hidden bonus.
Wall clock survives only as a fault watchdog. If the watchdog fires, that is a defect to be fixed, not a budget outcome, and the fallback is the last completed checkpoint's plan.
11.2 The budget table
Step costs are estimates (labelled as such in the header note). One step ≈ one Assay delta.
| Tier | Step budget | Seeds K |
Local passes | Hypotheses | Sandtable depth | Est. ms, midgame | Est. ms, late-large |
|---|---|---|---|---|---|---|---|
| Recruit | 1,500 | 2 | 1 | 1 (Passive) | 1 | 3 | 12 |
| Regular | 6,000 | 3 | 2 | 1 (Continuation) | 2 | 10 | 40 |
| Veteran (default) | 25,000 | 6 | 3 | 2 | 3 | 35 | 140 |
| Elite | 80,000 | 8 | 4 | 3 | 4 | 100 | 400 |
| Marshal | 250,000 | 12 | 6 | 3 + regret weighting | 5 | 300 | 1,200 |
On reference hardware, headless. Multiply by 2× for a browser tab and 2× again for a 2019-class ARM phone, per the architecture's stated multipliers. So Veteran on a phone at late-large scale is ~560 ms per seat — and §11.4 explains why that number never reaches the player.
11.3 Memory
This is the happy surprise. The Staff's working set is dominated by the Atlas and the threat field, both of which are independent of unit count and nearly independent of map size:
| Structure | Size | Notes |
|---|---|---|
| Atlas nodes + all-pairs distance, 3 movement classes | ≤ 256² × 2 B × 3 ≈ 384 KB | cached, incrementally invalidated |
| Threat field, 256 chunks × 4 horizons × 8 players | 32 KB | |
| Slate contact ledger, 400 contacts × 32 B | 13 KB | |
| Docket, 204 operations × 64 B | 13 KB | |
| Plan store, 12 seeds × 60 command objects × 24 B | 17 KB | |
| Sandtable working state, 3 hypotheses × depth 5 | ≈ 200 KB | |
| Total | < 1 MB |
Under one megabyte, at every scale, for every tier. Against the mobile plugin cap in the existing spec (128 MiB) this is not a constraint at all. The reason is entirely structural: the Staff reasons about formations and places, not units and tiles. Abstraction bought us memory before it bought us anything else.
11.4 The latency that isn't
The Orders phase is concurrent and the board is stable during it (10-turn-model.md §3.6: nothing is
happening, for anyone). Therefore the Staff thinks while the human thinks. In solo play — which
01-decision-turn-model.md's first amendment names as the common case and requires to run instantly —
the AI seats begin planning the instant the Cascade ends and the new views are published, on a worker,
off the UI thread. Unless the human commits faster than the AI finishes, the AI's latency is
completely hidden.
Two consequences worth stating:
- The solo latency budget is not "how fast is the AI," it is "how fast is the AI relative to the human's fastest turn." A human who commits a turn in 8 seconds gives a phone-tier Veteran AI a 14× margin at late-large scale. The only case that binds is a human hammering End Turn through a quiet stretch, and the Staff's trigger-based re-planning (§5.3) makes exactly those turns the cheap ones.
- The rolling-planning refinement in
10-turn-model.md§3.8 applies to AI seats identically. Once a formation has activated in CascadeT, the Staff can begin planning forT+1. That is free latency recovery and it needs no new machinery.
There remains a fairness question I should name rather than bury: if a desktop can afford Marshal and a phone cannot, does the same difficulty setting mean different things on different devices? The answer must be no, and the mechanism is that a tier pins its step budget absolutely. A device that cannot run a tier within its watchdog does not run a weaker version of it — it declines to offer it, and says so at setup ("this device supports up to Elite"). Desktop headroom buys more tiers, never a secretly stronger Veteran.
12. Determinism and replay
12.1 What is already settled, and what it buys
The existing spec set settles the important half: every order an AI submits enters the authoritative order log exactly as a human's does, and replay never re-invokes AI code. Replays and desync detection derive purely from the log. So AI determinism is not required for replay exactness — and that is a genuinely valuable decoupling, because it means an experimental AI cannot corrupt a saved game.
But we want AI determinism anyway, for four things replay exactness does not give us: farm reproducibility, desync arbitration, bug reproduction, and the fog-leak test in §12.3.
12.2 What this design adds
- Integer everywhere. The Assay, the Sandtable and every threshold in this document are integer
or per-mille. No floating point crosses any boundary and none is needed internally. Ratio
comparisons are cross-multiplied. This matches
13-command.mdCM-120and means the Staff and the command layer share one arithmetic discipline. - No rules-PRNG consumption, ever. The Sandtable resolves at fixed draws (
r = 0, 500, 999). The Staff never touches the rules streams. Where a low tier wants deliberate variety it draws from the AI-private stream the architecture already provides, which is outcome-neutral by construction. Veteran and above draw nothing at all and are strictly deterministic functions of the view. - Step budget, not clock (§11.1). This is the requirement that makes time-boxing safe, and it is the one most likely to be quietly violated by an implementer under deadline pressure. It needs a CI assertion, not good intentions: run the same recorded view on two machines of different speed and assert byte-identical order sets.
- Plan memory is advisory. The Staff's cross-turn memory (current plan, switching-cost baseline, force-accounting integrals) is serialised outside hashed state. A dropped or version-mismatched blob must degrade to "no memory" and still produce a legal, competent turn from the view alone — never a crash, never a refusal.
- Explain records. Every committed plan emits a structured record: the chosen seed, the operations
and their assignments, the top three rejected alternatives with their scores, and the Assay
decomposition. Outside the order log, outside the state hash, reproducible from a replay. This is
the same trust argument
CM-1940makes for the command layer's audit trail, and it is the best debugging tool the team will have.
12.3 The fog-leak gate
This is the most important test in the document, because a fog leak in the AI is invisible, is devastating to trust, and is the single easiest bug to introduce while building an opponent model.
Gate: mutate any element of world state that is invisible to player
P. Re-run the Staff for seatPon the resulting state. The emitted order set MUST be byte-identical.
The architecture's property-test suite already contains this shape for the view projection itself; the
addition is putting the AI inside the loop. It is cheap to run, it fails loudly, and it is the only
mechanism I trust to keep a hundred small opponent-modelling changes honest over a project's lifetime.
A second, weaker gate is worth having alongside it: assert that the Staff's code never reaches the
true-state API at all, mechanically, the way the determinism lint already forbids Math.random.
13. Difficulty and character
13.1 Difficulty is search and knowledge, never a bonus
The tier table in §11.2 gives the search knob. Knowledge discipline is the second knob, and it is the more interesting one because it degrades the AI in a human direction:
| Tier | Contact ageing | Force accounting | Pivot analysis | Vocabulary |
|---|---|---|---|---|
| Recruit | contacts drop after 3 turns | none | none | Sanction presets only, Balanced |
| Regular | drop after 8 turns | city-count only | none | presets |
| Veteran | full decay curve | full | yes | all four dials |
| Elite | full | full + per-player attribution | yes, with recon operations generated | all dials, per-role Doctrines |
| Marshal | full | full | yes | all, plus Track-denial planning and initiative exploitation (§9.3) |
A Recruit that forgets an army after three turns walks into it, which is a mistake a person makes. A Recruit that has full knowledge but searches badly makes mistakes that look like glitches. Degrade belief and search together; never degrade the Constraint Floor.
13.2 Characters
Personalities are weight presets plus a caution coefficient plus Track preferences — data, not code forks. Five, with original names:
| Character | k |
Signature weights | Reads as |
|---|---|---|---|
| Steward | 750‰ | A7/A8/A12/A18 up; A21 down | Digests everything, defends well, rarely gambles |
| Vanguard | 300‰ | A21/A23 up; A13/A25 down | Expands fast, takes the mortgage, sometimes collapses |
| Ledger | 550‰ | A9/A10/A11 up; prefers Prosperity and Great Work | Builds an economy and wins on Tenure |
| Reaper | 400‰ | A3/A5/A6 up; prefers Throne and Keys | Hunts Seats and Landmarks; short, violent games |
| Balanced | 500‰ | defaults | The reference |
The promotion gate must show each character's signature is measurable — Vanguard's median turns-from-contact-to-first-attack must be materially lower than Steward's, Ledger's un-integrated city count must stay under 8 — or the characters are cosmetic and should be cut rather than shipped as flavour text.
14. Self-play: what it buys, priced honestly
14.1 What we are actually going to do
Tune ~42 integer parameters: 27 Assay weights, the caution coefficient, the press() breakpoints,
the decay constants, the sufficiency and timeliness slopes, the reserve fraction, and the Stance
switching penalty. Method: CEM or SPSA over the integer vector, with a statistically gated promotion —
a candidate replaces the incumbent only on ≥ 55% over ≥ 1,000 games at p < 0.05, without regressing
character signatures or tier monotonicity.
The compute arithmetic, so the claim can be checked:
- A tuning game: 200 × 200 map, 4 seats, ~160 turns, Veteran.
- AI cost: 160 turns × 4 seats × ~35 ms ≈ 22 s.
- Simulation cost: the architecture's per-player upkeep budget is ≤ 60 ms, so 160 × 4 × 60 ms ≈ 38 s.
- Say ~70 s per game per core, with trigger-based re-planning likely halving the AI half.
- CEM: 200 iterations × 200 games = 40,000 games ≈ 780 core-hours ≈ 12 hours on a 64-core box.
That is a weekend, on one machine, for a full tuning run. It is not a research programme. And it is
the same infrastructure 14-victory.md already requires for its length-estimate calibration table
(≥ 2,000 games per cell) and its linter thresholds, so the farm is being built regardless.
14.2 What we are not going to do, and what it would cost
Learning a policy from self-play — the AlphaZero shape — is the thing an AI document in 2026 is expected to promise. I am not promising it, for three reasons and one arithmetic:
- Representation. There is no obvious network input for a variable-size command hierarchy over a variable-size map with per-mille integer economies. Solving that is research, not engineering.
- Determinism. A float network's outputs are not bit-identical across the WebAssembly engines the client targets, and NaN payloads are engine-defined. An integer-quantised network is deterministic and is a real option — see below — but a float one cannot ship into this architecture at all.
- Explicability.
CM-2000requires automation to teach its reason in numbers. A learned policy cannot produce "stopped because sanctioned at ≥ 700‰; predicted 610‰." The Assay can.
The arithmetic: an RL run at the scale that produces a strong policy for a game of this branching factor is conservatively 10⁶–10⁷ self-play games — 30–100× the tuning run, or 25,000–80,000 core-hours — plus the unsolved representation work, plus the risk that it does not converge to anything better than the hand-designed Assay. That is a plausible research project and an implausible shipping dependency.
The compromise that is real: distil a learned evaluation into the shipped integer weights offline. Train whatever you like on the farm, then fit the 27-feature Assay to it and ship the fitted integer weights. All the determinism and explicability properties are preserved because what ships is still a weighted integer sum, and the learning has done what learning is good at — finding weights a person would not have guessed. If that works, the natural next step is a small integer-quantised MLP over the same 27 features (64 → 32 → 1 in int32, ~2,000 multiply-accumulates, deterministic, ~10 KB of weights, well inside every budget in §11). That is a v2 option with a stated precondition — the distillation must first beat the linear Assay on the promotion gate — and not a v1 promise.
15. What this design is not doing, and why
Stated flatly, because an AI document's omissions are the part most often left vague.
- No full-game tree search, and no MCTS over the real engine. A full Cascade at late-large scale is ~1,200 activations plus per-player upkeep — I estimate 150–500 ms per simulated turn. A desktop's whole Veteran budget buys less than one simulated turn. MCTS needs thousands. The forward model has to be abstract, which is why the Sandtable exists.
- MCTS over the Sandtable was considered and rejected for the shipping configuration. Beam search over strategic seeds is better here for three specific reasons: the tree is shallow (3–5 plies of turns, not moves); the leaf evaluation is deterministic, so UCT's exploration bonus — which exists to manage rollout variance — has little to buy; and random rollouts would put PRNG consumption on the decision path, which §12 wants to avoid. The condition under which MCTS becomes right is specific: if we acquire a value function with genuine uncertainty estimates, or if opponent hypotheses become a sampled distribution rather than three fixed cases, then PUCT over that distribution is the natural structure and this decision should be revisited.
- No neural network in the shipped client, for the reasons in §14.2. Integer-quantised inference is a v2 option with a stated gate, not a v1 promise.
- No per-unit tactical search. Delegated entirely to Postures and Sanctions in core. The Staff
issues explicit Tasks only where a Sanction would decline something the plan requires — the same
escape hatch a human uses, and
CM-270already says a Task is its own authorisation. - No online learning. Weights are fixed for the life of a game and do not adapt to the player between games. Determinism, fairness, and support burden all point the same way.
- No cheating, in any form. No unfogged view, no resource bonus, no production bonus, no combat bonus, no knowledge of pending PRNG draws. Difficulty above Marshal, if a player wants it, is bought with the setup's disclosed asymmetries (extra starting cities, map advantage) and labelled as such. §12.3 makes the no-vision-cheat claim mechanically checkable rather than merely asserted.
- No coordination between AI seats. This is an adversarial game with no diplomacy; two AI seats must not implicitly ally, and they share no memory or channel.
- No modelling of human psychology. No bluffing model, no tilt detection, no adaptive difficulty inside a game. Tempting, and a different project.
16. How strong will this actually be?
The brief asks for rigour here and it is right to, because an over-promised AI that ships weak is the worst outcome available.
16.1 High confidence
The Constraint Floor (§10). Garrisons, Cadre, Depots, lift, endurance, production continuity, no idle units, no stranded formations. These are arithmetic against specifications that already exist in closed integer form, and they are where opponents in this genre visibly fail. An AI that is perfect here will be perceived as strong before it has made a single interesting decision.
Fog-honest belief (§4) and deadline-driven objectives (§4.5). Both are structurally easy in this game and structurally hard in most others.
Economic reasoning (§5.1). 12-economy.md gives exact payback formulas for Cadre and
02-units-and-industry.md gives one for Industry. The Staff performs arithmetic the designers
already did. It will make these decisions better than most humans, simply because most humans will not
do the sums.
16.2 Moderate confidence
Operational assignment via portfolio plus local search. The technique is sound and the landscape argument in §2.2 is plausible, but "operations are mostly separable" is an assumption. The test: run independent random restarts on the same Docket and measure how often they converge to plans within 5% of each other on the Assay. If they scatter, the landscape is rugged, and the answer is more restarts or simulated annealing rather than a redesign — but the cost goes up.
Risk-weighted plan choice (§9.2). The mechanism is elegant and the caution coefficient will need real
tuning; a badly set k produces either a suicidal or a catatonic opponent, and both are worse than a
mediocre one.
16.3 Low confidence — and the one place this ships weak
The Sandtable is the risk. If its ranking of plans is noise, then the entire L2 search layer is decoration: an expensive greedy heuristic with a random tiebreaker, and every millisecond spent above the Constraint Floor is wasted. This is exactly the failure mode where an AI is announced as advanced and plays like a script.
The falsification test, which should be built before the Sandtable is optimised:
Plan-ranking skill. Sample 200 turns from farm games. At each, take the top 5 plans by Sandtable score. Play each forward in the real engine for 5 turns against a fixed opponent policy and record the true Assay delta. Compute Spearman's ρ between Sandtable rank and true rank.
- ρ ≥ 0.5 — the Sandtable is earning its cost. Invest in depth.
- 0.3 ≤ ρ < 0.5 — marginal. Keep depth at 2 and spend the budget on local search instead.
- ρ < 0.3 — the Sandtable is not working. Replace it with a 1-ply Assay delta, cut the hypotheses to one, and ship the greedy-plus-local-search AI.
The bounded downside is the reason I am comfortable proposing it: the fallback is still a competent opponent. Greedy assignment plus local search on a static evaluation, over a good Docket, on top of a perfect Constraint Floor, is roughly what the better shipped 4X AIs are. We would lose the multi-turn concentration and the risk-weighted strategic dilemma, which are the two things I most want, but we would not lose the product.
16.4 The honest ceiling
This design aims to beat most players and lose to experts. It will not be superhuman, and the gap against a strong human will be in exactly the place §16.2 identifies: choosing the decisive axis and timing a concentration, which is where human pattern recognition over a hundred games is worth more than 25,000 integer steps. What the Staff will have that a strong human will not is that it never gets tired, never leaves a city empty on turn 140, always does the Cadre arithmetic, and always notices the Alarm on the turn it fires.
One further honest note. A hard-to-fix asymmetry: the Staff re-derives its plan every trigger, so it has no long-horizon commitment beyond the switching cost. A strong human plays a forty-turn plan. Mitigating this properly needs plan persistence with an explicit commitment ledger, and I have deliberately left it out of v1 because a half-built commitment mechanism produces an AI that stubbornly pursues a dead plan, which is worse than one that re-plans too often. It is the first thing I would add in v2, and open question 6 states the shape.
17. What could go wrong, and how playtest and the farm would show it
| Risk | Signal | Threshold that means we were wrong |
|---|---|---|
| The Sandtable does not rank plans | Spearman ρ, §16.3 | ρ < 0.3 — take the fallback |
| Local search lands in bad optima | Assay spread across independent restarts on the same Docket | > 15% spread means the landscape is rugged |
| Doom-stacking | Median formation size at the point of contact; share of engagements at frontage-saturation | Any drift upward means frontage limiting (§7.3) is not biting |
| Caution mistuned — catatonic | Share of turns with zero Seize or Raid operations assigned, midgame | > 40% means k is too high |
| Caution mistuned — suicidal | Share of assaults committed below 400‰ predicted | > 10% means k is too low |
| Fog leak | The §12.3 gate, in CI | Any byte difference. This is a gate, not a metric |
| Non-determinism | Same recorded view, two machines of different speed, byte-compare orders | Any difference |
| Tiers not monotone | Round-robin tournament, ≥ 1,000 games per pair | Any tier failing to beat the one below at ≥ 60% |
| Low tiers fail absurdly rather than plausibly | Constraint-Floor violations by tier: empty cities, stranded aircraft, idle production | Zero at every tier. A single violation at Recruit is a defect |
| Belief estimator is exploitable | Win rate of a scripted "hold indefensible cities to inflate the AI's fear" opponent | > 55% means force accounting needs damping |
| AI turn cost regresses | Steps consumed per turn, p50 and p99, by tier and scale | p99 above the tier's budget |
| The AI is boring to play against | Human survey: "did the opponent do anything that surprised you?" | Not automatable, and the most important row here |
| Characters are cosmetic | Character-signature tests (§13.2) | Any character whose signature metric is statistically indistinguishable from Balanced |
Two rows deserve emphasis. The Constraint-Floor row admits no failures at any tier — that is what separates "easy" from "broken," and it should be a build gate rather than a metric with a band. And the plan-ranking row is the one that decides whether this document was right; it should be built early, before anyone optimises the Sandtable, because it is the measurement that tells us whether to optimise it at all.
18. Two capabilities this architecture gets nearly free
Worth naming because they are usually separate projects.
AI substitution for a departed player. 10-turn-model.md §3.5 wants an AI to take over a
disconnected seat. Normally this is painful — the AI inherits a command structure it did not build and
either tears it down or is paralysed by it. Here the Staff reads the existing Formations, Postings and
Requisitions as pre-existing Operations on its own Docket, because they are expressed in exactly
the vocabulary the Staff plans in. The switching cost (A27) then makes it conservative by default: it
keeps the human's plan and changes only what its search says is decisively better. A returning player
finds their army roughly where they left it, doing roughly what they told it to.
A legible opponent. Because the Staff's output is Postures, Sanctions and Postings, and because
every automated action already emits a CommandDecisionRecord, an AI's turn is inspectable in the same
UI a human uses to audit their own automation. A post-game replay can show "III Corps: March to Ash
Ford; Engagement Favourable ≥ 700‰; chosen over Screen the Vell approach, which scored 240 lower."
That is a review feature, a debugging tool, and a teaching tool, and none of it needed to be built for
the AI specifically.
19. Contracts — what this subsystem needs from others
| From | What |
|---|---|
10-turn-model.md |
Initiative computable by any seat from committed state, so the Staff can reason about activation order (§9.3); the frozen order readable before the Cascade; activation index within the Cascade for Intent Freshness; guarantee that an AI seat's submission is one order delta per turn with no mid-Cascade input |
11-combat.md |
The exact per-mille predictor over a fog-limited knowledge view, and the pivot analysis of its §10 exposed as a callable that names the dominant unobserved field; the aggregate/support shape of its §5 so the Sandtable's frontage-limited exchange (§7.3) uses the same arithmetic as the real thing |
12-economy.md |
Closed-form integer Works, Levy, Integration, Unrest and Cadre-cost functions callable outside the turn pipeline, so the Sandtable's economy is exact rather than approximated; garrison requirement by class and Integration; supply-grade query for an arbitrary hypothetical position |
13-command.md |
Everything, unchanged. The Staff writes Postures, Sanctions, Formations, Groups, Postings, Consignments, Requisitions and Doctrines and reads them back. Two additions requested, both small: a read API for a seat's own Attention Event stream (already required to be emitted, CM-1610), so the Staff can use it as a re-planning trigger; and confirmation that CM-100's "identical for every AI seat" wording is understood as the AI uses the same core command layer, which is the assumption §2.3 rests on |
14-victory.md |
Per-Track progress functions and thresholds callable for a hypothetical position (the Sandtable needs to score futures); the banded progress values as the AI sees them, with band edges, so force accounting (§4.3) uses the published information and nothing more; the Warrant and Tally calendars as data; the deterministic Verdict vote policy this document owns — see below |
01-game-rules.md |
Chokepoints from map generation where derivable, or the terrain data to derive them once at load; Region membership per city; the abstract chunk graph the Atlas is built on |
02-units-and-industry.md |
Per-class capability data driving all unit reasoning — the Staff must never branch on a unit's identity, so that custom rosters play competently; Industry upgrade costs and outputs for the payback computation |
03-architecture.md |
Hierarchical pathfinding callable for abstract node-to-node distance; a MapClass-independent worker to run the Staff off the UI thread; the AI-private PRNG stream; the fog-leak property test extended to include an AI seat (§12.3) |
| Build pipeline | Headless self-play farm with the promotion gate of §14.1; the plan-ranking skill measurement of §16.3 as a first-class farm job, not an ad-hoc script |
19.1 The Verdict vote policy, since 14-victory.md asks for it
14-victory.md §8.3 requires "a deterministic Verdict vote policy derived from evaluation plus seed,"
and says most of its value lands in solo play. Here it is:
An AI seat votes to end the game when both hold:
- Its own arithmetic elimination from every enabled accumulative Track is proven by that document's own ceiling arithmetic (
progress + remaining_turns × max_gain < leader), and- Its Assay trend over the last 12 turns is negative, and a Sandtable run to the Horizon under its own best plan does not project it passing the leader's Standing.
Otherwise it votes to play on. Abstention is not used: an AI seat always votes.
Deterministic, derived from the evaluation the AI already computes, conservative in the right direction (it plays on whenever the arithmetic leaves a path), and it means a beaten AI concedes when it genuinely should — which is what makes the solo mop-up disappear, as that document hopes.
20. Open questions I could not settle
Is
press()the right convexity? The three-segment shape makes near-threshold progress worth disproportionately more, which is what drives the AI to contest Vigils. The breakpoints (500, 900) and slopes (1, 2, 6) are invented to hit a feel, not derived. They are prime self-play tuning targets, and it is possible the right answer is a smooth integer curve rather than three segments.Should the Assay be one function or one per Empire Stance? A single weight vector must serve both "I am expanding" and "I am defending my Seat," and the Stance currently only reweights the Docket, not the Assay. Per-Stance weight vectors would be more expressive and would quadruple the tuning surface. I lean single-function-plus-Docket-weighting because the tuning cost is real and the evidence for the alternative is speculative.
How many opponent hypotheses is the right number, and are three the right three? Passive / Continuation / Best-reply is a guess. A fourth — Opportunist, which assumes the opponent takes whatever I leave undefended — might be worth more than Best-reply and is a tenth of the cost. The farm can settle this by ablation and probably should before Elite tier is tuned.
Frontage is doing a lot of work on an unsettled rule.
02-units-and-industry.mdassumes one combat unit per land tile outside cities;11-combat.mdassumes stacks with a top defender. The Sandtable's frontage limiting (§7.3) is correct under either but the numbers differ substantially, and the anti-doom-stack property depends on them. This needs the stacking rule settled before the Sandtable is calibrated.Is trigger-based re-planning (§5.3) safe, or does it produce a lazy AI? The saving is large and the anti-staleness floor is 8 turns, but a slowly deteriorating position that never fires a trigger is exactly the case where re-planning matters most and would not happen. A gradient trigger on the Assay itself — re-plan when the Assay has fallen by more than X since the last plan — probably fixes it, and I have not worked out X.
Long-horizon commitment. §16.4 names this as the main strategic weakness. The shape of a fix is a commitment ledger: an operation the Staff has invested
Nturns in accrues a decaying bonus to its own continuation, so abandoning a half-executed concentration costs more than starting one. The danger is obvious — a badly-tuned ledger produces an AI that pursues a dead plan into a wall, which is a far worse failure than re-planning too often. Needs a designed decay curve and a hard abandonment override onImpossibleand on Seat exposure.Does the force-accounting estimator need damping, and how much? §4.3's estimator is exploitable by an opponent who understands it. Some exploitability is healthy — it is a bluffing surface — but I cannot tell from a desk whether the equilibrium is interesting or degenerate. The risk table has the measurement; I do not have the answer.
Should the AI use the Dispatch's adaptive player weight?
CM-1770's per-class weightPis client-side, non-hashed, and learns what a player cares about. An AI seat has no client and no player, so it should presumably not have aPat all. But an AI substituting for a departed human arguably inherits one. I lean toward "AI seats have noP, and a substitution discards it," but that is a small deliberate loss of the departed player's expressed priorities.Where does the Staff run in multiplayer? Server-side is the obvious answer for fairness and for async, but it puts the whole AI budget on the server for every AI seat in every game. Client-side execution with server verification is cheaper and reopens a trust question the existing spec set has already answered for plugins. This is a services-platform decision more than an AI one, and it should be made with the cost model in front of it.
Is one Atlas enough? A single 256-node graph must serve land, sea and air reasoning, and air units in particular have a radically different distance metric (endurance in turns, basing constraints). Three per-class distance matrices over one node set is the cheap answer and is what §4.4 proposes, but air may genuinely want its own node set — bases and carriers, not cities and chokepoints. I have not tested whether the shared node set degrades air planning.