ai-architecture.md · non-normative

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%.

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:

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:

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.

  1. Sort the Docket by value descending, ties by lowest Atlas node index.
  2. For each operation, take the best unassigned formation by fit; skip if fit < 200.
  3. Operations still short of requiredForce may 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.
  4. Leftover formations: Reserve (Hold at the theatre's Atlas node, Engagement Defend) up to the reserve fraction, then Screen on the nearest uncovered frontage, then Survey.

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:

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:


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

  1. Move. Each formation advances pace along its Atlas route. Arrival is a threshold on accumulated abstract cost.
  2. Clash. Where opposing formations contest a node or an edge, resolve one aggregate exchange (§7.3).
  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 applies 12-economy.md's consequences exactly: Integration reset to floor(Loyalty/2), Unrest by class, Industry degraded one tier, garrison requirement raised.
  4. Economy. Closed form, straight from 12-economy.md: effective Works floor(base × (20 + floor(4 × Int / 5)) / 100); Levy floor(pop × Int / 1000); Integration by the Cadre/Unrest/Reach table; Cadre cost floor(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.
  5. 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:

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:

  1. 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.

  2. 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.

  3. 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:

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:

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

  1. 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.md CM-120 and means the Staff and the command layer share one arithmetic discipline.
  2. 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.
  3. 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.
  4. 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.
  5. 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-1940 makes 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 seat P on 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:

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:

  1. 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.
  2. 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.
  3. Explicability. CM-2000 requires 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.

  1. 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.
  2. 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.
  3. 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.
  4. 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-270 already says a Task is its own authorisation.
  5. 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.
  6. 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.
  7. 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.
  8. 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:

  1. 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
  2. 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

  1. 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.

  2. 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.

  3. 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.

  4. Frontage is doing a lot of work on an unsettled rule. 02-units-and-industry.md assumes one combat unit per land tile outside cities; 11-combat.md assumes 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.

  5. 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.

  6. 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 N turns 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 on Impossible and on Seat exposure.

  7. 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.

  8. Should the AI use the Dispatch's adaptive player weight? CM-1770's per-class weight P is 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 a P at all. But an AI substituting for a departed human arguably inherits one. I lean toward "AI seats have no P, and a substitution discards it," but that is a small deliberate loss of the departed player's expressed priorities.

  9. 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.

  10. 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.