02-units-and-industry.md · 239 requirements

02 — Units and Industry

This document specifies what a unit is as data — the complete attribute schema and the closed capability-flag vocabulary that together let a custom unit set be a JSON file rather than a code change — and the Industry ladder, the per-city rating that decides both how much a city produces and which unit classes it may build. Industry is how this game delivers escalating complexity: rules that no unit exercises are invisible, so controlling when unit classes become available controls the entire complexity curve with no second system. It also specifies the production queue that turns Works into units, the rule that capture degrades Industry — the load-bearing anti-snowball mechanism this document owns — and the full shipped roster of twenty manoeuvre units plus three auxiliaries, with concrete stats, a one-sentence roleText each, and an explicit statement of every counter relationship. It says nothing about how combat resolves (11-combat.md), where Works and Manpower come from (12-economy.md), how orders are issued (13-command.md), or how terrain costs movement (01-game-rules.md).

Status: Draft v0.2 · Owner: unassigned · Depends on: docs/design/00-direction.md, docs/design/01-decision-turn-model.md, docs/design/units.md, docs/design/economy.md, 01-game-rules.md (terrain, movement, map, fog), 10-turn-model.md (Initiative, activation, Forming Up), 11-combat.md (resolution, modifiers, damage application), 12-economy.md (Works, Manpower, Integration, city classes and traits, supply), 13-command.md (Consignments, Requisitions, Doctrines, Dispatch), 03-architecture.md (determinism, integer arithmetic, canonical hashing)

Replacement notice, binding. This document replaces 02-unit-sets-content.md in its entirety. That file is a transcription of another game's unit tables and is void under docs/design/00-direction.md §2. No value, name, table, or rule carries across, and its US-NNN requirement numbers are not the ones used here. Every cross-document citation of a US-NNN identifier written before this file existed is stale and must be re-resolved against this document. See §16.

Naming note, binding. Strength always means 11-combat.md's persistent per-unit integer in [0, 100] (CB-100, CB-110), and it is the quantity 13-command.md calls strength. Its maximum is 100 for every unit in the game, so CM's "current and maximum strength" are strength and the constant 100; there is no per-class hit-point maximum and no hits field. Durability differences between classes are carried by tough (US-230, CB-160), which divides incoming losses rather than raising a maximum. 12-economy.md's Works is the build currency this document prices units in; Manpower is the second cost axis. There is no third resource and no per-turn upkeep of any kind (§2.3).

Naming note, role. This document's one-sentence description field is roleText (US-070). 11-combat.md's role (CB-150) is a six-value enumeration the combat core reads, and this document carries it under that name in US-230. The prose field was renamed rather than the enumeration because the enumeration is consumed by name in CB-510, CB-560, CB-1450, CB-1490 and CB-2320, and by the terrain profile of every terrain type, while the prose field is display text read only by 04-ui-ux.md and 07-modding-content.md. Renaming the smaller surface was the cheaper correction; the two documents cannot both own the name.


1. Scope, ownership, and the arithmetic contract

US-010 This document MUST be the single normative source for: the unit definition schema; the closed capability-flag vocabulary; unit instance state; the Industry rating and its ladder; the buildable-set test; the production queue and its funding rules; Industry degradation on capture and on strike; and the shipped roster's stat lines.

US-020 This document MUST NOT define: combat resolution, damage application, or combat modifiers (11-combat.md); Works generation, Manpower levy, Integration, Unrest, city classes, city traits, supply states, or garrison requirements (12-economy.md); terrain movement costs, passability, stacking limits, or the coastal test (01-game-rules.md); Initiative computation, activation order, Forming Up, or Stance (10-turn-model.md); order stacks, Postures, Sanctions, Consignments, Requisitions, or the Dispatch (13-command.md); victory conditions (14-victory.md).

US-030 Every quantity defined in this document MUST be an integer. No unit attribute, cost, multiplier, or derived value in this document MAY require Q16.16 fixed-point or floating-point arithmetic. Where a fractional ratio is needed it MUST be expressed per-mille as an integer numerator over an implicit denominator of 1000, and applied as floor(value × permille / 1000).

Rationale: the unit and production layer is the most heavily modded surface in the game and the one most likely to be edited by hand. Restricting it to integers means a custom set cannot introduce a desync through rounding, means every cost and multiplier is exactly what it reads as on the card, and means this whole subsystem needs no fixed-point review. The economy subsystem reached the same conclusion independently for the same reasons.

US-040 Where a chain of per-mille multipliers is applied to one value, each MUST be applied in the order stated by the requirement that introduces it, with a floor after each step. An implementation MUST NOT reassociate, batch, or defer the divisions.

Rationale: floor(floor(a × p / 1000) × q / 1000) and floor(a × p × q / 1000000) differ, and both are defensible. Fixing the order is what makes the result reproducible across implementations, which the canonical hash requires.

US-050 All iteration over unit definitions in the simulation core MUST occur in ascending lexicographic order of unit id. All iteration over a city's production queue MUST occur in queue index order. No engine behaviour MAY depend on the order in which unit definitions were loaded, on hash-map iteration order, or on object identity.

Enforced 2026-09-15, and it had been violated in the one place where it cost the most. standard.json is written in design order — scout, infantry, field_gun, transport — which reads far better for a human and is not what this requirement asks for. Two readers took that order straight: the buildable list a city offers, and unitSetDigest.

The digest is the one that mattered. It is hashed into the canonical state as unitSet.hash (US-2090), and US-2100 makes a save or replay whose set hash differs refuse to load — so moving one unit's block within the JSON, with no change to any number, invalidated every existing save and every game in the thirty-three-game replay corpus. That is exactly the harm this requirement's last sentence names, and it was reachable by a purely cosmetic edit that no reviewer would question.

The fix is a sort by id before the hash and before the offer, through one shared definitionsInOrder, so the rule has a single home rather than a copy at each reader. It moved the hash, which is the forty-second re-emit of the P0 containers and a regeneration of the corpus — both first-class operations here, recorded at artifacts/README.md with the reason. Worth stating plainly: this was not a rules change and not a shape change. No field joined the canonical form and no unit number moved; the same twenty-three definitions are now read in a defined order instead of an incidental one.

Held at core/test/units/definition-schema.test.ts, and the row that matters is the order-independence one: it hashes a deterministically shuffled copy of the shipped set and requires the same digest, so the dependence cannot come back.

US-060 No code in the simulation core MAY branch on a unit's id, name, or display text. Every behavioural difference between units MUST be expressed through the attributes of §2 and the flag vocabulary of §3. This MUST be enforced by a CI lint over the core package.

Rationale: this is the rule that makes custom unit sets a real feature rather than a promise. A single if (unit.id === 'submarine') in the core is the end of it: from that moment a modder's new unit can never behave like a submarine, and the promise silently becomes a lie. It is mechanically checkable, so it is checked.


2. The unit definition schema

A unit definition is a record in a unit set. It is pure data: integers, enumerated strings, and booleans. The complete schema follows. Every field is listed; there are no optional behaviours hidden behind absent fields except where a field is explicitly nullable.

2.1 Identity and taxonomy

US-070 Every unit definition MUST carry the following identity fields.

Field Type Constraint Meaning
id string matches ^[a-z][a-z0-9_]{1,31}$, unique within its set Stable key. Referenced by saves, replays, and the canonical hash.
setId string matches ^[a-z][a-z0-9_]{1,31}$ The unit set that owns this definition.
name string 1–32 UTF-16 code units (US-076) Display name.
roleText string 1–160 UTF-16 code units (US-076); a single sentence (US-1620) The unit's one-sentence role (§9). Renamed from role; the name role is 11-combat.md's combat-role enumeration and is carried in US-230.
class enum one of US-090 Taxonomy key for Requisitions, Doctrines, trait discounts and AI evaluation.

US-076 The name and roleText bounds of US-070 are loader input guards, not legibility rules. They MUST be counted in UTF-16 code units and MUST NOT be enforced in grapheme clusters, columns, glyph counts, or rendered width. Legibility is an obligation on 04-ui-ux.md (US-2190): every string that satisfies US-070 MUST render legibly in the narrowest surface that presents it — the unit card and the encyclopedia index (US-930, US-950) — eliding where a locale's script is wider than that surface and presenting the full string on the same screen. Separately, every name and roleText in the shipped roster MUST fit unelided in that surface in every shipped locale; a custom set MUST NOT be required to.

Rationale: the caps used to read "1–32 characters", which named neither what a character is nor what the limit was for. Both matter. The unit matters because US-800 requires the validator to be a pure function of the set document producing identical diagnostics on every platform, and 03-architecture.md AR-100 bars locale- and ICU-dependent string handling from simulation code for the reason that ICU data differs per engine and per build. Grapheme-cluster segmentation is exactly such a table: two clients on different Unicode versions can disagree about the length of the same emoji sequence, and because set acceptance feeds the content hash of US-2090 that disagreement presents as one client refusing a set the other loaded. A UTF-16 code-unit count needs no table, cannot drift with a library upgrade, and is the same unit AR-100's ordinal comparison and US-070's id grammar already use.

And the purpose matters because a character count is a proxy for rendered width that only holds in a fixed-width, single-script display. Thirty-two CJK characters are about twice the width of thirty-two Latin ones, and under 07-modding-content.md MOD-210 a name may be a localization key whose resolved translation is the thing actually drawn — so the authored length says nothing useful about what appears on the card. Width can only be judged where it can be measured, which is the rendering surface, so that is where the requirement is placed. What is left here is a bound on input size, and the two numbers are set to never bind on honest content: 32 matches the id grammar's own bound and is 60% longer than the longest shipped name (Administrative Cadre, 20 code units), and 160 is a ceiling on one sentence, where US-1620 is the requirement that actually constrains roleText and this count only stops a set shipping a paragraph.

US-080 A unit definition's id MUST NOT change across game versions once shipped. Renaming a unit MUST be done by changing name only. Rationale: id appears in every saved game and every replay; changing one silently invalidates every stored game that referenced it.

US-090 class MUST be one of exactly the following eighteen values. This enumeration is closed; adding a value is a schema version change.

recon · infantry · garrison · artillery · armour · engineer · air_defence · fighter · bomber · missile · transport_sea · transport_air · escort · submarine · capital_ship · carrier · administration · logistics

US-100 class MUST be the key by which 13-command.md's Requisition rows (CM-1240), Doctrine defaults (CM-790) and 12-economy.md's class-scoped city-trait discounts select units. No subsystem MAY select units by id for these purposes.

Held 2026-09-23. The mechanical half already existed and cited nothing: scripts/check-unit-id-branching.mjs runs in verify and is this clause made enforceable. The test names the connection, and checks the taxonomy is a taxonomy — fewer classes than units, which is what makes the rationale's promise true: “a Requisition that names armour keeps working when a mod adds a third tank; a Requisition that names heavy_armour does not.”

Rationale: a Requisition that names armour keeps working when a mod adds a third tank; a Requisition that names heavy_armour does not. Taxonomy is the seam that lets the command and economy layers be written once against a roster they do not know.

US-110 Every value of the class enumeration MUST be carried by at least one unit in the shipped roster. A CI check MUST fail if any value is unused.

Rationale: an enumeration value no shipped unit exercises is an untested code path that a modder will be the first to discover. If we cannot justify a class with a unit, we do not ship the class.

2.2 Domain, movement and vision

US-120 Every unit definition MUST carry the following movement and detection fields.

Field Type Range Meaning
domain enum land · sea · air Which tiles the unit may occupy. 01-game-rules.md owns passability.
terrainClass enum foot · wheeled · tracked · naval · air Index into the terrain movement-cost table owned by 01-game-rules.md.
draft int 0–3 Sea only. Minimum water depth class the unit may enter. 0 for non-sea units.
move int 0–32 Movement points per turn.
vision int 0–12 Sight radius in tiles against surface units and terrain.
visionAir int 0–16 Sight radius against units with domain = air.
endurance int 0–8 Air only. Turns the unit may spend away from a base before it is lost. 0 for non-air units.
zoc bool — Whether the unit exerts a zone of control on adjacent tiles.
mayOccupyCity bool — Whether the unit may end movement inside a friendly city.

US-130 terrainClass MUST be consistent with domain: a land unit MUST use foot, wheeled or tracked; a sea unit MUST use naval; an air unit MUST use air. A CI check MUST enforce this.

US-140 visionAir MUST be greater than or equal to vision for every unit. Rationale: an aircraft is visible against the sky from further away than a tank is visible against a hillside. A unit that saw aircraft less well than it saw ground would need a story nobody has, and permitting it invites a modder to build an accidental blind spot.

US-150 draft MUST be non-zero if and only if domain = sea. A sea unit MUST NOT enter a water tile whose depth class is lower than its draft. Depth classes and their map representation are owned by 01-game-rules.md.

US-160 move MAY be zero. A unit with move = 0 MUST still activate, and MAY act (attack, bombard, build, repair) from the tile it occupies.

US-170 The three movement classes foot, wheeled and tracked MUST each be carried by at least one shipped land unit, and their terrain cost profiles MUST differ from one another in at least three terrain types. Rationale: three land movement classes that behave nearly alike are two classes of confusion for no strategic return. The distinction has to be visible in play or it should not exist.

US-2240 This document MUST publish the integer MAX_DISPLACEMENT = 322: a sound upper bound on the map distance, in tiles under 01-game-rules.md GR-170, that any unit of any legal unit set may be displaced in one turn, including every transport, carriage, road and rail effect and every chained transfer legal within one turn (14-victory.md VC-2650, VC-3130). The bound MUST hold for every legal move value (US-120: 0–32), not merely the shipped roster. It is the tightest bound the P0 rules admit, and it is the sum of four independent maxima:

  1. The unit's own movement. The MP allowance is move × 4 (01-game-rules.md GR-280), so at most 32 × 4 = 128 MP per activation; banked movement (13-command.md CM-1560) adds at most one further normal allowance, capped at that allowance, for at most 256 MP in one activation. The cheapest entry cost any legal set and map may present is 1 MP — wheeled on a Road (GR-420) — so the unit's own movement displaces it at most 256 tiles. (The GR-330 minimum move is not additive: it applies only where the unit has spent no MP at all, and then it reaches one adjacent tile.)
  2. The embarkation's repositioning. A unit that embarks onto a transport on an adjacent water tile ends on the carrier's tile (GR-640, GR-650), at most 1 tile beyond where it stood. The embarkation's cost is all its remaining MP (GR-640), and no MP are required to remain: a unit that spent its entire allowance moving may still embark for 0 MP. Reading recorded for the spec owner: the alternative reading — that the step to the water tile itself costs 1 MP — would lower the bound by one tile (to 321); the literal cost text is what is published here, and the difference is one tile of permissiveness in VC-2660's guard, which errs in the safe direction.
  3. One transport leg. The carrier's own activation — banked, at most 256 MP — moves it across its domain at the cheapest entry cost of 4 MP per tile (naval on Coastal Sea or Open Ocean, GR-290; air at 4 MP per tile regardless of terrain, GR-610), displacing its cargo at most 64 tiles. A unit unloaded during a Cascade is spent (10-turn-model.md TM-1840) and receives no second activation, so there is at most one leg and no own-movement after it; a unit that boards after its own activation does not activate again (TM-1860).
  4. The disembarkation. Onto a legal adjacent land tile (GR-640), at most 1 tile.

256 + 1 + 64 + 1 = 322. The airdrop alternative (GR-670) is shorter — the dropping carrier's own 256 MP at 4 MP per tile is 64 tiles, and the dropped unit is spent (TM-1840) — and is covered by the same sum. No P0 rule displaces a unit faster than its activation's MP at the cheapest entry cost: the P0 Railhead (12-economy.md EC-1590) is a supply Reach trait that moves no unit, so rail contributes zero in P0, and no other chained transfer the specification admits exceeds the sum.

Rationale: this bound is the whole basis of 14-victory.md VC-2660's reach guard, and a bound that is not sound for a legal custom set makes the elimination arithmetic unsound for that set — VC-2690 would then publish an elimination a player could check and dispute. Overstatement is safe in exactly one direction: it makes the guard permissive and the bound computes fewer eliminations, never wrong ones. The derivation is stated in full because the number is only as good as its chain of maxima, and the chain's weakest links are not on a unit card — the cheapest entry cost is an installation (GR-420), the banking cap is 13-command.md's (CM-1560), the spent rule is 10-turn-model.md's (TM-1840) — which is why the requirement lives here: this document owns the move range the chain starts from.

2.3 Cost

US-180 Every unit definition MUST carry the following cost fields.

Field Type Range Meaning
worksCost int 1–9999 Works consumed to complete one unit.
manpowerCost int 0–999 Manpower consumed at completion.
costEscalationPerOwnedPerMille int 0–2000 Per-mille increase in both costs per unit of this class the ordering player already owns or has in production.

US-190 A unit's price at the moment an order is enqueued MUST be computed as:

Held 2026-09-23, and the finding is how quiet this formula is. On the shipped roster the escalation is a no-op for every class but one: US-220 requires costEscalationPerOwnedPerMille to be zero for every combat, transport and engineering class, so escalation is 1000 and floor(cost × 1000 / 1000) is the catalogue cost. Exactly one class escalates — administration, the Cadre, which is the anti-snowball engine and the one thing the design means to get dearer.

That is worth a row precisely because it is invisible: a reader of this requirement reasonably expects prices that climb, and on this roster one does. The test also pins the order — "any city-trait discount … MUST then be applied" — against the economy’s own note at the site, because the two orders are not interchangeable: a discount applied first would be discounted again by the escalation.

n            = count of units of this class the player owns or has in production
escalation   = 1000 + n × costEscalationPerOwnedPerMille
worksPrice   = floor(worksCost    × escalation / 1000)
manpowerPrice= floor(manpowerCost × escalation / 1000)

Any city-trait discount owned by 12-economy.md MUST then be applied to worksPrice and manpowerPrice, each as a per-mille multiplier with a floor, in ascending lexicographic order of trait name where a city carries more than one applicable trait.

US-200 The price computed by US-190 MUST be stored on the production order at the moment it is enqueued and MUST NOT be recomputed while that order remains in the queue.

Rationale: an escalating price that rises while you are paying it is a moving goalpost, and the player cannot plan against it. Fixing the price at enqueue time means the queue you see is the queue you get, and it makes the escalation a decision about when to commit rather than a punishment for committing slowly.

US-210 There MUST NOT be any per-turn upkeep, maintenance, drain, or efficiency penalty charged against a unit for existing, and no production rate in this game MAY be a function of a player's total unit count.

Rationale: this is the direction brief's named weakness (§6, "opaque anti-snowball economics") and it is refused outright. A unit is paid for once, at commissioning, in two visible currencies. The brakes on growth are Industry degradation on capture (§6), the escalating price of administration (12-economy.md), integration time, and supply geography — every one of them a thing the player can see, price, and plan around. A hidden tax on having an army punishes the exact behaviour the game is about.

US-220 costEscalationPerOwnedPerMille MUST be zero for every unit of a combat, transport or engineering class. It MAY be non-zero only for classes whose scarcity is itself a designed brake.

2.4 Combat attributes

US-230 Every unit definition MUST carry the following combat fields, and exactly these. Their meanings, their scale and their consumption are owned by 11-combat.md; this document owns their values (CB-150, CB-2760).

Field Type Range Shipped range Meaning
atk int ≥ 0 0–132 Offensive base on 11-combat.md's common scale. 0 means the class may never initiate or support a Clash (CB-230).
def int ≥ 0 12–84 Defensive base on the same common scale.
tough int ≥ 20 20–240 Divides incoming losses (CB-160). 100 is the reference value the whole scale is read against.
role enum Foot · Armor · Gun · Recon · Naval · Air all six used 11-combat.md's combat role: indexes the role × terrain table (CB-1450) and counts toward combined arms (CB-560).
retreatClass enum Ground · Detached both used Behaviour on break (CB-1330).
engages set of enum subset of {land, sea, air} — The defender domains against which this class may initiate a Clash.
flak int 0–4 0–4 Air-defence tier (US-260).
bombardRange int 0–4 0–3 Indirect-fire range in tiles (CB-1680).
bombardShots int 0–4 0–2 Bombardments this class may resolve per turn (CB-1680).
initiativeBase int 0–100 — The unit's class base term in the Initiative score owned by 10-turn-model.md.

Rationale for stating a shipped range without a schema ceiling on atk, def and tough: CB-150 declines to fix an upper bound and CB-2760 requires this document to state the range that actually ships, because the binding constraint is CB-2510's exactness bound on a four-factor product, not a bound on any one stat. CI asserts the shipped maximum against that product (CB-2685); at atk 132 the largest §5 intermediate is about 2³⁵, four orders of magnitude inside 2⁴⁷.

US-235 There MUST NOT be a per-unit hit-point maximum. A unit's condition MUST be 11-combat.md's strength on the normalised [0, 100] scale (CB-110), identical for every class, and class durability MUST be expressed only through tough.

Rationale: this is the load-bearing consequence of adopting 11-combat.md's model, and it is worth stating as a prohibition rather than leaving as an absence. A hit-point maximum and a tough divisor are two encodings of the same idea, and shipping both would let a modder express durability twice, inconsistently, with no rule saying which wins. One bar in the interface means one number in the data.

US-240 A domain absent from engages MUST make an Assault, a Reaction or a supporting contribution against a defender in that domain illegal at order time, reported to the player as an unachievable order. It MUST NOT be resolved as a weak attack, and it MUST NOT be expressed as a low atk.

Rationale: this is where the old per-domain attack vector's absolute zeros went, and they are better off here. "This thing cannot touch that thing" was a zero in one of three attack columns; under a single atk there is no column to zero, so the prohibition needs its own field or it silently becomes "attacks very badly", which is a different game — a submarine that can shell a hilltop at 10% effectiveness is a submarine that will be ordered to do it. A capability set is also the only form the rule can take that the client can grey out with a sentence, per US-370.

US-250 def MUST be a single value applied against every attacker, from every domain. There MUST NOT be a per-domain defence vector.

Rationale unchanged in substance from the previous model and strengthened by it: 11-combat.md computes one defensive contribution from one def (CB-480), so a vector would have nowhere to go. Asymmetry between "hard to hurt from the air" and "hard to hurt on the ground" is carried by flak and highAltitude, which are two numbers a player can hold rather than nine.

US-260 flak MUST be an integer tier in [0, 4], not a strength on the atk/def scale, and MUST have exactly two effects, both owned by 11-combat.md:

  1. where the lead attacker's role is Air, it adds +6 × flak to that attacker's shock in the break check (CB-965);
  2. flak ≥ 2 is the threshold that permits a class to engage a highAltitude unit at all (US-660).

It MUST NOT contribute power, resolve or volatility, MUST NOT apply when the unit is itself the attacker, and MUST NOT resolve as a separate exchange before the Clash.

Rationale, and this is a real change of mechanism rather than a rescaling. The old flak was return fire resolved in its own round before the attacker's strike; 11-combat.md has no rounds (CB-020) and its single exchange already returns losses to the attacker through La (CB-870), so the "hits back" half of flak is now in the model generically and a separate flak exchange would double-count it. What the generic exchange does not carry is the part that matters — that anti-aircraft fire mostly does not down the aeroplane, it makes the pilot break off the run. That is a shock effect, not a damage effect, and 11-combat.md already has an attacker break check to put it in. At tier 4 the term is +24 against a typical attacker resolve of 30, so an Air Defence Battery drives off a Bomber it could never destroy, which is exactly what US-1490 claims for it and what the old damage-shaped flak never quite delivered. Keeping it a tier rather than a strength is what stops a modder tuning it against atk and discovering it is on a different scale.

US-270 An Air-role attacker MUST break off a failed attack by the ordinary falter rule (CB-1040 outcome 5, CB-1150) and MUST NOT carry a per-class round limit. There MUST NOT be a strikeRun field.

*Held 2026-09-23, and both halves. The first is easy: no definition carries the field. The second is the one worth a test, because it is the half that quietly stops being true when somebody tunes a number — the rationale’s claim that the asymmetry "survives for free and without a rule, because air classes carry flak 0". Every air unit in the shipped roster is now checked for it, and a mutant that gives the fighter flak is killed.*

Rationale, and this is the honest disposal of a mechanic rather than a rename. strikeRun bounded the number of resolution rounds before a unit disengaged; 11-combat.md resolves a Clash as a single exchange, so there are no rounds to bound and the field has no referent at all. Its design intent survives intact and in two pieces: an air attack that goes badly is thrown back by the falter check and barred from attacking again that turn (CB-1150), and the flak tier of US-260 is what makes that outcome likely against a defended target. The asymmetry the old rule bought — aircraft break off from ground targets, aircraft cannot break off from each other — survives for free and without a rule, because air classes carry flak 0: an air-to-air Clash gets no flak shock, so a fighter that meets a bomber presses the attack home on the ordinary arithmetic. Air superiority stays decisive rather than attritional, which was the whole point of US-270.

US-280 initiativeBase MUST be the only unit-definition input to Initiative. 10-turn-model.md owns every other term and owns the formula. Creation sequence MUST remain the tiebreak that makes the activation order total.

US-290 initiativeBase values MUST be assigned to express when in a battle a unit commits, not how fast it moves: reconnaissance and prepared fires open, lift and screening forces position, exploitation forces follow, assault infantry and engineers go in last.

Rationale: this is not decoration — it is what makes combined arms possible inside a single turn. Artillery must activate before the infantry it is supporting, or the bombardment lands after the assault and the game's most interesting mechanic cannot be executed at all. Ordering by speed would put the guns last and quietly break it. Ordering by commitment sequence puts the guns at 72–74 and the infantry at 32, which is exactly the ordering an operation needs.

2.5 Transport

US-300 Every unit definition MUST carry the following transport fields.

Field Type Range Meaning
slots int 0–8 Slots this unit consumes as cargo. 0 means it cannot be carried.
capacity int 0–16 Slots this unit provides to cargo. 0 means it carries nothing.
carryDomains set of enum subset of {land, sea, air} Which cargo domains it accepts. Empty if capacity = 0.
carryMaxSlots int 0–8 Largest single cargo item it accepts, by slots. 0 if capacity = 0.

US-310 A unit MAY be loaded onto a carrier if and only if all of: the carrier's capacity minus its currently used slots is at least the cargo's slots; the cargo's domain is in the carrier's carryDomains; the cargo's slots is at most the carrier's carryMaxSlots; and the cargo's slots is non-zero.

US-320 Cargo MUST NOT count toward any tile stacking limit owned by 01-game-rules.md; only the carrier occupies the tile.

US-330 Cargo MUST NOT contribute flak, vision, zoc, def, tough or any other combat value to its carrier, MUST NOT be counted in the carrier tile's stackExtra (CB-1930), and MUST NOT be attacked independently of it.

Rationale: cargo that fights from inside the hold turns every transport into a warship and removes the entire reason escorts exist. The one exception is assaultLanding (US-560), which is a purchased capability on one hull rather than a general rule.

US-340 A carrier MUST NOT be loaded as cargo onto another carrier. Nested transport MUST be rejected at order time.

2.6 Build requirements

US-350 Every unit definition MUST carry a buildRequirements record with the following fields.

Field Type Range Meaning
minIndustry int 1–4 Minimum city Industry (§5).
requiresCoastal bool — The producing city must be coastal for this unit's terrainClass.
requiresTraits set of string 0–3 entries City traits (12-economy.md) the producing city must carry.
minCityClass enum any · town · city · metropolis Minimum city class.
minIntegration int 0–100 Minimum city Integration (12-economy.md).

US-360 A unit definition U MUST be buildable in city C by player P if and only if all of:

  1. U.buildRequirements.minIndustry ≤ min(C.industry, P.industryCap)
  2. requiresCoastal is false, or C is coastal for U.terrainClass per 01-game-rules.md
  3. every trait in requiresTraits is present on C
  4. C's class is at least minCityClass
  5. C's Integration is at least minIntegration
  6. U.setId is the unit set in force for this game

US-370 Every clause of US-360 MUST be evaluated identically for AI seats and for human seats, and the failing clause MUST be reportable to the client so a greyed-out build option can state why it is greyed out in one sentence.

Rationale: a disabled button with no explanation is the single most common way a strategy game teaches a player that it is arbitrary. Six clauses is a small enough set that the reason is always one short sentence.

2.7 Capability parameters

US-380 Every unit definition MUST carry the following parameterised capability fields. A value of 0 means the capability is absent. bombardRange and bombardShots are not here: 11-combat.md consumes them as combat stats (CB-150), so they are carried in US-230 with the rest of that set.

Field Type Range Meaning
interceptZone int 0–4 Radius within which hostile air ending movement is engaged automatically, as a Reaction (CB-1770).
cityStrike int 0–2 Severity of works strikes this unit may make (§6.4).
repairAdjacent int 0–4 strength per turn restored to an adjacent friendly unit.
repairEmbarked int 0–4 strength per turn restored to units carried as cargo.
buildables set of enum subset of {road, airfield, fort, bridge} Terrain constructions this unit may make. 01-game-rules.md owns their effects.
commandRating int or null 1–24 or null Optional command span override for 13-command.md (CM-2170). Null in the shipped roster.

US-390 bombardRange and bombardShots MUST both be non-zero or both be zero. A unit with non-zero bombardShots MUST have a non-zero atk and MUST have a non-empty engages.

Ruling, 2026-09-05. The clause read "MUST have land in engages", and three rows of the shipped roster — the Corvette, the Destroyer and the Cruiser — bombard while engaging sea alone, so the roster and the rule could not both be true. The rule was the error. Which domains a bombardment may be fired at is already decided by engages through US-240, for every kind of attack; a second statement of it here that admitted only one domain was redundant for the artillery it was written against and wrong for every warship. What the clause is actually for is that a class which fires at range must be able to engage something — a bombarder with an empty engages would be an order the player can issue and the engine can never resolve — and that is what it now says. No roster value moved.

US-395 bombardShots MUST NOT exceed 2 for any unit in the shipped set.

Rationale, and it is arithmetic rather than taste. Under 11-combat.md a bombardment is a whole Clash, not a round: one shot against a city defender adds 3 × Ld + 5 ≈ 23 disorder, and that defender has fought, so its city recovery is halved to floor(45 / 2) = 22 (CB-2080). One shot therefore nets +1 and accomplishes nothing; two net +24; three make real progress. A class with three shots is a gun line in one unit and can reduce a fortification single-handed, which is precisely the concentration decision CB-1750 asks this document to protect. The shipped Field Gun accordingly carries 1 shot and the Heavy Artillery 2 — the same total the old model expressed as 2 and 3 rounds, re-read against a procedure in which a "shot" is an engagement rather than a die roll.

US-400 commandRating MUST be permitted to be null, and MUST be null for every unit in the shipped roster. Where it is null, the default command span of 13-command.md applies unchanged.

Rationale: the field exists because 13-command.md asked for it and a custom set may want a headquarters unit. Shipping it null everywhere is honest: we are not shipping a headquarters unit, and pretending otherwise would put an untested code path in the core.

2.8 Unit instance state

A unit definition is shared and immutable. A unit instance on the map carries the mutable state below. This is authoritative game state and contributes to the canonical hash.

US-410 Every unit instance MUST carry: defId (the definition id), owner, sequence (the monotonically increasing global creation number, owned by 10-turn-model.md as the Initiative tiebreak), tile, enduranceRemaining, cargo (ordered list of carried instance references), carriedBy (instance reference or null), and movePointsRemaining.

It MUST additionally carry the combat state 11-combat.md defines — strength, disorder, xp and shaken (CB-100) — and the entrenchment counter 10-turn-model.md defines (TM-700). This document MUST NOT restate the type, range, initial value or update rule of any of those five fields; it names them here only because they are part of the same serialised instance and of the same canonical hash.

Rationale for naming fields this document does not own: a reader of the schema needs the whole instance in one place, and a reader of the save format needs to know what is in it. Restating the ranges would be the second copy that goes stale, which is the defect this reconciliation exists to remove.

US-420 A unit's condition MUST persist across turns. It MUST NOT reset at turn boundaries and MUST be changed only by 11-combat.md's Clash losses (CB-870), its recovery table (CB-2070), the per-unit repair modifiers of §14.2, and 12-economy.md's isolation attrition (EC-1740).

Ruling, 2026-09-13, and binding. The list was incomplete, and a test testing it is what found that out. EC-1740 reduces strength by ceil(maxStrength × pct / 100) for every Isolated unit at EC-1990 step 2, it is implemented, it is covered, and EC-860 names attrition alongside combat and capture as an ordinary way to lose a unit — so a "changed only by" enumeration that omitted it was false of the shipped engine rather than a constraint on it. Attrition is added rather than forbidden because this document does not own supply: §16's US-2130 already requires 12-economy.md to provide "supply states, referenced by US-1970", and EC-1740 is the consequence of those states that acts on a unit's condition.

How it was found is worth recording, because it is the same shape as the day's other findings read from the other side: a row written to assert that a damaged unit recovers across a turn boundary came back with the unit five strength lower, twice. A lone unit standing on its own city at Integration 0 is not inside its own supply fill, so it was Isolated and took the 5% rather than the city's +12. The figure was correct and the requirement's list was not.

Rationale: persistent damage is what makes a unit a formation with a history rather than a token that is either alive or not. It creates the repair economy, gives withdrawal a purpose, makes a costly victory genuinely costly, and gives the Engineer and the Carrier something to do. It is also the reason the direction brief's complaint about flat attrition combat has an answer: outcomes are graded, not binary. The scale on which the grading happens is 11-combat.md's, and there is only one of it.

US-430 A unit whose strength reaches 0 MUST be removed from the map. 11-combat.md owns when and how strength is lost (CB-130).

US-440 A unit instance MUST NOT store a veterancy grade. Grade MUST be derived from xp by 11-combat.md (CB-170, CB-1600) at every point of use, and xp MUST be the only stored experience quantity.

Rationale, and it reverses an earlier decision in this document deliberately. The old field was a stored integer in [0, 3] that no requirement here read and that two other documents claimed to own; 11-combat.md meanwhile awards experience continuously (CB-1210) and derives four grades from thresholds (CB-1600). A stored grade beside a stored xp is a denormalisation with no benefit and one obvious failure — the two disagreeing after a load, a mod, or a scenario edit — so CB-170 forbids it and this document carries no such field. 10-turn-model.md's Initiative term reads the derived grade (TM-930 row 4) and does not need it stored either.

US-450 enduranceRemaining MUST be in [0, definition.endurance] and MUST be maintained per §14.3.

US-460 A unit's instance state MUST be fully serialisable as integers and references, with no derived caches that are not recomputable from it. The canonical hash MUST cover every field named in US-410.

Held 2026-09-23 as packages/core/test/units/schema-contract.test.ts, with US-410. "The canonical hash MUST cover every field named in US-410" is a checkable claim about unitToCanonical and nothing asserted it — a field on the instance and missing from the canonical form is a field two clients can disagree about while both hashing the same. Two mutants that drop a field are killed, as is one that puts a Map into the canonical form.

The clauses use the schema’s names and the engine uses its own, so the mapping is written down, and writing it down is half the value: a reader of US-410 and a reader of UnitState had to do it in their heads. Four are plain renames — defId/classKey, sequence/creationSequence, carriedBy/carrier, movePointsRemaining/bankedMovement. Two are not.

cargo — "ordered list of carried instance references" — is not a field at all, and that is this requirement working rather than an omission. The engine stores the relation once, on the cargo side, as carrier; the list is its inverse, and storing both would be exactly the second copy "no derived caches that are not recomputable from it" forbids. And movePointsRemaining is bankedMovement, which is a real difference rather than a rename: CM-1550 banks unspent allowance from an Opportunity or Blocked halt, and the allowance itself is move × 4 (GR-280), derived from the class rating rather than stored.


3. The capability flag vocabulary

Flags are booleans. Together with the parameterised fields of §2.7 they are the complete and closed vocabulary of unit behaviour. There is no scripting hook in a unit set.

US-470 The set of capability flags MUST be exactly the eighteen values enumerated in §3.1–§3.5. A unit set MUST NOT introduce a flag, and the core MUST reject a set that names an unknown flag rather than ignoring it.

Note, recorded because the question recurs: 01-game-rules.md GR-1270 raises observation fidelity for a recon observer, and no recon flag exists in this vocabulary. That is deliberate on both sides. GR-1270 reads the class enumeration value recon of US-090, which US-110 guarantees a shipped unit carries, and GR-1700 withdraws that document's earlier request for a flag on exactly the ground this requirement states: a closed vocabulary that invalidates a set naming an unknown flag cannot be satisfied by a rule written against a flag it does not contain. Reinstating it would give one rule two data hooks and would need a schema version change.

US-480 Every flag in the vocabulary MUST be carried by at least one unit in the shipped roster. A CI check MUST fail if any flag is unused.

Rationale: a reserved flag that nothing uses is an untested branch with a promise attached. The design proposal carried one (clearMines, explicitly used by no unit) and it is cut here for exactly this reason. If a capability is worth a code path it is worth a shipped unit that exercises it; if no shipped unit wants it, the capability is speculative and belongs in the WASM plugin escape hatch, not in the closed vocabulary.

US-490 Three flags — reactiveOnly, garrisonOnly and expendable — MUST be classified as restrictive. A restrictive flag narrows what a unit may do and MUST be treated as a disadvantage by the non-domination test of §10, never as a capability.

Rationale: without this classification the domination test reads a restriction as a feature and concludes that a unit which may not attack, may not move, or dies after one use is superior to one that may do all three. The Militia and the Missile are both cheap enough that the test would otherwise flag them as dominating units they are plainly worse than.

3.1 Ground and capture

Flag Meaning
capture The unit may take an enemy or neutral city by entering its tile.
entrench The unit may take Stance Entrench and so accrue the entrenchment counter owned by 10-turn-model.md (TM-700), whose combat effect is owned by 11-combat.md (CB-1550).
garrisonOnly Restrictive. The unit may never leave the tile of the city that produced it.
reactiveOnly Restrictive. The unit may never initiate an attack on its own activation. It fights only when attacked and through interceptZone.

US-500 Only a unit with capture MAY change a city's ownership. Cargo MUST NOT capture while carried; it MUST be unloaded onto the city tile first, except under assaultLanding (US-560).

US-510 entrench MUST be carried only by units whose terrainClass is foot or wheeled. A unit without the flag MUST NOT be given Stance Entrench and MUST NOT accrue entrenchment (TM-700). Rationale: digging in is what dismounted troops and towed crews do. A tank that entrenches is a tank that has stopped being a tank, and giving tracked units the bonus erases the whole reason infantry is the answer to a dug-in line.

US-520 A unit with garrisonOnly MUST NOT be given a March, Screen, Survey or Attach Posture, and MUST NOT be loaded as cargo. It MUST remain eligible to defend, to entrench, and to count toward 12-economy.md's garrison requirement.

Ruled 2026-09-16 (Claude Code): a garrisonOnly starting unit begins in its player's start city, under a Garrison Posture on it. 01-game-rules.md GR-1570 gives each player "the starting units 02-units-and-industry.md specifies", and this document fixes no placement, so the generator's placement was a reading. That reading put every unit on the land nearest the start city and never on a city tile. The Militia it placed there could never enter the city, because a garrisonOnly unit may never leave its city's tile (US-490's rationale, as orders.ts enforces it). Every seat therefore began with a unit that held a tile of open ground all game and never counted toward any garrison, while its capital waited for other units to walk in. Measured the same day: on Small/2 and Standard/4, every starting Militia stood on its starting tile 30 AI turns later. A city holds six on the Surface (GR-790), so a force of up to eighteen per seat puts every Militia inside; any further garrisonOnly starter takes the land walk.

US-530 A unit with reactiveOnly MUST still activate in the Cascade, MUST be able to move, MUST resolve interceptZone engagements as Reactions (CB-1770), and MUST contribute its flak tier normally when it is the defender. It MUST NOT initiate an Assault or a Bombardment and MUST NOT be selected as a supporter.

Defect and fix, 2026-09-13. Neither prohibition was enforced, and reactiveOnly was a flag in exactly two files — the schema and the closed vocabulary list — read by no rule. engages did not cover the gap in either direction: the Air Defence Battery's excludes land so an assault on infantry was already refused, but it includes air, so a dedicated anti-air emplacement could initiate an Assault against an aircraft. The bar now sits above the Assault/Bombardment split in resolveClash, which is the requirement's own shape, and a second clause excludes the unit from supporter selection.

The class that made this live rather than latent is the Militia, which carries reactiveOnly with engages {land} — a starting-force unit, in almost every game, that had been assaulting the ground troops beside it. Landing the bar moved one corpus game (hazard-4-clock, 38 of its 40 hashes) with zero non-hash differences across the baseline: the same turn count, the same outcome, the same unit and event counts. It is US-1420's design being enforced rather than described — a Militia that storms out of its city is not cheap occupation currency.

The permissions were the part at risk from an over-broad fix, and they are intact: the bar is inside resolveClash, and resolveReactionClash is a separate function that does not call it, so interceptZone Reactions (CB-1770) still resolve. A flag whose whole purpose is reactive fighting would be worse than useless if the fix had silenced its reactions, so there is a row asserting the separation structurally.

Updated 2026-09-13: the transport model has landed, and with it US-500's cargo clause (the carried unit's refused capture at entryTriggersCapture), US-550 (the airdrop's landing legality — the two city kinds a drop could capture are excluded, so capture stays a movement outcome) and US-580 (the launch — the carrier's Unload, from the carrier's current tile, the deck as the anchor). The one still blocked is US-560: the assault landing's "attacker penalty owned by 11-combat.md" does not exist in 11-combat.md yet, so the cargo that attacks from aboard has no number to fight with.

3.2 Transport and delivery

Flag Meaning
cargoLost Destruction of this unit destroys all cargo aboard it.
assaultLanding Cargo may attack directly from aboard this unit, at the attacker penalty owned by 11-combat.md.
airdrop Cargo may be unloaded onto an unoccupied land tile that contains no enemy unit and no enemy or neutral city.
airbase Embarked air units' enduranceRemaining is reset to full each turn they end aboard.

US-540 Every unit with capacity > 0 MUST carry cargoLost. Rationale: cargo that survives its sinking transport removes every consequence from losing the escort battle, and the whole point of a convoy is that the convoy can be lost. There is no design in which a transport is worth risking if the cargo swims home.

US-550 A unit with airdrop MUST NOT permit a drop that captures a city. Capture MUST remain a movement outcome only.

Rationale: an air-dropped capture is a one-turn, zero-warning loss of a rear city with no approach a defender could have watched. Requiring the dropped unit to survive a turn on the ground before it can walk in gives the defender exactly one turn to react, which is the difference between a raid and a gotcha.

US-560 A unit with assaultLanding MUST permit its cargo to attack an adjacent land tile from aboard, without unloading, during the cargo's own activation. The carrier MUST have completed its own activation for that turn, which 10-turn-model.md TM-1140 guarantees by excluding embarked units from the activation roster entirely (TM-1830) — so cargo cannot act before the hull carrying it under any initiative values.

Corrected 2026-09-11: this clause previously credited US-290's initiative assignment, and the shipped numbers do not support it. The Landing Ship's initiativeBase of 56 sits below the Scout's 90, the Field Gun's 74 and the Heavy Artillery's 72, all three of which it can carry, and within the Tier 1 swing of the Armour, Heavy Armour and Infantry. Under TM-945 the ordering fails for six of the ten land classes it admits. The roster exclusion carries it instead, and carries it absolutely rather than by arithmetic. See the ruling under US-2150.

Ruled and built 2026-09-15, because as written this requirement could not be satisfied and was not. It asks for the attack to happen "during the cargo's own activation" and in the same breath says 10-turn-model.md TM-1140 excludes embarked units from the activation roster entirely. Both cannot hold literally: a unit with no roster slot has no activation of its own to attack in, and the capability was therefore unreachable — the flag sat on the Landing Ship in the shipped roster and in the closed capability vocabulary, 01-game-rules.md's capture path named it "a later increment", and no rule read it.

TM-1830's second sentence is the resolution: an embarked unit "MUST be acted upon as part of its transport's activation". So "the cargo's own activation" means the cargo's turn to act within its transport's, not a roster slot of its own, and "the carrier MUST have completed its own activation" means the hull has finished its route and its own attacks before the cargo strikes — the ship closes on the beach, fights what it must, and only then do the marines go over the side. TM-1840's rationale reads the same way from the other side, describing a landing as happening "during the transport's activation". Nothing in this document or in 10-turn-model.md needed amending; the contradiction was in this requirement's own account of why the ordering holds.

Three consequences are fixed with it, each held at core/test/turn/transport.test.ts. The landing is authorised by the cargo's own Attack Task naming the tile, so the choice stays with the player and nothing searches the true world for a victim the player could not see. The cargo does not advance and so cannot capture: "without unloading" is enforced at the Clash's advance, which is also the only thing that settles a capture, so US-500's bar on cargo taking a city needs no second rule. And the cargo is spent afterwards, for the reason TM-1840 gives about an unloaded unit — "without this, an amphibious force gets a free move on the beach" — which also means 12-economy.md EC-1730 clause 2 refuses its strength recovery that phase, so a passenger that attacks pays the rotation price like anybody else.

The reach is measured from the hull: adjacency, the ambush observation of CB-885 and the retreat destinations the attacker closes off are all taken at the carrier's tile. The hull consequently counts as a flanker of the beach under CB-980, which is that requirement read literally and is the right answer — a ship standing off the shore is a presence the defender must account for. The capability is checked inside buildClashSpec, which CB-350 makes the one place a Specification is constructed, so no caller can route around it and an ineligible order is refused as unachievable (CB-340) rather than resolving into a Clash thrown from nowhere.

US-570 A unit with airbase MUST reset the enduranceRemaining of every air unit that ends its activation aboard, and MUST apply repairEmbarked to them per §14.

US-580 An air unit MAY launch from a carrier that has not yet activated this turn, using the carrier's current tile. Rationale: without this, a carrier's air wing is hostage to the carrier's initiative and the whole hull becomes unusable in the turn it repositions — a 510-Works capability purchase that only works every other turn is not a capability, it is a bug with a price tag.

3.3 Fire support

Flag Meaning
siege Bombardment against a defender in a city or works is strengthened, by the attackerEdgeMods term of US-615.
ambush An Assault this unit initiates while unobserved returns no losses to it (US-630).

US-590 A unit with non-zero bombardShots MAY, in place of all other action on its activation, resolve up to bombardShots Bombardment Clashes against targets within bombardRange, under 11-combat.md §10. Each Bombardment is one whole Clash, not a round, and returns no losses to the firing unit (CB-1700).

US-600 Bombardment MUST target a tile whose defender's domain is land. A class MUST NOT bombard a sea or air target, regardless of engages.

Rationale: indirect fire against a manoeuvring ship or an aeroplane is a different weapon system with different physics, and modelling it here would make every gun a universal answer. Naval gunfire support is the same mechanism pointed at the shore, which is why the warships carry bombardRange rather than a separate flag. Note that a warship's engages is {sea} and its bombardRange is non-zero: the two fields are independent, and the pair is exactly how a hull that fights other hulls also shells a beach.

US-610 Bombardment's effects MUST be exactly those 11-combat.md defines: it MUST NOT reduce a target below strength = 1 (CB-1710), MUST perform no break check (CB-1020), and MUST apply 3 × Ld + 5 disorder to the target (CB-1720). This document MUST NOT define a Suppressed or Pinned condition; CB-140 forbids one, and disorder is the sole representation of a shaken unit.

Rationale, and this is where a whole mechanic changed hands rather than changed name. The old rule had bombardment ignore the entrenchment modifier and apply a Suppressed condition — two special cases doing one job, which was to make a hopeless frontal assault viable. 11-combat.md does that job with one number and no special case: bombardment removes very little strength and a great deal of disorder, disorder subtracts point-for-point from resolve (CB-710), and resolve is what decides whether the tile changes hands. The §12.3 worked example is re-derived on that arithmetic and reaches a sharper version of the same lesson. Un-digging a defender is no longer a modifier this document asks combat to suspend; it is the observable consequence of shelling one, which is both truer and one rule shorter.

US-615 The siege flag MUST be expressed as a +250 attackerEdgeMods term (CB-550, CB-1740) applying to a Bombardment whose defender occupies a city or a works of level ≥ 1, and MUST have no other effect. It MUST NOT alter 11-combat.md's procedure.

Rationale for +250 rather than the old "doubled": doubling was a multiplier on a per-round score in a model that no longer exists, and under additive per-mille modifiers (CB-540) a doubling would be +1000, which is larger than every other term in the document put together and would put the attacker in the clamp. +250 is the same size as the Advance Stance term — the largest single thing a player can buy — which is the right weight for the one flag whose entire purpose is reducing fortifications.

US-620 A unit that bombards MUST NOT move, attack, build or repair in the same activation, and MUST NOT support a Clash in the same turn (CB-260).

Defect and fix, 2026-09-13, on testing §3. US-600 was enforced nowhere. resolveClash tested engages inside its Assault branch; the Bombardment branch beside it carried a range gate, a shot cap, a moved gate and CB-260's supporter bar, and no target gate at all — so any class whose engages included sea or air could lay indirect fire on a ship or an aeroplane. The fix is a second gate rather than moving the engages test, because US-600's "regardless of engages" and its rationale require the two fields to stay independent: gating Bombardment on engages would stop a Cruiser shelling a beach, which is the one case the pair exists to allow. Nothing in the suite failed when the gate went in and no hash moved, which says no test had been relying on it either.

Note on US-620's scope in this build, recorded rather than papered over: of its five prohibitions, move, attack and support have a mechanism and build and repair do not. The order vocabulary has no Build or Construct action, and repair is a Reckoning step applied to units rather than an activation component, so there is nothing for those two clauses to forbid yet. The "MUST NOT attack" clause is satisfied indirectly — CB-1690 forfeits all remaining movement and CB-240 requires a point to assault — rather than by a clause of its own, so a change to either would quietly lift it.

US-630 ambush MUST set intensityBack to 0 for an Assault the carrying unit initiates while the defender holds no observation of it, so that the defender returns no losses in that exchange. It MUST NOT apply to a Bombardment or a Reaction, MUST NOT apply when the unit is the defender, and MUST NOT apply if the defender observed the unit at any point during the current turn.

Rationale: "the defender cannot score" was a statement about a first round; in a single-exchange model the same statement is intensityBack = 0, which 11-combat.md already defines and already uses for Bombardment (CB-200). Expressing the flag with a constant the procedure already carries means it needs no new modifier, no new step and no change to the monotonicity proof of CB-2150 — and it makes the Submarine's claim literal: attacking from concealment costs it nothing, and being found costs it everything.

3.4 Detection and concealment

Flag Meaning
submerged Invisible to all enemies except sonar units within their vision and units on an adjacent tile.
sonar Detects submerged units within vision.
highAltitude May be engaged only by units with intercept, or by units with flak ≥ 2.
intercept May engage air units passing through the unit's visionAir during their movement.

US-640 A submerged unit MUST be excluded from the knowledge view of every enemy player except where detected per §3.4. Detection MUST be recomputed at the start of each activation, not cached across the Cascade.

US-650 A submerged unit that initiates an attack MUST lose concealment for the remainder of the turn.

Built 2026-09-15, and until then the risk was missing from the whole class. submerged was written onto a unit at creation and never again, and neither concealment gate read anything else — so a Submarine could torpedo a convoy from beyond sonar range and remain unseen, which is the one cost the design attaches to using it. Found by reading the traceability index's not-core list against the requirements themselves; nothing in the core cited this id.

The condition needed no new state: initiatedCombatThisCascade is set on every initiation and cleared at the Cascade's head, so it already means "initiated an attack this turn" exactly. The boat does not surface — 01-game-rules.md GR-1080/GR-1090's layer rule is untouched, so its stacking and what may engage it are unchanged, and it is hidden no longer and nothing else. That distinction is the whole of the implementation.

The fix also closed a duplication that would have let it half-land: the concealment gate was written out twice, once in world/vision.ts's unitVisibleTo and once in world/contacts.ts, each with its own copy of the sonar-and-adjacency logic. A rule in two places is how the copies stop agreeing, and applying US-650 to one of them would have produced a submarine that the Contact Register could see and the fog could not. Both now call one exported concealedFromEnemies. Held at core/test/world/contacts.test.ts, with the row checked against a disabled clause to be sure it fails when the rule is gone.

US-660 highAltitude MUST exclude every unit with flak < 2 and without intercept from engaging the unit at all — not merely reduce their odds. Exclusion MUST be enforced at order time as an engages failure is (US-240), not as a combat modifier.

Rationale: this is the sharpest tech gap the design permits, and it is deliberate. An Industry-2 army facing a Heavy Bomber has exactly one answer, and that is the pressure that makes refusing to climb the ladder cost something. It is a hard edge and it will feel bad the first time; the mitigations are that the encyclopedia shows it from turn one (§7), that the counter (Fighter) is available two tiers earlier and cheaper, and that a Destroyer screen already provides flak 2. The threshold moved from 6 to 2 only because flak is now a four-step tier rather than a strength (US-260); the set of classes that clears it — Destroyer, Air Defence Battery, Cruiser, Carrier — is unchanged.

3.5 Special

Flag Meaning
expendable The unit is destroyed after resolving one attack, whatever the outcome.
administrator The unit drives Integration in the city whose tile it occupies. 12-economy.md owns the rate.
emplace The unit converts, over one full turn, into an immobile installation on its tile.
supplySource Once emplaced, the unit projects supply Reach. 12-economy.md owns propagation and range.

US-670 An expendable unit MUST be removed at the end of the Clash it initiates, before any repair or end-of-turn processing, regardless of its strength.

Defects and fixes, 2026-09-13, on testing §3.4 and §3.5. Both highAltitude and expendable were flags in the shipped roster with no behaviour anywhere. highAltitude appeared in exactly two files — the roster data and the closed flag list — and no rule read it; intercept, which US-660 names as the other half of its test, appeared only in the schema, the roster, the validator and that list; expendable was read by the perceptible-event module and by nothing in the combat path. So the Heavy Bomber carried a capability the game did not know about, the Missile was not one-use, and a test asserted that the Heavy Bomber carries its flag. US-660's exclusion now sits on the line after the engages gate, which is where the requirement itself puts it ("at order time as an engages failure is"); US-670's removal is inside the Clash, before the advance, because "at the end of the Clash it initiates" and "before any repair or end-of-turn processing" name one instant and that is the only place which is both.

Also on US-630: four of its five clauses held. The fifth — "MUST NOT apply if the defender observed the unit at any point during the current turn" — did not, because the implementation tested instantaneous observation, so a unit seen earlier in the turn that had since slipped out of vision still received the ambush. ClashView gained seenThisTurn, answered from the Contact Register's lastSeenTurn (GR-1300): the fact was already recorded and nothing was asking for it.

Rationale for keeping the flag when the arithmetic would nearly do it alone: the shipped Missile carries tough 20, 11-combat.md's floor, at which an even exchange removes 150 strength against a maximum of 100 (CB-150), so it is destroyed by its own attack under the ordinary rules in almost every case. Almost is not enough for a unit the player is told is one-use, and a rule the player can read beats an arithmetic accident they cannot.

US-680 A unit with emplace MUST become immobile permanently once emplaced, MUST NOT be loaded as cargo thereafter, and MUST remain destroyable by normal combat.

US-690 administrator and supplySource MUST be inert in this document. Their entire behavioural definition belongs to 12-economy.md; this document owns only the flag, the stat line of the unit that carries it, and its place in the production queue.


4. Unit set validation

US-700 A unit set MUST be a JSON document containing a set identifier, a schema version, and an array of unit definitions. It MUST NOT contain executable code, expressions, or references to external files.

US-710 The core MUST validate a unit set at load and MUST reject the whole set — never partially load it — if any check in US-720 through US-790 fails.

US-720 Schema. Every field of §2 MUST be present, of the declared type, and within the declared range. Unknown fields MUST be rejected, not ignored.

Rationale: silently ignoring an unknown field is how a modder spends an afternoon wondering why stealth: true does nothing. Rejecting it with the field name and the line number is one error message and saves the afternoon.

US-730 Referential integrity. Every id MUST be unique within the set; every trait named in requiresTraits MUST exist in the trait vocabulary owned by 12-economy.md; every value in buildables MUST be in the construction vocabulary owned by 01-game-rules.md.

US-740 Consistency. Every cross-field constraint stated in §2 and §3 MUST hold: US-130, US-140, US-150, US-310, US-390, US-510, US-540.

US-750 Reachability. For every unit in the set there MUST exist at least one combination of city class, trait set, Industry level and Integration reachable in a standard game that satisfies its buildRequirements. A unit that can never be built MUST be a load error.

Rationale: an unbuildable unit is not a balance problem, it is a typo, and it will be discovered in turn 80 of someone's game rather than at load.

US-760 Flag and class coverage. The shipped set MUST exercise every flag (US-480) and every class (US-110). A custom set MUST NOT be required to.

US-770 Non-domination. The shipped set MUST contain no strictly dominated unit as defined in §10. A custom set failing this check MUST load with a warning naming the dominated unit and its dominator, not an error.

Rationale: we hold ourselves to a standard we do not impose on modders. Someone deliberately building a joke set, a historical set with obsolete equipment, or a teaching set with a strictly-worse trainer unit has a legitimate reason, and refusing to load it would be paternalistic. Warning is the right strength.

US-780 Build-time band. Every unit in the shipped set whose class is not administration, garrison or logistics MUST complete in 3 to 11 turns inclusive at the reference producer of US-1290. A custom set MUST NOT be required to.

US-790 Power ceiling. Every unit in the shipped set MUST satisfy the tier power ceiling of US-1310. A custom set MUST NOT be required to.

US-800 The validator MUST be a pure function of the set document, MUST produce identical diagnostics on every platform, and MUST report every failure in one pass rather than stopping at the first.


5. The Industry ladder

5.1 What Industry is

US-810 Every city MUST carry an integer Industry rating in [1, 4]. Industry MUST determine exactly two things and nothing else: the multiplier applied to the city's Works output (US-840), and which unit definitions the city may build (US-360).

US-820 The four Industry levels MUST be named and MUST have the following properties.

Industry Name Works multiplier Upgrade cost to reach (Works) Manpower What it unlocks
1 Depot ×1000‰ — (every city starts here) — 5 manoeuvre units + 3 auxiliaries
2 Workshop ×1750‰ 90 0 +3 units: armour, fleet escorts, the sky
3 Foundry ×2750‰ 180 0 +6 units: concealment, strategic air, engineering, amphibious assault, air defence
4 Arsenal ×4000‰ 360 0 +6 units: fleet-scale power and strategic reach

US-825 The multipliers of US-820 — 1000 / 1750 / 2750 / 4000 per-mille — are the governing values. No other document MAY state an Industry multiplier table, and any worked example computed from a different value MUST be recomputed against these (US-2130).

Rationale: 12-economy.md carried a second table at 1000/1500/2100/2800 together with a worked example computed from the stale 1500, and that document already defers to this one for the values. Two normative tables for one multiplier is the defect this reconciliation exists to remove, and the arithmetic matters: at the reference City the two tables differ by 15 Works per turn at Industry 4, which is more than a Town's entire output. These values are not a taste judgement — US-1740 derives them as the solution to the constraint that build times stay inside 3 to 11 turns at every tier — so they are the ones that must survive.

US-830 Industry MUST be a per-city property. There MUST NOT be an empire-wide technology level, research points, research, or a technology tree of any kind.

Rationale: per-city Industry gives the map an industrial geography, and that geography is worth more than it costs. Frontier towns become cheap infantry mills; the one Arsenal on the safe coast is where carriers come from. There is now a reason to raid two hundred tiles behind the line and a reason to garrison a city nowhere near the front. It is legible in vocabulary players already have — "that city has a shipyard, this one doesn't" needs no tutorial — and it stops a single lucky capture from unlocking the whole game. The cost is a doubled management surface, and §7 and 13-command.md's Templates are the answer to that.

US-840 A city's effective Works per turn MUST be computed as floor(W × m / 1000), where W is the Integration-scaled effective Works owned by 12-economy.md and m is the Industry multiplier of US-820.

US-850 The Industry multiplier MUST apply to Works only. It MUST NOT affect Manpower levy, Population, Reach, Integration, Unrest, or garrison requirement.

Rationale: this asymmetry is doing more work than it looks like. A city's people do not multiply because you built a foundry, so climbing the ladder shifts a player from a manpower-bound economy toward a works-bound one — and the units that cost Works rather than people are precisely the mechanised, naval and air units the higher tiers unlock. The ladder therefore pays for itself in exactly the currency it generates. It also means the fast conqueror, whose captured cities yield Works immediately and Manpower never quite in time, is pushed toward Industry upgrades and mechanised forces, which is the same conclusion 12-economy.md reaches from the other direction. Two subsystems agreeing without coordination is the strongest evidence available that the shape is right.

US-860 Industry upgrades MUST cost zero Manpower.

Rationale: it is the escape valve. A player starved of people can always still invest, so a manpower crisis is a change of strategy rather than a dead end. A manpower cost here would close the only door left to a blitzer and turn the anti-snowball design into a death spiral.

5.2 Worked output and payback

US-870 The reference producer for all output statements in this document MUST be an untraited City-class city at Integration 100. Its effective Works per turn by Industry MUST be:

City class Base Works Ind 1 Ind 2 Ind 3 Ind 4
Town 6 6 10 16 24
City (reference) 12 12 21 33 48
Metropolis 20 20 35 55 80

US-880 The flat upgrade costs of US-820 MUST NOT be scaled by city class.

Rationale: flat costs mean a Metropolis pays back its Workshop in 6 turns, a City in 10, and a Town in 23. That is the industrial geography made of arithmetic rather than assertion: big cities become the industrial core because upgrading them is obviously correct, small ones stay cheap infantry mills because upgrading them obviously is not, and the player derives the map's economic shape themselves instead of being told it. Scaling the cost by class would flatten every city into the same decision and delete the geography.

US-890 Payback periods for the first upgrade, stated as a normative tuning fact:

City class Works/turn gained Upgrade cost Payback (turns)
Town +4 90 23
City +9 90 10
Metropolis +15 90 6

5.3 The player Industry cap

US-900 Every player MUST carry an integer Industry cap in [1, 4], set at game setup (§13) and constant thereafter unless a scenario changes it.

US-910 The Industry cap MUST gate two things: the maximum Industry to which that player may upgrade a city, and the buildable set per US-360 clause 1.

US-920 The Industry cap MUST NOT alter the Industry value of a city the player holds. A player with cap 2 who captures an Industry-4 city MUST retain that city at Industry 4, MUST receive its full Works multiplier, and MUST NOT be able to build units above minIndustry 2 in it.

Rationale: the cap exists for mixed-skill play, where it is a handicap on roster complexity — how much machinery a player is asked to run. It is not a handicap on production. Demolishing a captured factory to enforce a complexity setting would be an ugly, unexplainable event on the map, and would make the handicap punish conquest rather than limit depth. Giving the capped player the Works but not the toys is exactly the intended shape: a veteran capped at 2 is playing a genuinely harder game and is not being taxed for winning fights.

5.4 Progressive disclosure

US-930 The unit encyclopedia MUST be complete from turn one. Every unit's stat line, flags, roleText sentence, and counter relationships MUST be inspectable by every player at every point in the game, regardless of Industry, of what they have built, and of what they have seen.

Rationale: there is fog over the map, not over the rules. A player who is out-teched should be beaten by tempo and by having made worse decisions, never by not knowing what the thing that killed them was. Hiding stats converts a strategy game into a memory test that only rewards previous play, which is the opposite of the escalation this ladder is for.

US-940 Before spending any Works, a player MUST be able to inspect the full unlock set of any Industry level in any city they own, together with that upgrade's cost and its Works gain for that specific city.

US-950 On the turn a city's Industry upgrade completes, the client MUST present one unlock card per newly available unit, each containing: the unit's name, its roleText sentence, its full stat line, the two units it counters, and the two units that counter it (§9).

US-960 The first time a player observes an enemy unit of a class they have never produced, the client MUST present that unit's card unprompted — a contact briefing.

Rationale: the real hole in an in-game complexity ladder is that you can meet a unit class before you can build one. The briefing closes it. It costs one card, it fires at most twenty times per game, and it arrives at the single moment the player most wants to read it — which is also the moment the information sticks.

US-970 A city's Industry MUST be visible to any player with current vision on that city, and its last observed value MUST be retained in that player's knowledge view with its observation age.

Rationale: scouting industry becomes a genuine objective. An enemy Arsenal is worth a two-hundred-tile raid, and a player cannot plan that raid against a number they cannot see.


6. Capture, degradation, and works strikes

This section owns the single most important balancing rule in the unit and production layer.

6.1 Degradation on capture

US-980 When a city changes ownership, its Industry MUST be reduced by 1, to a minimum of 1 — unless the receiving player's recorded Industry memory for that city (US-1000) is greater than or equal to the city's Industry immediately before the transfer, in which case Industry MUST be unchanged.

US-990 Degradation MUST apply on every ownership change, including capture from a neutral holder and including a city changing hands twice in the same turn. Only one degradation MUST be applied per ownership change.

US-1000 Every city MUST record, per player, an Industry memory: the highest Industry that player ever held that city at. It MUST be updated at the end of every round in which the player holds the city, and MUST never decrease.

Rationale for the whole rule: without degradation, conquest of a developed empire is a jackpot and the game snowballs on capture rather than on production. Taking an Arsenal gets you a Foundry and a bill for 360 Works, which turns conquering an industrial core from a windfall into an investment — you still have to rebuild it — and makes defending yours proportionally more valuable than defending a frontier town.

Rationale for the memory exemption: the drop models the workforce and the management leaving, not the buildings burning. That is why it does not apply to a city's own government walking back in, and it is why an Engineer conducting the capture would not help either. It also means this rule and 12-economy.md's Loyalty memory share one mental model — your own ground comes back whole; someone else's does not — so a player learns one idea and gets two systems. And it stops the failure the naive rule produces: a border city changing hands eight times ratcheting to Industry 1 and becoming worthless to everybody, which is a spiral where both players lose and neither chose it.

US-1010 On any ownership change the city's Yard (§7.2) MUST be set to zero and its production queue MUST be emptied, unconditionally and including for the original owner retaking it.

Rationale: the Yard is materials on the shop floor and the queue is somebody else's plan. Neither survives a change of government. Making the Yard survive for the original owner would be a third special case for a small gain and would let a defender bank production inside a city they expect to lose and retake.

US-1020 Units already produced MUST be unaffected by degradation. A Cruiser built in an Arsenal MUST NOT be weakened, removed, or made unrepairable because the city that built it dropped to Foundry.

US-1030 After a transfer the city MUST adopt the receiving player's standing production for its class and traits, per 13-command.md's Consignment rules, and MUST NOT require player input to become productive.

6.2 Queue behaviour when Industry drops

US-1040 When a city's Industry falls below the minIndustry of an order already in its queue, that order MUST become Stalled. It MUST retain its position and its stored price, MUST NOT be cancelled, and MUST NOT consume Works.

US-1050 A Stalled order MUST be skipped for funding purposes; the queue MUST fund the first order that is not Stalled (§7.3). The Yard MUST NOT be lost.

Rationale: this is the design proposal's open question 8 and the answer is paused. Losing a half-built Cruiser to the strike that dropped the Industry, and then losing the accumulated Works as well, is a compounding punishment for one event — and compounding punishments are how a player learns that a mechanic is unfair rather than that it is dangerous.

US-1060 A city containing a Stalled order MUST raise a ProductionStalled condition for the Dispatch of 13-command.md, every turn, until the order is buildable or removed.

6.3 Rebuilding after degradation

US-1070 An Industry level lost to a works strike (§6.4) MUST be restorable at half the upgrade cost of US-820, rounded down, for 30 turns from the strike. After 30 turns the full cost applies.

Rationale: a bombed factory is damaged, not erased. Without this, 396 Works of Heavy Bomber permanently destroys 360 Works of upgrade every time it succeeds, and strategic bombing becomes a one-way ratchet that reduces every contested map to Industry 1. With it, bombing is a tempo and denial weapon — it costs the target time and Works and forces a decision — which is exactly the "attack their curve rather than their units" role the design wants, without the erasure.

US-1080 An Industry level lost to capture MUST NOT be eligible for the half-cost restoration of US-1070. The full upgrade cost applies.

Rationale: the two cases are different events and should price differently. Bombing damages plant you still hold; capture means the people who ran it are gone. Making conquest the expensive one is the anti-snowball rule doing its job.

Built 2026-09-16, the day the gap went live. Neither requirement had existed: industryUpgradeCost priced US-820's full cost and nothing else, and no city recorded how or when a level was lost. That cost nothing until 13-command.md's US-1090.1 made the works strike a player's order the same day — after which every successful strike permanently cost the target a full upgrade, the one-way ratchet this requirement's rationale warns of.

The record is per level, industryStrikeLosses: the turn a strike took each of levels 2, 3 and 4, with no slot for level 1, which US-1130 makes unlosable. A single stamp could not serve, because US-1120 lets a strike take a level every five turns while this requirement keeps each restorable for thirty — a city struck at turns 0 and 5 has two discounts running, one expiring five turns before the other. The existing worksStrikeIndustryTurn is kept for US-1120's window rather than folded in, since the two facts diverge on rebuilding: the window counts from the last level taken whether or not it has been rebuilt, and this record forgets a level the moment it is.

The discount is decided at enqueue, because US-200 freezes a price there: an order placed on the twenty-ninth turn keeps its half price however long it waits. "For 30 turns from the strike" counts the strike's own turn as the first, the same turn − stamp arithmetic as US-1120. The Ledger clears a level's entry when that level is rebuilt, so the entitlement is spent once; and US-1080 holds by construction — capture clears every entry, so the new owner, who never held the struck levels and whose capture loss is not a strike loss, rebuilds nothing at a discount. Held at core/test/economy/strike-restoration.test.ts, each half checked against a mutation that removes it. The record is hashed city state, so the corpus and the P0 containers were re-emitted.

6.4 Works strikes

US-1090 A unit with cityStrike ≥ 1 MAY, in place of attacking a unit, declare a works strike against an enemy city it could otherwise attack.

Built 2026-09-13, and the state it was found in is worth recording. The whole of §6.4 was unimplemented. cityStrike appeared in three files — the schema, the roster and the validator — and there was no worksStrike anything anywhere in the core, so a formula, an effects table, a rate floor, four prohibitions and a fog exception had nothing between them. US-1540 states that the Bomber is "the only tier-3 unit that can knock a city's works offline", and it could not.

The procedure lives in turn/works-strike.ts, which is US-1095 being taken at its word: it borrows 11-combat.md's draw and the grade and supply tables and nothing else, and it is reached through a field on the activation decision rather than a third attackKind, because making it a kind of attack would hand it to the Clash resolver and that is the coupling the requirement exists to forbid. The three figures the US-1100 rationale works through — share 666 undefended, 476 against a Destroyer and a battery, 425 with a fort — are test vectors and all three reproduce.

US-1090.1 13-command.md MUST surface the works strike as an order. Added 2026-09-13, and it is a contract gap rather than a ruling: US-1090 says a unit "MAY ... declare" a works strike, and nothing in §16 obliges the command layer to offer a way to declare one. The resolution procedure and the Cascade wiring are complete and reachable through the activation-decision seam; no player order produces one. That is the same state Bombardment is in — cascade.ts has said for weeks that "the production Bombard order arrives with the command increment; until then the policies issue assaults only" — so the mechanic is not in a worse position than its neighbour, but the obligation was missing from this document and is now stated. The order needs a target tile and nothing else; the reach test is the one an attack would use.

US-1095 A works strike MUST NOT be a Clash and MUST NOT use 11-combat.md's resolution procedure. It MUST NOT change any unit's strength, disorder or xp, on either side.

Rationale, and this settles an obligation that could not be met. The previous rule asked 11-combat.md for a per-round procedure and a flak exchange, and that document has neither: a Clash is a single exchange (CB-020) and CB-010 reserves the Clash for reducing a unit's strength, which a works strike does not do. A works strike is an attack on a production ledger, and the ledger is this document's. Rather than bend combat into a shape it forbids, the strike is resolved here, in four lines of integer arithmetic that borrow 11-combat.md's draw and nothing else. The cost is that this document owns one resolution procedure. The benefit is that it owns a procedure that does not pretend to be combat, so a change to the Clash cannot silently change what a bomber does to a factory.

US-1100 A works strike MUST resolve as exactly the following, with one draw and no rounds:

strikePower  = floor( atk × strength × (1000 + gradePower + supplyPower) / 100 000 )
worksDefence = 40 + 8 × (sum of the flak tiers of units in the city)
                  + 20 if the city tile carries a fort
share        = floor( strikePower × 1000 / (strikePower + worksDefence) )
r            = coreHash(seed, turn, strikerSequence, attackOrdinal) → [0, 1000)
worksHits    = (r ≥ 1000 − share)             ? 1 : 0
             + (r ≥ 1000 − floor(share / 2))  ? 1 : 0
             + (r ≥ 1000 − floor(share / 4))  ? 1 : 0

gradePower and supplyPower MUST be read from 11-combat.md's tables (CB-1610, CB-1650) so that a veteran crew and a supplied one strike as they fight. r MUST be drawn by the positionally addressed hash of CB-800 and attackOrdinal MUST advance as it does for a Clash (CB-830), so that a unit that strikes twice does not reuse a draw.

Rationale for the shape. One draw and a three-step ladder replaces strikeRun rounds because there are no rounds anywhere in this game any more, and because the ladder gives the same distribution shape — more hits are progressively harder — in a form whose exact probabilities are three subtractions rather than a binomial. It is monotone non-decreasing in r, matching CB-2150's convention, so the same preview machinery can show a player the odds of each outcome before they commit. Against an undefended city a Bomber (atk 80) reaches share 666, so it zeroes the Yard about two turns in three; a Destroyer and an Air Defence Battery in the city take that to 476, and a fort takes it to 425. Anti-aircraft units defend a factory by the same tier they defend themselves with, which is one number doing two jobs rather than a second anti-air rule.

US-1110 Works hits accumulated in a single strike MUST have the following effects, applied once at the end of the strike.

Works hits Effect
0 None.
1 Yard reduced to floor(Yard / 2).
2 Yard reduced to 0.
3 or more Yard reduced to 0, and if the striking unit's cityStrike ≥ 2, Industry reduced by 1 (minimum 1).

US-1120 A city MUST NOT lose more than one Industry level to works strikes in any five-turn window, regardless of how many strikes it receives.

Rationale: without a floor on the rate, a coordinated flight of four Heavy Bombers erases an Arsenal to a Depot in one turn, which is a three-tier loss the defender could neither see coming nor answer. One level per five turns makes strategic bombing a campaign that the defender can respond to — with fighters, with an Air Defence Battery, with a fort, or by moving production — rather than a decapitation.

US-1130 A works strike MUST NOT reduce Industry below 1, MUST NOT change city ownership, MUST NOT damage units in the city, and MUST NOT affect Integration, Unrest, or Population.

US-1140 A works strike against a city the striking player cannot currently see MUST be a legal order and MUST resolve normally against the true city state.

Rationale: strategic bombing at range is precisely the case where you are shooting at your last-known intelligence. Requiring current vision would make the Heavy Bomber's whole reason for existing — reach beyond the front — unusable, and it would require a scouting apparatus for every strike that neither the roster nor the command layer offers.


7. The production queue

7.1 Orders

US-1150 Every city MUST carry a production queue: an ordered list of orders bounded at 8, plus an optional repeating tail owned by 13-command.md's Templates and Requisitions. The bound MUST be enforced as a validation rule at enqueue time and MUST NOT be assumed as a storage shape. No rule in this document and no engine behaviour MAY depend on the particular value.

Rationale: two outcomes set this bound, and the number is derived from the second rather than chosen. First, the queue is a plan the player reads at a glance rather than scrolls through, so it must fit whole in one presentation — 04-ui-ux.md UX-930 owns that surface and US-2190 records the obligation. Second, the queue is per-city hashed state on a map that 01-game-rules.md GR-1530 allows up to 400 cities, so it must be bounded for the same reason GR-1230 bounds the Contact Register: unbounded hashed state is an unbounded save file and an unbounded desync surface.

Eight comes out of the production budget. At most one order completes per city per turn (US-1290) and every manoeuvre unit costs 3 to 11 turns at the reference producer (US-780, US-1740), so a full queue is 24 to 88 turns of committed production — longer than most games run from the point it is filled. Depth beyond that is not plan but wish, and depth beyond the head buys only pre-planning in any case: the Yard is capped at the head eligible order's price (US-1240) and only the first non-Stalled order is funded (US-1250, US-1260). A deeper queue would therefore cost save state and reading time for orders the city cannot reach. Because nothing reads the number, a scenario or a later revision that has a reason to move it moves one constant and one panel, not a rule.

US-1160 A production order MUST be one of exactly three kinds.

Kind Payload Owner of its effect
unit a unit definition id this document
industry none — upgrades the city by one Industry level this document
works a civic improvement key 12-economy.md

US-1170 Every order MUST store, at enqueue time: its kind, its payload, its worksPrice, its manpowerPrice, and the turn it was enqueued.

US-1180 A city MUST NOT hold more than one industry order in its queue at a time.

US-1190 An industry order MUST be ineligible, and therefore Stalled, whenever the city's Industry is already equal to min(4, owner's Industry cap).

US-1200 Removing an order from the queue MUST be free and MUST NOT alter the Yard.

Rationale: because progress accumulates in a single per-city Yard rather than against an individual order (§7.2), there is nothing to refund and no cancellation penalty to design. A player may reorder and re-plan freely, which is the correct amount of friction for a decision they will make hundreds of times.

US-1210 An order that has begun funding MUST NOT be cancelled, retargeted, or reordered by any automated system, only by explicit player action. This satisfies the obligation 13-command.md places on the production layer (CM-1270).

7.2 The Yard

US-1220 Every city MUST carry a single integer Yard, its accumulated unspent Works. The Yard MUST NOT be partitioned per order.

US-1230 At the top of each round, after 12-economy.md has computed the city's effective Works and this document has applied the Industry multiplier (US-840), that amount MUST be added to the Yard.

US-1240 The Yard MUST be capped at the worksPrice of the city's head eligible order. Where the queue is non-empty and contains no eligible order, the Yard MUST be capped at one turn of the city's effective Works. Works accrued above the cap MUST be discarded, and the city MUST raise a ProductionOverflow condition for the Dispatch on any turn discarding occurs. The empty queue is not this requirement's case: it is 12-economy.md EC-610's, at a cap of 2 × effectiveWorks with a ProductionIdle event.

Ruling, 2026-09-10. The empty queue belongs to EC-610, and clause 2 is the Stalled case. As written, this clause said "no eligible order", and an empty queue contains no eligible order, so it collided with EC-610 — two MUSTs over the same city, one capping at 1 × effective Works and raising ProductionOverflow, the other at 2 × and raising ProductionIdle. Parked on 2026-09-10 by the implementer, who kept the specific rule and pinned it; ruled here in the same direction, for reasons worth stating because the wording is what caused it.

First, specificity: EC-610's antecedent is the narrower set — an empty queue is one way to have no eligible order, and not the general way — and this repository already settles that pattern the same way twice (EC-400 against US-825, "the procedure governs and the table is the mirror"; EC-1130 against TM-1970, "the specific economy provision governs the revolt"). Second, domain: this clause caps the Yard at an order's price, which is a fact about orders; its fallback exists for the queue whose orders are all Stalled (US-1250, US-1260 fund only the first non-Stalled order), and that queue is non-empty by construction. 12-economy.md owns the Yard and the Works (EC-190, EC-200, EC-600, EC-610); it is the right document to say what a city with nothing to build accrues. Third, the rationales already disagree in EC-610's favour: EC-610 exists so "a player who edits a queue does not lose a turn's production for the privilege", and the 2 × buffer is exactly that concession — under a 1 × cap, emptying a queue to reorder it costs the accrual EC-610 was written to protect. This clause's own rationale argues about banking toward a build, which is not what an empty queue is doing.

The consequence for the events is that an empty queue raises ProductionIdle and not ProductionOverflow: this requirement does not reach it, so its condition is not owed. That is what shipped, and a row now pins the absence as well as the presence (economy/economy-ledger.test.ts). Nothing else changes — the built behaviour was already this reading, so no code moved and no hash moved.

Rationale: the cap is what stops a player banking forty turns of a quiet city's output and materialising a Cruiser the turn after they capture a port. Tying it to the head order's price means it never obstructs an honest long build — queue the Cruiser and you may bank the Cruiser — while making banking-for-later impossible, because there is nothing to bank toward. Raising a condition rather than silently discarding is the difference between a rule and a hidden tax; a city throwing away Works is a thing the player should be told once and then fix.

7.3 Funding and completion

US-1250 At the top of each round, after Yard accrual, each city MUST attempt to complete its head eligible order. An order is eligible when it is not Stalled and its payload satisfies US-360 for the city's current state.

US-1260 Where the head order is Stalled, the city MUST fund the first non-Stalled order in queue index order. The Stalled order MUST keep its queue position.

US-1270 An order MUST complete when Yard ≥ order.worksPrice and the owning player's Manpower pool is at least order.manpowerPrice. On completion, order.worksPrice MUST be deducted from the Yard, order.manpowerPrice MUST be deducted from Manpower, and the order MUST be removed from the queue.

US-1280 Where Yard ≥ order.worksPrice but Manpower is insufficient, the order MUST enter the Awaiting Levy state defined by 12-economy.md. The Works MUST be considered spent; the Yard MUST be reduced by worksPrice and the order MUST hold its place at the head of the queue until Manpower is paid.

US-1290 At most one order per city MUST complete per turn, regardless of how large the Yard is.

Rationale: this is a real rule with real consequences and it earns them. Without it a Metropolis Arsenal producing 80 Works a turn emits two Scouts every turn and becomes a swarm cannon, which erases the mass-versus-concentration tension the entire power curve of §12 is built to protect. With it, a large city produces large things — its efficient product is one that costs three to eleven turns of its own output — and small cities keep a genuine niche as the efficient producers of cheap units. There is no way to centralise, which is what keeps the map's industrial geography meaningful rather than collapsing to one super-city.

US-1300 The completion of an industry order MUST raise the city's Industry by exactly 1 and MUST take effect at the top of the following round, so that the Works multiplier for the completing round is the old one.

US-1310 A completed unit order MUST place the new unit on the city's tile, assign it the next global creation sequence, set strength to 100, disorder, xp, entrenchment and shaken to 0, and enduranceRemaining to full, and hand it to 13-command.md for its initial order stack (CM-1170, CM-1180). It MUST require zero player input.

US-1320 Where a completed unit cannot legally be placed — a naval unit in a city that is not coastal for its terrainClass, or a tile at its stacking limit — the unit MUST be held hosted in the city, occupying no tile, until a legal placement exists. A hosted unit MUST NOT act and MUST be destroyed if the city falls.

Built 2026-09-13. There was no hosted concept anywhere in the core, and the Commissioning step placed every completed unit on its city's tile unconditionally; with no invariant on occupancy against the limit, a seventh unit completing into a City that stacks six stood over the limit and nothing rejected or reported it. Only the stacking half of this requirement can arise: units/buildable.ts refuses a requiresCoastal class in a non-coastal city at enqueue (US-360 clause 2), so a naval unit never completes somewhere it cannot be put. Hosted is onMap: false and tile: null with hostedIn set, and "MUST NOT act" needs no rule of its own: the activation roster, the supply fill and every occupancy count already skip a unit that is not on the map — which also means a hosted unit takes no isolation attrition while it sits inside its own city. One settle at the head of Commissioning destroys hosted units whose city is no longer their owner's (a capture and a revolt alike, the same shape as GR-630's destroy-with-base), then places those that now fit, oldest first by creation sequence and before the phase's new completions, so a unit that has waited is not overtaken. The hosting check runs after the Consignment, so a held unit keeps the standing orders it was given and acts on them the moment it is placed.

US-1330 The reference producer of US-870 and the one-completion-per-turn rule of US-1290 together MUST yield a build time in [3, 11] turns for every manoeuvre unit at its unlock tier (US-780). §12.1 states the resulting table.


8. The roster

Twenty manoeuvre units and three auxiliaries. Every unit has a one-sentence roleText naming a thing no other unit does.

US-1340 The shipped unit set MUST be exactly the definitions of §8.1 through §8.5. Its setId MUST be standard.

US-1350 All costs in this section MUST be denominated in the Works scale defined by US-870, in which an untraited City-class city at Integration 100 and Industry 1 produces 12 Works per turn.

Rationale: costs are meaningless without a producer to measure them against. Stating the reference explicitly means that if 12-economy.md's base Works rates differ from the ones assumed here, every cost in this section scales by one integer factor and the build-time band of US-780 is the invariant that survives — rather than the roster and the economy quietly disagreeing about what a Works is.

8.1 Tier 1 — Depot (Industry 1). The complete simple game.

Unit W M Move Vis VisAir atk def tough role engages flak Slots Cap Init Class Terrain
Scout 36 15 5 4 4 18 22 60 Recon land 0 1 0 90 recon foot
Infantry 60 36 2 2 3 30 44 100 Foot land 1 1 0 32 infantry foot
Field Gun 78 18 2 2 3 48 18 70 Gun land 1 1 0 74 artillery wheeled
Transport 90 8 6 2 4 0 26 120 Naval — 1 0 6 54 transport_sea naval
Corvette 102 18 8 3 5 42 34 110 Naval sea 1 0 0 62 escort naval

Flags and parameters: Scout capture; Infantry capture, entrench; Field Gun entrench, bombardRange 2, bombardShots 1; Transport cargoLost, draft 1, carryDomains {land}, carryMaxSlots 3; Corvette draft 1, bombardRange 1, bombardShots 1.

Ruling, 2026-09-05, on two schema fields the tables of §8 never assign. US-120 requires every definition to carry zoc and mayOccupyCity, 01-game-rules.md GR-680 reads zoc to decide which units exert a zone of control, and 13-command.md CM-2170 reads mayOccupyCity; neither column appears in any tier's table, so the shipped set had no value for either. In the shipped set, zoc is true for the Infantry, the Field Gun and the Militia — the dismounted line and the emplaced gun, which are what GR-700's rationale describes a picket line being made of — and false for every other unit: the Scout is reconnaissance rather than a line unit, the auxiliaries are not combat units, and no shipped naval unit exerts one. mayOccupyCity is true for every shipped unit, a sea unit's occupancy being further gated by whether the city is a port (GR-890), which is the map's decision and not the class's. Tiers 2 to 4 take the same reading when authored: zoc true for ground combat classes that fight on foot or from an emplacement, false otherwise.

US-1355 retreatClass MUST be Detached for every class whose domain is air and Ground for every other class in the shipped set. A custom set MUST NOT be required to follow the rule, but MUST set the field explicitly.

Rationale: 11-combat.md's two retreat classes are "falls back one tile" and "goes home to a base" (CB-1330), and which of the two applies is a fact about whether the thing flies. Deriving it from domain rather than authoring it per unit removes twenty-three chances to get it wrong and makes the one interesting case — an aircraft broken over hostile ground, which is destroyed if no base is in endurance — a consequence of the roster rather than a per-unit decision.

US-1360 Scout — reveals the map and punishes an undefended flank. It can capture, which is the entire reason your rear cities need garrisons. atk 18 against def 44 means a single Infantry stops it dead: at the median draw, on Open terrain against a defender in Stance Hold with both units Green and Adequately supplied, the Scout loses 69 strength and the Infantry 18, so a Scout that meets a garrison is finished in one exchange. It is a punishment for carelessness rather than a strategy. Its class of recon is what makes its vision worth more than its radius, because 01-game-rules.md GR-1270 raises the fidelity of everything it looks at.

Corrected 2026-09-13. This requirement stated 66 and 19, and 11-combat.md computes 69 and 18. No stance or supply variation produces the stated pair — Hold gives 69/18 and Advance 65/20 — so the figures were derived against an earlier version of the combat tables and were never updated when those changed. The conditions are now stated, because without them the claim is not checkable at all: "the median draw" alone does not pin a pair. Locked at core/test/units/roster-claims.test.ts, so the next change to 11-combat.md breaks a test rather than quietly falsifying this sentence. The argument the numbers support is untouched — a Scout losing 69 of 100 is finished, and would have been at 66.

US-1370 Infantry — takes cities and holds them; nothing on land is more cost-efficient at defending one. def 44 > atk 30 is deliberate and is the spine of the land game: infantry is better at holding than at taking, so the attacker must always bring more than the defender. It is also the roster's reference unit — tough 100 is the value 11-combat.md's whole scale is read against (CB-150), and every other class's durability is a statement about this one.

US-1380 Field Gun — grinds down a dug-in defender so that infantry can finish it, and cannot take ground itself. It is placed at the bottom of the ladder on purpose, so that the most interesting mechanic in the game is available in the first fifteen turns rather than the first fifty.

US-1390 Transport — the only tier-1 way to move an army across water, and helpless while doing it. tough 120 so it does not evaporate under one attack; def 26 — the lowest of any hull — so it must never travel alone; atk 0 and an empty engages so it cannot defend itself by attacking; cargoLost so that losing the escort battle costs the army, not just the boat.

US-1400 Corvette — escorts convoys and contests coastal water; the cheapest thing that can sink a transport. draft 1 lets it go up rivers and into shallows where nothing bigger can follow, which is what keeps it relevant after the Destroyer arrives.

US-1410 The Field Gun MUST remain at Industry 1.

Rationale: this was an open question in the design proposal and it is decided here. Combined arms is the most interesting decision the combat model offers, and a player who does not meet it until Industry 3 has played fifty turns of a worse game. The cost is one extra concept on turn ten; the alternative — promoting the Scout out of tier 1 to compensate — trades the unit that teaches fog and exploration for the unit that teaches nothing until you own two of them. Five units on turn one is the right load, and this is the right five.

8.2 Tier 1 auxiliaries

These are units, so their stat lines and schema conformance are owned here. Their behaviour belongs to 12-economy.md, which owns garrison points, Integration rates, cost escalation basis, and supply propagation.

Unit W M Move Vis VisAir atk def tough role engages flak Slots Init Class Terrain
Militia 40 16 1 1 2 16 36 80 Foot land 1 0 12 garrison foot
Administrative Cadre † † 1 1 2 0 12 50 Foot — 0 1 10 administration foot
Forward Depot † † 1 2 3 0 22 90 Foot — 0 2 14 logistics foot

† Priced by 12-economy.md, not here (EC-280 assigns ownership; EC-1280 and EC-1620 are the values). Ruling, 2026-09-05. These two rows previously printed 450 / 60 and 120 / 12 against the economy document's 150 / 20 and 60 / 4 — the Cadre threefold apart on both axes. Ownership was never in doubt, so the duplicate is simply deleted rather than reconciled: a second copy does not merely risk drift, it conceals the disagreement, because each table is internally consistent with its own document and looks correct to anyone reading only one. Which figure is right is a separate, later question that EC-570's payback measurement exists to answer and that cannot be settled before the game is playable; every derived figure in 12-economy.md — the escalation closed form of EC-1290, the ranking of EC-1400, every worked vector in its §16 — is computed from that document's values, so adopting these would have invalidated all of them at once.

Flags and parameters: Militia entrench, garrisonOnly, reactiveOnly, slots 0; Administrative Cadre administrator, costEscalationPerOwnedPerMille 500, buildRequirements: minCityClass city, minIntegration 100; Forward Depot emplace, supplySource.

US-1420 Militia — pays for occupation in a cheap, dedicated currency so that occupation never strands good infantry. It cannot leave its city, cannot initiate an attack, and cannot capture; def 36 against tough 80 makes it a genuinely awkward thing to dig out for 40 Works, and it exists so a player is not clicking through fifty idle rifle companies every turn.

US-1430 Administrative Cadre — the visible convoy of bureaucrats that caps how fast an empire can digest what it conquers. Its escalating price is 12-economy.md's principal anti-snowball brake; this document owns only that the price escalates at 500‰ per Cadre already owned or in production, and that the price is fixed at enqueue (US-200).

Note, 2026-09-11, on where the 500 is read. 12-economy.md charges the escalation as cadreCost(owned, cadreSlope), whose increment per owned Cadre is base / slope; at the shipped cadreSlope of 2 that is +50%, exactly the 500‰ this requirement states. The two are the same rule in two notations, cadreSlope is tunable over [1, 4] (EC-2100) where the same rule means 1000‰ or 250‰, and the unit definition's costEscalationPerOwnedPerMille is read by no rule — the ledger reads the option. The equivalence is now held by a test rather than by coincidence (core/test/units/roster-prose.test.ts); which of the two should be the source is 12-economy.md's decision and is not made here.

US-1440 Forward Depot — pushes supply beyond your integrated territory, and is exactly as strong as its link home. slots 2 so it can be shipped to an island; emplace so pushing it forward costs a turn.

US-1450 The three auxiliary units MUST be exempt from the build-time band of US-780. Rationale: the Cadre's price is a deliberate brake owned by another subsystem and would take 38 turns at the reference producer; forcing it into the band would mean overruling the anti-snowball design from the unit layer, which is not this document's decision to make.

8.3 Tier 2 — Workshop (Industry 2). Land war gets fast; the sky opens.

Unit W M Move Vis VisAir atk def tough role engages flak Slots Init Class Terrain
Fighter 138 8 14 6 8 62 28 50 Air air 0 1 84 fighter air
Armour 156 20 4 3 4 62 38 130 Armor land 1 2 50 armour tracked
Destroyer 192 36 10 4 6 62 50 150 Naval sea 2 0 60 escort naval

Flags and parameters: Fighter intercept, endurance 2; Armour capture; Destroyer sonar, draft 2, bombardRange 2, bombardShots 1.

US-1460 Fighter — owns the sky over your own ground; short-legged, and near-useless against anything dug in. engages {air} is the whole design: a Fighter may not initiate against a ground or sea target at all, which is a sharper statement of the old feeble ground attack and a truthful one. Its atk 62 is the same as the Armour's, spent entirely on the one fight it is for. endurance 2 caps its operating radius at roughly fourteen tiles and makes airbase geography a real concern.

US-1470 Armour — breaks a line and exploits the gap, and is badly punished by rough ground. Two Infantry (120 Works, entrenchable, def 44 each) beat one Armour on the defence — a dug-in Infantry with one more beside it holds against a lone Armour at every draw, and throws it back 475 times in a thousand; one Armour breaks a single Infantry caught in the open 980 times in a thousand. Armor in Forest is −300 and in Mountain −500 (CB-1450), which is where the punishment lives. That trade is the map's question, not the roster's.

Conditions stated 2026-09-11, because the two figures were checked and are exact, and rested on conditions this requirement did not give. "Holds at every draw" and "throws it back 475 times in a thousand" hold for an attacking Armour in Stance Advance, against an Infantry in Stance Entrench at entrenchment 1 with one more in its stack, both sides Green and Adequately supplied, on Open ground; "breaks a single Infantry caught in the open 980 times in a thousand" is the same attacker against a lone Infantry at its defaults — Stance Hold, no entrenchment, nothing beside it. Two readings had to be ruled out. "Caught in the open" is plain language and not 11-combat.md's Caught term of CB-600: with that −200 actually applied the figure is 1000, not 980. And "dug-in" is the Stance, not the numeric field alone — at Stance Hold the 980 becomes 234, and 475 is what pins the level, since entrenchment 0 gives 385 and 4 gives 689. A consequence worth keeping: 475 is also the figure at entrenchment 2 with nothing beside it, so one more body in the stack is worth exactly one level of digging in, which is why the clause can pair them. Locked at core/test/units/roster-prose.test.ts; unlike US-1360's, these numbers never drifted.

US-1480 Destroyer — the fleet's general-purpose escort, and the only mid-tier hull that can shoot at both submarines and aircraft. sonar and flak 2 are both dormant at the moment it unlocks, which is precisely the point: it is a good ship today and the indispensable ship the turn your opponent reaches Industry 3.

8.4 Tier 3 — Foundry (Industry 3). Concealment, strategic air, engineering, assault.

Unit W M Move Vis VisAir atk def tough role engages flak Slots Cap Init Class Terrain
Air Defence Battery 126 24 2 4 8 74 40 90 Gun air 4 1 0 18 air_defence wheeled
Engineer 132 40 2 2 3 10 22 90 Foot land 1 1 0 26 engineer foot
Landing Ship 210 20 7 2 4 0 38 170 Naval — 1 0 4 56 transport_sea naval
Heavy Artillery 216 48 1 2 3 94 36 80 Gun land 1 3 0 72 artillery wheeled
Submarine 258 40 8 3 3 94 42 90 Naval sea 0 0 0 58 submarine naval
Bomber 264 16 12 4 6 80 36 60 Air land, sea 0 2 0 78 bomber air

Flags and parameters: Air Defence Battery entrench, reactiveOnly, interceptZone 1; Engineer capture, entrench, repairAdjacent 1, buildables {road, airfield, fort, bridge}; Landing Ship assaultLanding, cargoLost, draft 1, carryDomains {land}, carryMaxSlots 3; Heavy Artillery entrench, siege, bombardRange 3, bombardShots 2; Submarine submerged, ambush, draft 2; Bomber endurance 2, cityStrike 1.

US-1490 Air Defence Battery — makes a stretch of sky expensive and is useless against anything on the ground. reactiveOnly means it never attacks on its own activation and engages {air} means it could not touch a ground target if it did, so it can never be repurposed as a gun. Its work is done as a defender: flak 4 adds +24 to an air attacker's shock (US-260), which drives off a Bomber it has no hope of destroying.

US-1500 Engineer — turns terrain into infrastructure, and is the only way to make a large map traversable and a captured city defensible. On maps at the top of the supported size range it is not a specialist, it is the strategic-movement system. repairAdjacent 1 is the field answer to persistent damage, and it is deliberately smaller than 11-combat.md's own field recovery of +4 (CB-2070): the Engineer is a supplement to rotation, not a substitute for it.

Defect recorded 2026-09-11: nothing can build an installation. canBuildOn is implemented and covered (GR-420, GR-440), the construction vocabulary is 01-game-rules.md's, the Engineer is the only unit in the shipped set with a non-empty buildables — and no order in the command set builds one, so a Bridge can be destroyed (removeBridge) and never laid. buildables itself is read by no rule at all: canBuildOn decides legality from the builder's movement class and never consults the set, so the field is authored, validated for referential integrity (US-730), displayed by the encyclopedia, and never consulted. Until 13-command.md surfaces a build order and a rule reads buildables, this requirement's "only way to make a large map traversable and a captured city defensible" describes a unit that cannot build anything. The stat line, the flag and the vocabulary are correct; the order is missing. Same shape as US-1090.1.

US-1510 Landing Ship — puts an assault ashore against a defended beach instead of needing an empty one. It is what stops island maps from degenerating into "whoever owns the water wins by default."

Defect recorded 2026-09-11, shared with US-1560, and discharged 2026-09-15 — both flags are now read. The airdrop's legality is airdropLegal in turn/command-policy.ts under US-550, with GR-670's movement-class clause added on the later date (the domain check had been standing in for it); the beach assault is resolveAssaultLandings in turn/cascade.ts, with the ruling on US-560's self-contradiction recorded above and CB-550's amphibious term of −250 implemented with it. GR-670 is cited at both sites and tested. The original record follows, because the shape it describes is worth keeping: assaultLanding and airdrop were read by no rule. 01-game-rules.md GR-1700 tabulates what it reads from this document and names both as read by GR-670; outside the closed vocabulary that lists them, the shipped data that carries them and the encyclopedia that displays them, no rule reads either. GR-1700's own contract test asserts that the seven flags it asks for are members of the vocabulary, which is why this went unnoticed: the names are held and the behaviour is not. So the beach assault of this requirement and the drop of US-1560 are both unimplemented. What is held is the carriage arithmetic — the Landing Ship's carryMaxSlots 3 admits an Armour where the Air Transport's 1 does not — and that is what the test asserts (core/test/units/roster-prose.test.ts). GR-670 is 01-game-rules.md's to implement.

US-1520 Heavy Artillery — reduces fortified cities, and is nearly immobile and defenceless without a screen. siege adds +250 to a bombardment against a defender in a city or works (US-615); bombardShots 2 makes one Heavy Artillery the equal of two Field Guns in a single activation, which is the concentration CB-1750 says a siege requires; move 1, wheeled and slots 3 make moving a siege train an operation in itself.

US-1530 Submarine — kills shipping and heavy hulls from concealment, and is helpless once found. atk 94 is the highest sea attack until Industry 4 and is meant to be frightening, and ambush means a first strike from concealment costs it nothing at all (US-630). It cannot attack land or air and cannot capture; its answer to being hunted is not to fight, it is to be somewhere else.

US-1540 Bomber — projects force anywhere inside a wide radius, and is the only tier-3 unit that can knock a city's works offline. cityStrike 1 lets it destroy accumulated build progress but never Industry, which makes it a tempo weapon against production rather than a weapon against the ladder.

8.5 Tier 4 — Arsenal (Industry 4). Fleet-scale power and strategic reach.

Unit W M Move Vis VisAir atk def tough role engages flak Slots Cap Init Class Terrain
Missile 120 2 24 1 2 132 60 20 Air land, sea 0 1 0 86 missile air
Air Transport 252 12 16 4 6 0 16 60 Air — 0 2 2 68 transport_air air
Heavy Armour 372 40 4 3 4 106 64 190 Armor land 1 3 0 46 armour tracked
Heavy Bomber 396 24 14 5 7 106 48 80 Air land, sea 0 3 0 80 bomber air
Cruiser 480 80 9 5 7 106 84 240 Naval sea 3 0 0 44 capital_ship naval
Carrier 510 60 9 5 7 16 78 220 Naval sea 3 0 4 40 carrier naval

Flags and parameters: Missile expendable, endurance 1; Air Transport airdrop, cargoLost, endurance 2, carryDomains {land}, carryMaxSlots 1; Heavy Armour capture; Heavy Bomber highAltitude, endurance 3, cityStrike 2; Cruiser sonar, draft 3, bombardRange 3, bombardShots 2, requiresTraits {shipworks}; Carrier airbase, cargoLost, draft 3, repairEmbarked 2, carryDomains {air}, carryMaxSlots 2, requiresTraits {shipworks}.

US-1550 Missile — deletes one undefended high-value target twenty-four tiles away, at no risk to anyone you would miss. At 120 Works and 2 Manpower it is the purest conversion of industry into effect in the game, which makes it the weapon of the Works-rich, Manpower-poor conqueror. Against a defended fleet it is a coin flip that buys little; against an artillery park, an air defence battery, a transport, an engineer or a Cadre it is decisive.

Rationale: the design proposal justified the Missile as an upkeep-free way to spend a production lead. There is no upkeep in this game (US-210), so that rationale does not survive, and the unit needed a real one. The one it has is better and it falls out of the two-resource economy: a player whose Manpower is starved by twenty undigested cities has Works and nobody to crew anything, and this is the only unit that turns Works into effect with essentially no people. It also names a role nothing else fills — reach 24 with no manned asset exposed — and it is stoppable, by Fighters and by Air Defence Batteries, which is what keeps it honest.

US-1560 Air Transport — drops infantry behind the line; the only unit that can put troops on a rear city with no ground approach. carryMaxSlots 1 means it moves Infantry, Engineers, Field Guns and Scouts, never Armour.

Amended 2026-09-11: the list omitted two units. carryMaxSlots 1 admits every land class of slots 1, which is the four named plus the Administrative Cadre and the Air Defence Battery, both slots 1, and nothing in this section excludes either — so an Air Transport may fly a Cadre to a newly taken city, or a battery to cover it, and both are uses worth having rather than oversights to forbid. The admitted set is computed from the roster in the test rather than restated, which is what found the omission. The Militia is slots 0 and would fit arithmetically but is garrisonOnly and cannot leave its city. "Never Armour" is right and is now checked against both tracked classes, barred by the per-item cap rather than by the size of the hold. The airdrop behaviour is the defect recorded under US-1510.

US-1570 Heavy Armour — the hammer that cracks a dug-in line at one point, expensive enough that losing one is a campaign event. slots 3 means one Transport carries two of them and nothing else.

US-1580 Heavy Bomber — wrecks an enemy's industry from beyond the front, and cannot be touched by an army that did not climb. highAltitude is the sharpest expression of the tech gap this design permits (US-660). slots 3 exceeds a Carrier's carryMaxSlots 2, so it cannot operate from a flight deck.

US-1590 Cruiser — the strongest thing afloat gun for gun; slow, expensive, and a magnet for submarines and bombers. draft 3 bars it from shallows and rivers entirely, so it can be denied whole regions of a coastal map by geography alone.

US-1600 Carrier — moves your air force's home and projects air power beyond your borders. Almost no attack of its own; atk 16 and engages {sea} so it can see off a Corvette and nothing more; four air slots, resets endurance, restores 2 strength per turn to embarked aircraft. It is a capability purchase and it is priced as one.

US-1610 The Cruiser and the Carrier MUST require the shipworks city trait owned by 12-economy.md.

Rationale: capital hulls that can be laid down anywhere make every coastal city interchangeable and delete the naval half of the map's industrial geography. Requiring a specific trait turns a Shipworks port into a campaign objective worth a fleet action, and gives a player with no Shipworks a genuine strategic problem with a genuine answer — take one.


9. Roles and counter relationships

US-1620 Every unit MUST carry a roleText sentence naming a thing no other unit in the set does. Two units whose roleText sentences would be interchangeable MUST be treated as a design defect, not a balance problem.

US-1630 Every unit MUST have at least two units that counter it, and every unit whose class is not transport_sea, transport_air, engineer, carrier, administration or logistics MUST have at least two units it counters. Both lists MUST be stated in this document and shown on the unit card (US-950).

Rationale for the exemption: a Transport, an Engineer, a Carrier, a Cadre and a Depot counter nothing and are not supposed to. Forcing a counter list onto them would mean inventing an offensive role for units whose whole design is that they have none — and the exemption is one-sided, because everything in the game still has things that kill it.

Rationale: a counter relationship that exists only in the numbers is a counter relationship the player discovers by losing. Stating it makes the roster teachable in twenty cards instead of twenty games, and it is the mechanism that makes the escalation ladder work — each unlock arrives with its own place in a web the player already partly understands.

Ruled 2026-09-12 by measurement, and binding. The row this settles had been open since 2026-09-09: "either the minimums are wrong or those four lists are thinner than the classes deserve".

The lists were thin. The minimums stand. Measured against the shipped data rather than the prose, since this requirement binds both ("Both lists MUST be stated in this document and shown on the unit card"), exactly four units missed a minimum and they were the four the question named: Militia carried one entry on each side, Forward Depot carried an empty counteredBy, and Heavy Artillery and Heavy Bomber each carried one on the counters side. Every other unit passed.

Lowering a minimum was the alternative and it is the wrong answer, for the reason the rationale below already gives: the minimum is the mechanism. "A counter relationship that exists only in the numbers is a counter relationship the player discovers by losing. Stating it makes the roster teachable in twenty cards instead of twenty games." A card with one entry teaches less, not differently.

Every entry added was mirrored from an assertion this document already makes, not invented. US-1640's Scout row and its Air Transport row each say "any garrison at all" counters them, which is the Militia from the other side. US-1645 names bombardment disorder (11-combat.md CB-1720) as "Field Gun and Heavy Artillery over any dug-in defender", and this roster's dug-in defenders are Infantry and Militia. US-1640's own Heavy Bomber row says it counters "capital ships", and the capital ship of this roster is the Cruiser. The Forward Depot is the soft-rear family the Missile row already names — Engineer, Cadre, Transport — and it cannot run from what finds it.

What counts as an entry: a unit of the set. The lists in US-1640 are prose for a person and say things like "any garrison at all", "entrenched Infantry", "terrain", "its own move 1" — and they should, because a counter web is partly about the map and the unit's own limits. But the minimums count units, because a card that says "everything" gives a player nothing to look up. So the prose may say more than the data, and the data must carry at least two units on each required side. This is what scripts/check-counter-web.mjs enforces.

A self-reference belongs on both sides or neither. Found by that script's first run rather than by reading: the Corvette was the set's only self-reference and it was one-sided, so the data asserted that a Corvette beats a Corvette and is not beaten by one. A symmetric matchup is a real relationship — this table says "other Corvettes" in as many words — and a one-sided one cannot be true of any matchup. Made symmetric.

And the table was three rows short of the roster. This requirement says the web "MUST be exactly the following" and the table carried twenty rows for twenty-three units: Militia, Administrative Cadre and Forward Depot had no row at all, so three units' relationships lived in the data and not in the document the requirement points at. The rows are added above, and the script now fails if a unit has none — which is the difference between a table that is checked and a table that is proofread.

US-1640 The counter web of the shipped roster MUST be exactly the following.

Unit Counters Countered by
Scout undefended cities, exposed rear areas, unescorted Cadre and Engineers Infantry, Militia, Armour, any garrison at all
Infantry Scout, Air Transport landings, Armour in rough terrain and cities Field Gun, Heavy Artillery, Armour in the open, Bomber
Field Gun entrenched Infantry, Militia in cities, any dug-in defender Armour closing to contact, Bomber, Missile, Scout raids
Transport nothing — it is pure logistics Corvette, Submarine, Destroyer, Bomber
Corvette Transport, other Corvettes, shallow-water raiders Destroyer, Submarine, Cruiser, Bomber
Militia Scout raids, Air Transport landings — it is the "any garrison at all" the rows above name Field Gun, Heavy Artillery, Armour; anything that brings weight to a city it holds
Administrative Cadre nothing — it counters unrest, not a unit Scout raids, Missile, anything that reaches the rear; def 12 means it must never be alone
Forward Depot nothing — it counters distance Scout raids, Missile, Bomber, and its own move 1: it cannot run from what finds it
Armour Infantry in the open, Field Gun, Engineer, Scout entrenched Infantry in cities or rough ground, Heavy Artillery, Bomber, terrain
Fighter Bomber, Heavy Bomber, Air Transport, Missile — its whole engages set Air Defence Battery, Destroyer and Cruiser flak, its own endurance 2
Destroyer Corvette, Transport, detected Submarine; Bomber, defensively Cruiser, undetected Submarine, massed Bombers
Engineer terrain and distance — it counters the map, not a unit everything; def 22 and atk 10 mean it must never be alone
Air Defence Battery Fighter, Bomber, Missile, Air Transport within its zone Heavy Artillery, Armour, any ground attack at all
Landing Ship defended beaches, island garrisons Submarine, Destroyer, shore-based Field Gun, Bomber
Heavy Artillery fortified cities, entrenched stacks, Air Defence Batteries Armour breakthrough, Bomber, Missile, Scout raid, its own move 1
Submarine Transport, Landing Ship, Cruiser, Carrier Destroyer and Cruiser sonar, adjacency to anything at all
Bomber Armour, Heavy Artillery, unescorted shipping, city Yards Fighter, Air Defence Battery, Destroyer and Cruiser flak
Missile Heavy Artillery, Air Defence Battery, Engineer, Cadre, Transport Fighter intercept, Air Defence Battery interceptZone, its own expendability
Air Transport undefended rear cities, isolated garrisons Fighter, Air Defence Battery, any garrison at all
Heavy Armour dug-in lines at a single point, Armour, Infantry mass infantry frontage, Heavy Artillery, Bomber, rough terrain, slots 3 logistics
Heavy Bomber city Industry, Industry-2 armies, Heavy Artillery, capital ships Fighter, Air Defence Battery, Destroyer and Cruiser flak
Cruiser everything afloat gun for gun, shore targets, Corvette, Destroyer Submarine, massed Bombers, Missile, draft 3 geography
Carrier distance — it counters the map Submarine, Bomber, Cruiser, anything that finds it

US-1645 Every counter relationship in US-1640 MUST be carried by a named mechanism in the shipped data, and the mechanisms MUST be exactly these. A change that removes a mechanism MUST either restate the relationship against another one or delete the relationship.

Mechanism Which counters it carries
atk against def on the common scale every land and sea counter that is a straight fight — Armour over Infantry in the open, Heavy Armour over Armour, Destroyer over Corvette, Cruiser over Destroyer
role × terrain (CB-1450) Infantry over Armour in forest, mountain and city; the whole "Armour is punished by rough ground" family
engages (US-240) Fighter over the four air classes and over nothing else; Submarine confined to the sea; Air Defence Battery confined to the air; every "cannot touch" in the web
flak tier as attacker shock (US-260) Destroyer, Cruiser and Carrier over Bombers; Air Defence Battery over every air class; the reason a screened fleet is not a target
interceptZone as a Reaction (CB-1770) Air Defence Battery over Fighter, Missile and Air Transport passing through its zone
intercept (US-660) Fighter over highAltitude — the only counter to the Heavy Bomber other than flak ≥ 2
ambush as intensityBack = 0 (US-630) Submarine over Transport, Landing Ship, Cruiser and Carrier
submerged and sonar (GR §8) Destroyer and Cruiser over the Submarine, by finding it
Bombardment disorder (CB-1720) Field Gun and Heavy Artillery over any dug-in defender; the whole combined-arms case of §12.3
siege (US-615) Heavy Artillery over fortified cities
bombardRange on hulls Corvette, Destroyer and Cruiser over shore targets
cityStrike (§6.4) Bomber and Heavy Bomber over city Yards and Industry

Rationale: three of the old model's four combat mechanisms — the per-domain attack vector, the flak exchange, and strikeRun — do not exist in 11-combat.md, and between them they carried nine rows of this table. Listing where each row now lives is what makes the conversion checkable rather than merely asserted, and it is what a future change to either document has to be checked against. A counter relationship with no mechanism in this column is a claim on a unit card that the game does not implement.

US-1650 No unit in the shipped roster MAY have "everything" or "nothing" in its Counters column except where that is the literal design intent and is stated as such, as for the Transport, the Engineer and the Carrier.


10. Non-domination

US-1660 Unit A strictly dominates unit B when all of the following hold:

  1. A.domain = B.domain
  2. A.buildRequirements.minIndustry ≤ B.buildRequirements.minIndustry
  3. A.worksCost ≤ B.worksCost and A.manpowerCost ≤ B.manpowerCost
  4. A.slots ≤ B.slots
  5. every one of move, vision, visionAir, atk, def, tough, flak, capacity, bombardRange, bombardShots, interceptZone, cityStrike, repairAdjacent, repairEmbarked is greater than or equal on A than on B
  6. A.endurance ≥ B.endurance and A.draft ≤ B.draft
  7. A.engages is a superset of B.engages
  8. A's non-restrictive flag set is a superset of B's, and A carries no restrictive flag that B does not
  9. A.role = B.role, or A's role is not worse than B's in any terrain family of CB-1450
  10. A is strictly greater or strictly cheaper on at least one of the above

Rationale for clauses 7 and 9, which are new: under a single atk the domains a class may attack are no longer visible in its numbers, so a class that engages only air could otherwise dominate one that engages land and air on every integer. And role is now a real capability — a Foot class and an Armor class with identical stats are not comparable, because one of them is 700 per-mille worse in mountains. A domination test that ignored the terrain table would flag pairs that are not substitutes anywhere on the map.

US-1670 The shipped roster MUST contain no strictly dominated unit.

US-1680 Every unit in the shipped roster MUST have a unique claim: a capability or price point that no cheaper unit of the same domain and no lower-tier unit provides.

Unit Unique claim
Scout Cheapest capture; the only unit with vision 4 at Industry 1
Infantry The best defence per Works in the game (def × tough / W = 73, the highest of any class), and it entrenches
Field Gun The only Industry-1 indirect fire; the only unit that un-digs a defender before turn 20
Transport Six land slots — the largest sea lift, at any tier
Corvette Cheapest hull that can sink a Transport; the only warship with draft 1
Militia Cheapest body that satisfies a garrison requirement
Administrative Cadre The only administrator
Forward Depot The only mobile supplySource
Fighter The only intercept; the only counter to highAltitude
Armour The best land attack per Works below Industry 4
Destroyer Cheapest sonar; cheapest flak ≥ 2, the threshold that touches highAltitude
Air Defence Battery The only interceptZone; the only flak 4
Engineer The only buildables; the only mobile repair
Landing Ship The only assaultLanding
Heavy Artillery The only siege; range 3 and two shots, the deepest and heaviest bombardment
Submarine The only submerged and the only ambush
Bomber The only Industry-3 cityStrike; the longest reach at its tier
Missile The highest atk in the game and the only expendable; 2 Manpower
Air Transport The only airdrop
Heavy Armour The highest def and tough on land
Heavy Bomber The only highAltitude and the only cityStrike 2
Cruiser The highest def and tough in the game
Carrier The only airbase and the only repairEmbarked

US-1690 A CI check MUST evaluate US-1660 across the shipped roster on every build and MUST fail on any dominated pair.


11. The combat contract, and how §8's combat values were derived

US-1700 The combat values of US-230 MUST be stored and read as plain integers on 11-combat.md's single common scale (CB-150). There MUST NOT be a conversion step of any kind between this document's tables and the combat core: atk, def and tough enter CB-470 and CB-870 exactly as written here.

Rationale, and it corrects a real error rather than restating a decision. The previous requirement had this document store attack and defence as "whole points" and multiply them by 1000 for resolution, which was a conversion between two scales that no longer exist. 11-combat.md works in per-mille only for modifiers (CB-060); the stats themselves are integers on one scale whose reference value is tough = 100. A conversion factor here would be a second place for the scale to be defined and the first place for it to drift.

Held 2026-09-23 as packages/core/test/combat/roster-contract.test.ts. This is a rule that has already been broken once, which is the argument for testing it rather than trusting it: the retired version of this requirement mandated the very ×1000 step the current one forbids.

The guard looks for arithmetic at the assignment, not for "anything that is not a bare read" — a distinction the first draft got wrong and the run corrected. It flagged def: Int | null (a type declaration) and atk: fidelity >= 2 ? assessed.atk : null (GR-1260’s fog redaction, which withholds a value rather than rescaling one). Neither is a conversion, and a guard that fires on unrelated shapes is one that gets widened until it fires on nothing. So the rule is: a multiply, a divide, an intMul/intDiv, or a literal 1000 between the table and the core.

Three further rows, because a scan alone proves too little. The shipped roster’s own numbers are read back and asserted to be plain integers with tough on CB-150’s scale — a set that had been through a ×1000 conversion fails on its first entry. No COMBAT_SCALE-style constant exists in the units module, which is how the conversion returns without being inline. And the combat tests’ own golden rows are checked against the shipped roster, which closes a quieter failure: test/combat/helpers.ts hard-codes Infantry, Scout, Field Gun and Armour, and if the roster moved while those did not, the entire combat suite would keep passing against a unit the game does not contain — 11-combat.md’s 100% would be 100% of the wrong numbers. All four match today. Nine mutants, nine killed.

US-1710 11-combat.md owns combat resolution entirely, and the roster of §8 is derived against the model that document specifies: a single exchange (CB-020) producing losses from an odds ratio and a break from shock against resolve (CB-030), with additive per-mille modifiers (CB-540), normalised strength in [0, 100] (CB-110), and tough as the durability divisor (CB-160). This document MUST NOT describe a resolution procedure, a round, a hit share, a multiplicative defence bonus, or a suppression state.

Held 2026-09-23 by reading the prohibition as a constraint on this document’s implementation: no resolution vocabulary may live in core/src/units/ — no hit share, no round, no suppression, no defence multiplier, and no call to resolveExchange. That is how a second combat model starts, one helper at a time and each for a good local reason. Paired with the row that matters more: resolution is declared exactly once in the whole core, because CB-2140’s guarantee that the preview and the resolution agree is only meaningful while there is one of each.

US-1715 The combat values of §8 MUST be derived by the following method, applied in this order, and every deviation from it MUST be recorded with a reason in the unit's own requirement.

Step 1 — role. Assign 11-combat.md's combat role from class: recon → Recon; infantry, garrison, engineer, administration, logistics → Foot; artillery and air_defence → Gun; armour → Armor; fighter, bomber, missile, transport_air → Air; transport_sea, escort, submarine, capital_ship, carrier → Naval. The role is what indexes CB-1450's terrain table, so it is a statement about where the class fights well, not about what it is called.

Step 2 — tough, the size ladder. tough is bulk: how much unit there is to grind down. It is generated from the class's establishment size e — a 1–18 index recorded in the derivation table of US-1716 and deliberately not shipped as a field — scaled by domain:

ladder = round10( 25 + 12.5 × e )                    ties rounded up
tough  = round10( ladder × domainFactor / 1000 )
domainFactor = 600‰ for domain air, 700‰ for a class carrying `submerged`, 1000‰ otherwise

The air factor is not a balance dial: an airframe has no rear echelon to absorb losses into, so damage to an aircraft is damage to the whole formation. The named anchors of the ladder are tough 60 (a light screening element), 70 (a towed battery), 100 (the reference infantry battalion), 130 (an armoured battalion), 150 (a fleet escort) and 240 (a capital hull); the first four are the values CB-1870 fixes parting losses against, so every one of them is exercised by a shipped class.

Step 3 — atk, from offensive weight. atk = roundUpToEven( 4 + floor(32 × w / 5) ), where w is the class's 0–20 offensive weight index, taken against the one domain the class is built to attack. The four classes named in 11-combat.md's golden vectors (CB-2040, CB-2360, §23) keep the exact values those vectors fix, since a normative test vector cannot be rounded.

Step 4 — def, from the job. def is not derived from atk arithmetically; it is set by what the class is for, as a ratio to atk:

Job def : atk Classes
Holds ground 1.45 : 1 Infantry
Garrisons a place it may not leave 2.20 : 1 Militia
Screens and sees 1.20 : 1 Scout
Fights and survives by bulk 0.60 : 1 Armour, Heavy Armour
Fires and must not be reached 0.38 : 1 Field Gun, Heavy Artillery
A hull fights about as well as it is fought 0.80 : 1 Corvette, Destroyer, Cruiser
Cannot dig in and cannot hold — an airframe or a hull that fights by not being there 0.45 : 1 Fighter, Bomber, Heavy Bomber, Missile, Submarine

Every product is rounded to the nearest even value, ties up. For a class that does not attack — atk at or below 16 — def is set from the size ladder instead, at one of three rates: 220‰ for a hull whose loss is the point of the escort battle (Transport, Landing Ship), 250‰ for a class that is shielded by the line it stands behind (Administrative Cadre, Forward Depot, Engineer, Air Transport), and 350‰ for one built to survive being found (Carrier).

Rationale for the 220‰ rate being lower than the 250‰ one, which looks backwards and is not: a transport at sea with no escort is the one thing in this game that is supposed to die, because the whole design of convoy and escort collapses if it does not. A Cadre or a Depot sits inside its owner's own territory behind a front, and is lost to a breakthrough rather than to an intercepting hull.

Rationale for deriving def from the job rather than from a single old defence number: the old model had one def doing two jobs at once, because it had no tough. "Hard to displace" and "hard to grind down" were the same integer, so an armoured battalion had to buy its survivability out of its defence rating and ended up defending better than it should. 11-combat.md splits them (CB-160), and the split is exactly why the previous values could not be carried across by arithmetic. The check that the split was done honestly is that def × tough / 880 reproduces the old single number to within one step for every one of the four calibration classes — which is the sense in which the old field was the product of the two new ones.

Step 5 — the capability fields. engages records the domains the class may initiate against (US-240); flak is the four-step air-defence tier of US-260; retreatClass follows US-1355; bombardRange and bombardShots follow US-390 and US-395.

US-1716 The derivation of US-1715 applied to the shipped set is recorded below so that any single row can be checked. e is the class's establishment size on the 1–18 index of Step 2 and w its offensive weight on the 0–20 index of Step 3; neither is a shipped field, because each feeds exactly one derived value and shipping both the input and the output would be two chances to disagree.

Unit e ladder factor tough why it differs w formula atk job def
Scout 3 60 — 60 — 2 16 18 screen 1.20 22
Infantry 6 100 — 100 — (the reference) 4 30 30 hold 1.45 44
Field Gun 4 80 — 70 a towed battery is a crew and two guns, with no depth behind them 7 48 48 gun 0.38 18
Transport 8 130 — 120 a merchant hull has no damage control 0 — 0 escort 220‰ 26
Corvette 7 110 — 110 — 6 42 42 hull 0.80 34
Militia 5 90 — 80 a scratch formation with no reserves behind it 2 16 16 garrison 2.20 36
Administrative Cadre 3 60 — 50 not a fighting body at all 0 — 0 shielded 250‰ 12
Forward Depot 6 100 — 90 a dump and its handlers, not a formation 0 — 0 shielded 250‰ 22
Fighter 5 90 600‰ 50 air 9 62 62 air 0.45 28
Armour 8 130 — 130 — 9 62 62 armour 0.60 38
Destroyer 10 150 — 150 — 9 62 62 hull 0.80 50
Air Defence Battery 5 90 — 90 — 11 74 74 gun 0.38 → raised 40
Engineer 5 90 — 90 — 1 10 10 shielded 250‰ 22
Landing Ship 12 180 — 170 thin-skinned by design; it is a beach lorry 0 — 0 escort 220‰ 38
Heavy Artillery 6 100 — 80 a gun park has no infantry to soak losses into 14 94 94 gun 0.38 36
Submarine 8 130 700‰ 90 concealment is its armour and it has none once found 14 94 94 air 0.45 42
Bomber 6 100 600‰ 60 air 12 80 80 air 0.45 36
Missile 1 40 600‰ 20 air; one warhead, and the only class at CB-150's floor 20 132 132 air 0.45 60
Air Transport 6 100 600‰ 60 air 0 — 0 shielded 250‰ 16
Heavy Armour 14 200 — 190 a regiment of vehicles, not of bodies 16 106 106 armour 0.60 64
Heavy Bomber 9 140 600‰ 80 air 16 106 106 air 0.45 48
Cruiser 18 250 — 240 one step down: a hull that big is also that flammable 16 106 106 hull 0.80 84
Carrier 16 230 — 220 a full flight deck is a fire hazard 2 16 16 survives 350‰ 78

Exactly two values deviate from what the method produces, and both are governed by US-1717:

  1. Scout atk 18, not 16. 18 is the value 11-combat.md's golden vectors and its parting-loss anchors already fix for a tough 60 screening class (CB-1870, §23). A normative test vector outranks a rounding rule.
  2. Air Defence Battery def 40, not 28. At the Gun ratio the battery breaks before its own flak shock can throw the attacker back, which inverts US-1490 — the unit's entire claim is that it makes a stretch of sky expensive, and a defender that breaks first makes nothing expensive. 40 is the lowest value at which a Bomber assaulting an entrenched battery in the open cannot break it at any draw, which is the design intent stated as an inequality rather than guessed at.

Rationale for recording e and w here rather than shipping them: both are one-use inputs. e exists to place a class on the size ladder and w to place it on the offensive one, and once placed, the derived value is what the game reads. Shipping the inputs would invite a modder to change e and expect tough to follow, which nothing implements, and would put two numbers in the canonical hash where one is needed. Recording them makes the roster auditable without making it redundant.

US-1717 Where the derivation of US-1715 and the design intent recorded in a unit's own requirement (§8) or in the counter web (US-1640) disagree, the design intent MUST govern and the deviation MUST be stated.

Rationale: the derivation exists so that a reader can check any row and so that a modder can extend the roster on the same basis. It is a generator, not an authority. The counter relationships are the design; the integers are one expression of them, and a generator that produced a Fighter which beat tanks would be wrong about the Fighter, not right about the arithmetic.

US-1720 If 11-combat.md's procedure, tables or constants change materially, the values of §8 MUST be re-derived by US-1715 and the counter web of US-1640 MUST be re-verified against them. The relationships of §9 are the design; the integers of §8 are an implementation of them against a specific procedure. no-test-needed: a process obligation — it binds whoever materially changes 11-combat.md's procedure, tables or constants to re-derive §8 by US-1715 and re-verify US-1640, and the failure mode is that person not doing it. No test can make a human do it; what could be held mechanically (a generator run in verify) is recorded in the note below for when §8 next moves.

Flagged gap, 2026-09-23: this is a process obligation and nothing enforces it. It binds whoever changes 11-combat.md’s procedure, tables or constants — re-derive §8 by US-1715, re-verify US-1640’s counter web — and no test can make that happen, since the failure mode is a human not doing it.

What could be held mechanically, and is worth building when §8 next moves: the derivation of US-1715 is an algorithm with named inputs (the establishment-size ladder of US-1716, the role mapping, the domain scalings), so §8’s integers could be generated and the generator run in verify — which converts "somebody must remember to re-derive" into "the build fails until they do". The obstacle is not the algorithm but the deviations: the requirement admits that "every deviation from it MUST be recorded with a reason in the unit’s own requirement", so a generator needs those recorded as data rather than prose. Until then this stays a gap, and the honest reason is that a test asserting the current numbers are self-consistent would pass whether or not anyone had re-derived them.

Rationale: stating this plainly is cheaper than discovering it, and this reconciliation is the proof. A change to how losses are computed does not invalidate "artillery softens, infantry takes ground" — it invalidated every number on the Field Gun's line, and re-deriving them was a day's work while re-deciding what a Field Gun is for would have been a redesign.

US-1730 The client MUST be able to display an exact win probability for any proposed engagement before the player commits. That predictor MUST be 11-combat.md's winPermille (CB-2200), computed by the same function that resolves the engagement (CB-2140), and this document MUST NOT specify a second one.

Rationale: the old rationale here argued that a race between two hit counts at a fixed per-round probability has a closed form. It does, and it is not the model this game ships. 11-combat.md reaches the same honesty by a different and stronger route: one draw, every downstream quantity monotone in it, and therefore an exact count of favourable draws found in thirty-three evaluations of the real resolution function (CB-2230). The property the player cares about is unchanged and the guarantee is now enforceable — a preview that disagreed with a resolution would be a CI failure (CB-2660) rather than a bug report.

Held 2026-09-23 by exhaustion, which is the only honest way to check a claim of this shape. CB-2230 finds winPermille in thirty-three evaluations by monotonicity; the row does not trust the shortcut — it resolves the engagement at all thousand draws and counts the ones where the defender breaks and the lead attacker survives. The count the preview promises is the count the resolver delivers, and it is checked again at four lopsided matchups (Scout into Infantry, Armour into a Field Gun and the reverse, Infantry into an entrenched defender), because the edges are where a closed-form approximation parts company with the real procedure.

Two structural rows carry the clause the rest of the way. The core preview calls resolveExchange — so the agreement is not a coincidence of two implementations that happen to match on these specs — and the client takes its preview from the core package and declares no winPermille of its own, which is where this requirement actually points, since it addresses the client. A mutant that shifts the threshold by one, a mutant that severs the search from the resolver, and a mutant that gives the client its own predictor are all killed.


12. Balance laws and derivation

Every number in §8 is a design decision. These are the two rules that generated most of them, so that any single value can be re-argued on its own terms.

12.1 Law 1 — build times stay constant across the ladder

US-1740 The Industry output multipliers of US-820 MUST be chosen so that every manoeuvre unit takes between 3 and 11 turns to complete at the reference producer at that unit's unlock tier.

Tier Reference output Build times, cheapest to dearest
1 12 Scout 3 · Infantry 5 · Field Gun 7 · Transport 8 · Corvette 9
2 21 Fighter 7 · Armour 8 · Destroyer 10
3 33 Air Defence Battery 4 · Engineer 4 · Landing Ship 7 · Heavy Artillery 7 · Submarine 8 · Bomber 8
4 48 Missile 3 · Air Transport 6 · Heavy Armour 8 · Heavy Bomber 9 · Cruiser 10 · Carrier 11

US-1750 Industry upgrades MUST themselves fall inside the band at the reference producer: Workshop 8 turns, Foundry 9, Arsenal 11.

Held 2026-09-23 as packages/core/test/units/balance-laws.test.ts, and the three figures are recomputed rather than quoted: 90 Works at 12 per turn is 8, 180 at 21 is 9, 360 at 33 is 11, each inside US-1740’s 3–11 band. The row also checks the rationale’s own claim — that the multipliers 1000/1750/2750/4000 "are not a taste judgement, they are the solution to this constraint" — by deriving the 12/21/33/48 outputs from them.

Rationale for the whole law: the rhythm of the game must not change as the pieces get bigger. A tier-4 empire is not waiting longer for its toys; it is getting better toys at the same cadence. That is what makes the ladder feel like growth rather than like a tax, and it is where the multipliers 1000/1750/2750/4000 come from — they are not a taste judgement, they are the solution to this constraint.

12.2 Law 2 — concentration rises with tier, efficiency falls

This law is stated the other way up from the way it used to be, because 11-combat.md changed what a tier buys. Both figures below are design-time analysis quantities, not simulation arithmetic: US-030's integer discipline binds the engine, and neither of these is ever computed at runtime.

US-1760 Define, for each class:

combatWeight  Wt = floor( sqrt( primaryFace × tough ) )
concentration  E = round( 1000 × Wt / worksCost )

where primaryFace is max(atk, def) for a class that may initiate a Clash and def for a class that may not — a class with reactiveOnly, or with an empty engages, is worth what it is worth standing still.

Corrected 2026-09-23: E was defined with floor and every published value uses round. *Measured against the shipped roster while building §12’s tests: all thirteen of US-1780’s E figures match round and only nine match floor. The four that differ — Field Gun, Fighter, Landing Ship, Carrier — are each off by exactly +1, in the direction of rounding. The tables are internally consistent under round and inconsistent under floor, so the word in the formula was the error rather than the four numbers, and changing one word is cheaper and less risky than editing four values that nothing else disputes. Wt keeps its floor, which is correct: all twenty-one published combat weights match the shipped roster exactly.*

Nothing observable moves, and this requirement says why itself: both figures are "design-time analysis quantities, not simulation arithmetic ... neither of these is ever computed at runtime". No hash, no corpus game, no container. Flagged for the spec owner, who may prefer the other repair — keep floor and correct the four table values to 730, 398, 380 and 254.*

One trap worth recording for anyone recomputing these. reactiveOnly is a capability in the unit’s flags array, not a field of its own, so reading it as a top-level property yields undefined, which is falsy, which makes every unit look like an initiator. That misreading puts the Air Defence Battery at Wt 81 instead of 60 — and looks exactly like roster drift rather than like a bug in the reader. Its atk of 74 is real and is deliberately not counted: it cannot initiate a Clash, so US-1760 values it at what it is worth standing still.

Rationale for the shape: 11-combat.md multiplies power by condition and divides losses by tough (CB-470, CB-870), so what a class brings to one tile is the geometric mean of the two, which is what Wt measures. The old index used atk × hits, which under the new model would have counted an attack the class may not make and a durability field that no longer exists.

US-1770 No unit in the shipped roster MAY exceed its tier's combat weight ceiling.

Tier Ceiling Units at or near it
1 70 Corvette 67 · Infantry 66
2 100 Destroyer 96 · Armour 89
3 100 Submarine 91 · Heavy Artillery 86
4 160 Cruiser 159 · Heavy Armour 141

US-1780 A unit whose combat weight sits materially below its tier ceiling MUST have a stated capability it is buying instead.

Unit Wt E What it buys instead
Scout 36 1000 Five movement and four vision
Field Gun 57 731 Indirect fire that takes no return damage
Militia 53 1325 Garrison points at the lowest price in the game, and it may never leave
Transport 55 611 Six land slots
Fighter 55 399 intercept, and the only answer to highAltitude
Landing Ship 80 381 assaultLanding and four land slots
Air Defence Battery 60 476 flak 4 and the only interceptZone
Bomber 69 261 Reach, and cityStrike
Engineer 44 333 buildables and the only mobile repair
Missile 51 425 Reach 24, no manned asset exposed, and 2 Manpower
Air Transport 30 119 airdrop
Heavy Bomber 92 232 highAltitude and cityStrike 2
Carrier 130 255 airbase and repairEmbarked

Held 2026-09-23, and all thirteen rows match the shipped roster exactly (with E read as round, per the correction at US-1760). That is the result worth recording: these tables were computed once and the roster has moved since, and a published table that has silently parted company with the data reads as verified when it is not — nothing else in the suite would notice, because no engine behaviour depends on these numbers, so they could drift for as long as nobody recomputed them.

A second row checks the claim the table is making rather than its arithmetic: every unit listed here really does sit below its tier’s US-1770 ceiling. A row that had crept up to the ceiling would still match its own figures and would no longer belong in a table about units that buy something else instead of weight.

US-1790 Combat weight MUST rise across the ladder and concentration efficiency MUST fall. The shipped bands are:

Tier Combat weight, manoeuvre units Concentration efficiency
1 36 – 67 611 – 1100
2 55 – 96 399 – 571
3 44 – 91 261 – 476
4 30 – 159 119 – 425

Held 2026-09-23: all eight band endpoints are the exact range the shipped manoeuvre units occupy, on both curves. "Manoeuvre" is read as excluding the Militia (which garrisonOnly says may never leave), the Administrative Cadre and the Depot — without that reading the Militia’s efficiency of 1325 sits outside tier 1’s stated 611–1100, which is the band’s way of saying it is not a field unit.

And the law is asserted separately from the table, because all eight endpoints could match while the claim failed: the tier-4 ceiling more than doubles tier 1’s (159 against 67), and efficiency falls monotonically tier by tier, by roughly the factor of three the rationale claims. That is the property derived from CB-400 and CB-1950 — only the top defender of a stack contributes power — and it is what keeps mass and concentration a live question at every tier.

Rationale, and this is the sentence the old law got backwards. It used to say the power ceiling must rise about 20% per ladder so that climbing is worth it. Under 11-combat.md that is not why climbing is worth it, and pricing it that way would have made the roster wrong. The reason is CB-400 and CB-1950: only the top defender of a stack contributes power, and the rest add a flat +100 apiece to the modifier. Six Infantry on one tile therefore defend as one Infantry plus +300, while one Heavy Armour defends as a Heavy Armour — so what a higher tier sells is the ability to put more of your Works on one tile, and that is what Wt measures. It more than doubles across the ladder, from 67 to 159. Efficiency meanwhile falls by a factor of three, from Infantry's 1100 to the Cruiser's 331, and that fall is the counterweight: five Infantry at 300 Works hold five tiles of frontage that one Heavy Armour at 372 cannot, and lose to it at any single one of them. Mass and concentration stay a live question at every tier because the two curves run in opposite directions, which is a stronger guarantee than the old 20% premium and it is a consequence of the combat model rather than a target set beside it.

US-1800 Tier 3 MUST be understood as the capability tier and tier 4 as the power tier: no tier-3 unit reaches its ceiling and no tier-3 unit exceeds the tier-2 maximum, and this is intentional.

Rationale: the Foundry is not bought for a better tank. It is bought for concealment, strategic air, engineering, amphibious assault and air defence — five capabilities that do not exist below it and that no amount of tier-2 Works can substitute for. Pretending tier 3 also had to hit a power target would have meant inflating six units past their roles. The upgrade still pays for itself in output alone: +12 Works per turn at the reference producer against a 180 Works cost is a 15-turn payback before a single new unit is built.

US-1810 The Manpower cost of every unit MUST be set so that infantry masses sit below the empire's own Works-to-Manpower supply ratio and mechanised, naval and air forces sit above it. The following Works-to-Manpower bands are normative and are the ones every other document must use; they are derived from the shipped roster and every shipped unit falls inside its own band.

Band W : M Shipped members and their ratios
Line infantry 1.4 – 2.0 Infantry 1.67
Reconnaissance 2.0 – 3.0 Scout 2.40
Dedicated garrison 2.0 – 3.0 Militia 2.50
Engineering 3.0 – 4.0 Engineer 3.30
Artillery and air defence 4.0 – 5.5 Field Gun 4.33 · Heavy Artillery 4.50 · Air Defence Battery 5.25
Naval light, escort, submarine 5.0 – 6.5 Destroyer 5.33 · Corvette 5.67 · Submarine 6.45
Naval heavy and carrier 6.0 – 9.0 Cruiser 6.00 · Carrier 8.50
Administration 7.0 – 8.0 Administrative Cadre 7.50
Armour and mechanised 7.5 – 9.5 Armour 7.80 · Heavy Armour 9.30
Logistics and lift 9.0 – 16.0 Landing Ship 10.50 · Transport 11.25 · Forward Depot 15.00 †
Air 14 – 22 Heavy Bomber 16.50 · Bomber 16.50 · Fighter 17.25 · Air Transport 21.00
Unmanned above 40 Missile 60.00

US-1815 A CI check MUST assert that every unit in the shipped set falls inside the band its class maps to, and MUST fail the build on any violation. 12-economy.md MUST cite this table rather than restating it (US-2130).

The check MUST read the Forward Depot's cost from 12-economy.md EC-1620 rather than from the unit set, because the Depot is the one shipped unit whose worksCost and manpowerCost are not in standard.json — EC-1620 owns them (the † above). A check that read only the unit set would skip it silently.

Corrected 2026-09-13, by building the check this requirement asks for. Twenty-two of the twenty-three stated ratios matched the shipped data; the Forward Depot did not. It was stated at 10.00, and 12-economy.md EC-1620 prices it at 60 Works and 4 Manpower — a ratio of 15.00, which fell outside the 9.0–13.0 band this table assigned it. The band is widened to 9.0–16.0 and the ratio corrected.

Why this one and not the other twenty-two: the Depot is the only shipped unit whose costs are not in standard.json. Its worksCost and manpowerCost fields are simply absent — EC-1620 owns the figure, because the price varies with the Railhead trait (30 Works there, 60 elsewhere). So a derivation that read the roster file computed twenty-two ratios and quietly skipped one, and the skipped one was the only one that was wrong. US-1815 now says where to read it.

The remedy follows this requirement's own recorded precedent rather than a fresh judgement. The rationale below already decided this exact question once — five units fell outside the previous bands, and "correcting the roster to fit would have meant re-pricing five units to satisfy a table that was never derived from a roster; correcting the table costs nothing anyone can feel, because the bands are ratios". The same reasoning applies to one unit. And 15.00 is arguably the better number on its own terms: a Depot is an emplaced installation with almost no crew, so Works-heavy and Manpower-light is what it should be, and the thing the bands exist to protect — infantry below the empire supply band, everything mechanised above it — is untouched.

Rationale for moving the bands to fit the roster rather than the reverse, which was a real decision and not a shortcut. The bands as previously stated elsewhere were anchored to absolute costs — 40 Works for line infantry against this roster's 60 — on a different Works scale, and they omitted bands for reconnaissance, engineering, administration and unmanned classes entirely, which are four of the twenty-three units. Five shipped units fell outside them. Correcting the roster to fit would have meant re-pricing five units to satisfy a table that was never derived from a roster; correcting the table costs nothing anyone can feel, because the bands are ratios and a ratio band says the same thing on any Works scale. The thing the bands actually exist to protect — that infantry sits below the empire supply band and everything mechanised above it — is preserved exactly, and is now checkable, which it was not.

Rationale for the bands themselves, unchanged: an empire's own supply ratio runs about 1.67:1 for a populous Metropolis to 3.00:1 for a Town-heavy holding, clustering near 2.2:1. Infantry priced below that band is manpower-bound; everything mechanised is works-bound. Which army a player can field is therefore dictated by the shape of the empire they hold, not merely its size — and that is the entire return on having a second resource. It also produces the striking result the economy design predicts from the other side: the fast conqueror, rich in captured factories and starved of people who will answer his call, is forced to mechanise.

12.3 Worked example — combined arms

US-1820 The following computation MUST hold under 11-combat.md's shipped resolution and is normative as a test vector. Every figure is the exact output of the preview function of CB-2140.

The situation. An Infantry attacks an Infantry holding its own city on Open terrain, Stance Entrench, entrenchment 1, both Green and Adequate, both at full strength, no supporters, no flankers. The attacker is in Stance Advance.

Working Result
Attacker contribution 30 × 100 × 1000 × 1000 / 10⁸ = 30, then Advance +250 A = 37
Defender contribution 44 × 100 × 1000 × 1000 / 10⁸ = 44, then Entrench +300, position +100 D = 61
Defender resolve 40 + 0 + 15 + 0 + 10 65
Attacker resolve 40 + 0 − 10 30

They lose 17 / 22 / 28; you lose 31 / 37 / 42. They break: 0‰. Your attack falters: 1000‰. The assault is not merely unlikely, it is impossible, and it costs you half again what it costs them. This is the correct answer to a frontal assault on a dug-in garrison and the readout says so before the player commits.

Now bring two Field Guns — 156 Works — and fire them first, which US-290's initiative assignment makes possible in the same turn.

Shot A D Strength taken Disorder added
1 48 61 9 3 × 9 + 5 = 32
2 48 56 10 3 × 10 + 5 = 35

The defender is now at strength 81 and disorder 67. Its resolve has fallen from 65 to 5 — 67 points of disorder subtract point for point, and nine points of missing strength subtract four more (CB-710). Its defence power has fallen only from 61 to 49.

The same assault, unchanged: they lose 19 / 25 / 32, you lose 27 / 34 / 40, they break: 1000‰.

Rationale: never to certain, for 156 Works of artillery and one turn of setting up. Note what did and did not move. The odds went from 37 : 61 to 37 : 49 — still against the attacker, still a losing exchange, and the attacker still pays more strength than it takes. What changed was resolve, and resolve is what decides whether the ground changes hands. That two-track structure (CB-030) is the whole reason a third role exists: guns do not kill, guns unstick. The old worked example reached the same lesson as 7.7% against 49.2%; this one reaches it as 0‰ against 1000‰, which is a sharper statement of the same idea and a considerably better teaching moment.

12.4 Worked example — one bomber is not an answer

US-1830 A Bomber attacks a Destroyer at sea, both full strength, Green, Adequate, the Bomber in Advance. A = 100 against D = 50 — two to one, and the odds are the least important number on the panel.

The Bomber removes 21 / 26 / 30 strength and takes 23 / 33 / 45 back, because tough 60 against tough 150 means the exchange hurts the aeroplane more than the ship whatever the ratio. The Destroyer's flak 2 then adds +12 to the attacker's shock (US-260), against an attacker resolve of 30. Attacker shock is therefore at least 35 at the most favourable draw: the attack falters at every draw, the Bomber gains +25 disorder, and it may not attack again this turn (CB-1150).

Rationale: one bomber cannot sink a destroyer, and under this model it cannot even press the attack. Four bombers over two turns can, and will lose one doing it. Massed air works; sniping with air does not. And a fleet that brings a Destroyer screen has multiplied its own survivability without buying a single dedicated anti-air unit — because flak is an attribute, not a unit class. Note that the mechanism has changed completely and the conclusion has not: the old model got here with a return-fire round before the strike, and this one gets here with a shock term in the break check, which is one fewer step in the procedure and a better description of what anti-aircraft fire actually does.

12.5 Worked example — is the ladder worth climbing?

US-1840 One City-class city at Industry 1, 12 Works per turn, no captures.

Path A, tempo. Scout (turns 1–3) · Infantry (4–8) · Infantry (9–13) · Infantry (14–18) · Transport (19–26). At turn 26: one Scout, three Infantry, one Transport — 306 Works on the board, three cities holdable, and a boat.

Path B, industry. Scout (1–3) · Workshop, 90 Works (4–11) · output becomes 21 from turn 12 · Armour (12–19) · Destroyer in progress (20–26, 147 of 192 paid). At turn 26: one Scout, one Armour, most of a Destroyer — 339 Works of value, and three unit classes Path A cannot build.

The upgrade's break-even lands at turn 22. But Path A holds three cities at turn 18 and Path B holds one.

Held 2026-09-23. A worked example the specification nominates is a normative test vector by this repository’s testing discipline, so both paths are recomputed from the shipped costs rather than taken on trust: Path A’s turn spans (3, 5, 5, 5, 8) sum to the 26 the example stops at and its pieces to the stated 306 Works; Path B’s Workshop takes 8 turns at 12, its Armour 8 at 21, and seven turns of 21 pay 147 of the Destroyer’s 192, for the stated 339. The turn-18 comparison is checked too, since it is the tension the example exists to show.

Rationale: the right tech pace is a function of the map and of the victory condition. On a crowded continent, tempo wins outright — the land is taken before the Workshop finishes. On an archipelago, or under a victory condition that runs past turn 60, the ladder wins. That is a genuinely good property: it means 14-victory.md's configurable conditions and this subsystem reinforce each other, and a short scored game and a long conquest game will not merely end differently, they will be played differently from turn four.


13. Setup: the shape of the curve

US-1850 Setup MUST NOT ask a player to choose a roster. It MUST ask for the shape of the Industry curve through the parameters of US-1860.

Rationale: the setup screen is where a new player is least equipped to judge how much complexity they want, and they will pick wrong in both directions. Moving the decision into the game hands it to the same person fifteen turns later, with information, with something at stake, and with the option to change their mind. It also means one roster, one balance surface, one AI evaluation function and one regression suite instead of four — which is the engineering budget that buys twenty properly tuned units instead of eighty badly tuned ones.

Built 2026-09-24 (Codex CLI), with the readings it leaves open. "Cap parity" is presentation, not storage: the screen offers one control or per-player controls, and mechanically both write to SetupPlayer.industryCap (US-900) — no rule computes differently for having seen a flag. The presets ship their cap applied to every player (parity on), and per-player divergence afterwards is exactly the handicap US-1890 exists for. The multiplier's lower bound reads as 1‰: it scales a cost, so zero or less names no scaling, and the named refusal says so rather than shipping free Industry as an unlisted option; its upper end stays open (US-1860 states per-mille and nothing more). The parameters ride SetupConfig.industry beside the cap's existing home, resolved at setup with GR-1490-shape refusals — held in industry-curve.test.ts, which pins the surface, ranges and defaults.

US-1860 Setup MUST expose exactly four parameters: start Industry (1–4), Industry cap (1–4, settable per player), upgrade cost multiplier (per-mille), and cap parity (whether all players share one cap).

Built 2026-09-24 (Codex CLI) where setup owns it: the curve's closed surface names no units — a game never chooses a roster through this, and US-2080/US-2090's one-set discipline stands held where it is named. "Ask for the shape of the Industry curve" through the screen itself rides with 04-ui-ux.md's setup surface.

US-1870 The following presets MUST ship, and MUST be presented with the sentence given.

Preset Start Cap Upgrade ׉ Costs For
Outpost 1 2 1000 90 A first game. Eight unit types, ever — a complete game of land, sea crossing and escort.
Standard 1 4 1000 90 / 180 / 360 The full arc. The default.
Escalation 1 4 600 54 / 108 / 216 Shorter games; the deep roster arrives by mid-game.
Total War 3 4 1000 360 Starts at the Foundry, for players who want the deep game immediately.
Fixed n n n — — Freezes the roster at one tier.

Built 2026-09-24 (Codex CLI) in values: INDUSTRY_CURVE_PRESETS carries the four rows exactly — Start / Cap / Upgrade ׉ — and industry-curve.test.ts re-derives the Costs column from start, cap and scale rather than asserting it as a fourth input. The presentation half ("MUST be presented with the sentence given") rides with 04-ui-ux.md's setup screen: the registry deliberately carries no sentences (the content-boundary discipline of US-2190's held core half).

US-1880 The Fixed preset MUST be implemented purely as start = cap, with no additional code path.

Rationale: the classic fixed-tier experience falls out of this design as a degenerate case, for free. Anyone who wants "pick a set and play it" sets start equal to cap and has exactly that. We get their mode as a special case of ours; the reverse is not true, and that asymmetry is the whole argument for the ladder in one row of a table.

Built 2026-09-24 (Codex CLI). There is no Fixed in the registry and none to add: a fixed tier n is { startingIndustry: n } plus per-player caps of n through this very surface — one code path, by construction. industry-curve.test.ts pins both halves: the absence, and that the pair of options actually freezes a tier (every city at n from turn one; an upgrade beyond the cap refused in US-910's own sentence).

US-1890 The Industry cap MUST be settable per player, and MUST be the game's supported handicap mechanism for mixed-skill play.

Built 2026-09-24 (Codex CLI), pinned where it stands: SetupPlayer.industryCap per player, absent the no-handicap 4 — held in industry-curve.test.ts beside the curve that surrounds it.

US-1900 The start Industry parameter MUST apply to every city on the map at game start, not only to players' initial cities, unless the map data specifies a per-city Industry value, which MUST take precedence.

Rationale: if Total War raised only the starting cities, a player's first conquest would drop them back into the shallow game and the preset would fail at exactly the moment it mattered. Applying it to the map means the preset genuinely delivers the deep game, which is the only reliable way tier-4 play gets tested at all — and tier 4 is the most likely place for a balance disaster to hide, because most games will end before anyone reaches it naturally.

Built 2026-09-24 (Codex CLI). The parameter stands at every city of a generated map from turn one — founding and neutral, Charter-placed among them — with the Ledger memory opening under it (applyStartingIndustryCurve, one named place; what maps can carry today is exactly the pass's scope, which is how the precedence clause reads against the built surface). One composition the clause leaves open: AI-105's per-seat grant reads as raising above the curve, never below it — US-1000's memory never decreases, so a seat cannot be granted back under what the map already stands at (the max of the two; on Standard games this is exactly what grants always did).

US-1910 Tier-4 balance MUST be validated by AI self-play at start Industry 3 and 4, and MUST NOT be validated by sampling ordinary Standard games.

Held 2026-09-24 (Codex CLI): a self-play validation obligation — measured in scripts/full-game.mjs's AI lane at the curve's own starts, not pinnable by vitest. The parameter it waits for now exists (SetupConfig.industry.startingIndustry), so the gate is runnable when that measurement takes it.

Run 2026-09-24 (Claude Code), and the gate fails on its instrument, not yet on the roster. The curve now reaches newGame (industryCurve, absent for the Standard preset so no hash moves) and node scripts/full-game.mjs --start-industry 3 (or 4) plays the game at that start and reports US-2210's gates. Played on the reference seed:

Game Heavy Armour's share of Works Dead classes Scout griefing Yard cap
Duel/2/120, start 3 99.2% 21 of 22 36.4% 16.6%
Duel/2/120, start 4 98.9% 21 of 22 28.6% 17.2%
Standard/4/120, start 3 98.5% 21 of 22 19.4% 11.7%
Standard/4/120, start 4 95.5% 21 of 22 21.6% 12.5%

Every seat reached Industry 4 and built Heavy Armour and almost nothing else. That says the Constraint Floor's build choice buys the one class it rates best, which is a statement about the AI, not about tier-4 balance: a validation by self-play is only as good as the self-play's willingness to choose. So US-1910 is runnable and not yet meaningful, and it stays that way until the AI's production chooses among classes. That work is recorded on the standing list, in the AI lane.


14. Damage, repair and endurance

14.1 Damage

US-1920 Damage MUST persist across turns (US-420) and MUST be removed only by 11-combat.md's recovery table (CB-2070) as modified by the per-unit repair fields of §14.2.

US-1930 A unit's strength MUST scale its combat power linearly and MUST reduce its resolve, exactly as 11-combat.md specifies (CB-120, CB-470, CB-710). This document MUST NOT state that a worn unit fights at full strength, and MUST NOT define any other automatic effect of strength on combat, movement or vision.

Rationale, and this reverses the previous requirement in full. The old rule said a unit at one hit fights at full strength, and argued for it on the ground that a pure race between hit counts is legible and exactly predictable. Both halves have been overtaken. 11-combat.md's prediction is exact with scaling — the preview is three evaluations of the real function plus three binary searches, and every quantity is monotone in the one draw (CB-2150, CB-2160) — so the legibility argument no longer buys anything. And the scaling is doing load-bearing work the old model had no way to express: a worn unit is not merely closer to death, it is worse at fighting and quicker to break, so attrition compounds locally, fronts wear out, and rotating a battered formation out of the line becomes a decision instead of a formality (CB-120). That compounding is what makes bombardment worth doing at all. The old rule would have made a defender at strength 1 fight exactly as hard as one at 100, which would delete the entire second half of every siege.

14.2 Repair

US-1940 There MUST be exactly three sources of strength recovery: 12-economy.md EC-1730's base rate table, which covers a city with an emplaced Depot, a city, and anywhere else; a unit with repairAdjacent; and a carrier with repairEmbarked. This document MUST NOT define a repair rate, table, gate or step of its own. It owns only what EC-1735 clause 5 gives it: the per-class magnitudes, and the adjacency or carriage condition each is subject to.

US-1950 repairAdjacent MUST add its value to the strength recovery of one adjacent friendly unit at EC-1990 step 3, chosen by the repairing unit's owner or, absent an order, by lowest strength then lowest sequence. The recipient's choice is this document's; the step, the gates and the composition are EC-1735's, and a second provider covering the same recipient adds nothing (EC-1735 clause 3).

Built 2026-09-13. repairAdjacent was in the schema, the roster and the validator and was read by no rule; the Ledger's recovery step had said in its own docblock that the EC-1735 modifiers would "land with the roster content increment", and the roster had landed. Two things are worth recording. The recovery step was restructured to cap once, on the sum: it used to cap inside each branch of EC-1730's table, which is indistinguishable while no modifier exists and wrong the moment one does, because EC-1735 puts the modifier "after the base recovery and before the cap". And a documented reading of the recipient choice: no repair-target order exists in the vocabulary, so the default rule — lowest strength, then lowest sequence — always applies, and it chooses among units that will recover this phase, since a bonus on a unit whose own gates refuse it restores nothing (EC-1735 clause 1) and would spend the Engineer on nothing. Two Engineers choosing the same unit give it the larger single bonus and the second does not re-target, which is this requirement's "a second provider covering the same recipient adds nothing" read literally.

Updated 2026-09-13: the carriage model has landed (the carrier link, the Load and Unload tasks, and the air increment's launch), so US-1960 no longer waits on it. What remains is the rate: its recipients are cargo, an embarked unit has tile: null by invariant, and EC-1730's rates are indexed by tile, so cargo recovers nothing for a modifier to add to. repairEmbarked stays a roster field with no mirror on the unit until the recovery step learns to rate the cargo.

US-1960 repairEmbarked MUST add its value to the strength recovery of every unit carried as cargo, at EC-1990 step 3. EC-1735 clause 3's largest-single-bonus rule composes across providers for one recipient and does not reduce a carrier's reach across its own cargo: a carrier repairing eight aircraft provides eight bonuses, one to each, and competes with any other provider only per aircraft.

Rationale for the asymmetry: an engineer is one team with one set of tools and can be in one place; a carrier is a floating workshop whose entire purpose is servicing the air wing it carries. Making the carrier's repair per-unit rather than one-of is a large part of what a 510-Works capability purchase is buying.

Built 2026-09-15. It had been inert since the field existed. repairEmbarked was in the definition schema, in the closed modifier list of §14, and on the shipped Carrier at 2, and no rule read it — because 12-economy.md EC-1730's rate table is keyed on where a unit stood and cargo stands nowhere, so there was no base recovery for the modifier to add to. Both sites said so in their own comments and each pointed at the other. EC-1730 now rules that cargo is rated at its carrier's tile and takes its carrier's supply, which gives the modifier something to compose with; the per-unit reach this requirement states is implemented as stated, with EC-1735 clause 3 still choosing the largest bonus per recipient and clause 2 still binding the carrier — a carrier that initiated an attack repairs nobody, while its passengers, who attacked nothing, still take the base row.

US-1970 The gate on strength recovery MUST be the one 12-economy.md EC-1730 states and no other: a unit recovers if and only if its supplyState is Supplied at step 1 of the Ledger phase and it did not initiate an attack or a bombardment during that turn's Cascade (10-turn-model.md TM-1380 defines initiation). This document MUST NOT state a gate of its own, and MUST NOT substitute 11-combat.md's broader "fought this turn" flag (CB-1270) for the initiation test — 12-economy.md EC-2290 forbids that substitution by name.

The repairAdjacent and repairEmbarked modifiers are subject to EC-1735's composition rules, which gate both ends: the recipient's own gates apply unchanged and no modifier may restore a unit that fails them, and the provider must itself be Supplied and must not have initiated (EC-1735 clauses 1 and 2).

Ruled 2026-09-13. This requirement previously stated the opposite of a MUST NOT. It fixed the gate as "fought this turn (CB-1270)" — the participation reading — and argued for it at length. 12-economy.md had already adjudicated the question and chosen initiation: EC-1730's rationale opens "the part four documents disagreed about", names all three readings in circulation including this one, and rejects participation with two arguments this document's own rationale did not answer.

The first is involuntariness. Initiation "is a fact about the unit's own activation ... 'did I fight?' is decided by everybody else's activations too — including reactions, which 10-turn-model.md TM-1730 makes non-declinable. Under the participation gate a player who buys a picket by paying six points of initiative for Stance Hold and forfeiting its movement would also forfeit its repair, involuntarily, because an opponent chose to walk past it."

The second is the besieged city. "Under the participation gate an attacker suppresses a garrison's recovery indefinitely by throwing one cheap unit at it every turn, at no risk and for no gain but the suppression — which converts a siege into free attrition and quietly cancels EC-1760's decision that a cut-off city keeps working."

That second case is decisive against the argument this requirement used to make. The old rationale said a line under pressure should not repair itself and that rotation is the answer; the besieged garrison cannot rotate, so under the participation gate the rule it was defending does not produce a decision, it produces free attrition for the attacker. EC-2290 then closed the question mechanically: 11-combat.md must expose an initiation flag, and its "fought this turn" flag "counts defending, supporting and reacting — and MUST NOT be substituted for the initiation test".

The implementation already followed 12-economy.md: economy/ledger.ts gates on initiatedCombatThisCascade, and combat/recovery.ts carries a note that it retains the disorder column only. So the code was right and this requirement was wrong, which is the failure mode a mechanical citation check cannot see: CB-1270 exists, so check-spec-refs passed every time.

US-1980 strength recovery MUST resolve where 12-economy.md places it — at EC-1990 step 3 of the Ledger phase, after EC-1740's attrition at step 2 (EC-2023 fixes that order) — and MUST NOT exceed maxStrength.

Amended 2026-09-13 with US-1970's ruling. Two corrections. The step is EC-1990's, not CB-2070's: EC-2290 states that "the Ledger's step list is this document's and must be the only one", and supersedes CB-2130's opposite ordering of attrition and recovery. And the cap is maxStrength rather than the literal 100, because that is what EC-1730 caps at: its rates are per-mille of maximum strength precisely so that neither it nor this document needs to know 11-combat.md's scale, and a literal 100 here would be a second place the scale is written down. (The per-mille expression is EC-120's convention; the scale-agnosticism is EC-1730's reason for using it, and citing EC-120 for the second of those was this amendment's own first draft getting it wrong.)

US-1990 The per-unit repair modifiers MUST be added to the base recovery 12-economy.md EC-1730 computes for the receiving unit, within EC-1990 step 3, after that base recovery and before the cap, as an integer number of strength points (EC-1735). They are subject to EC-1735's composition rules entire, of which one is a rule this document must not contradict: where two or more providers could act on one recipient, only the largest single bonus is taken, ties breaking by the provider's lowest creation sequence — bonuses MUST NOT sum (EC-1735 clause 3).

At a maxStrength of 100, EC-1730's field rate of 40‰ is +4, so the Engineer's repairAdjacent 1 raises a unit's field recovery to +5 per turn and the Carrier's repairEmbarked 2 raises it to +6 for every aircraft aboard. Those figures are illustrations of EC-1730's rates at one scale and are not a rate this document sets.

Amended 2026-09-13 with US-1970's ruling. The arithmetic survived — EC-1730's 40‰ of 100 is the same +4 CB-2070's superseded strength column carried, because EC-1730's rates "reproduce 11-combat.md CB-2110's calibration exactly" — but three things around it did not: the citation, the gate, and the absence of EC-1735's no-stacking rule, which this requirement's silence would have permitted. EC-1735's own rationale is why that matters: stacking "would make repair a numbers game — park three engineers behind the line and a mauled formation is whole again next phase — which turns rotation from a decision into a logistics chore and defeats the asymmetry EC-1730's rates exist to create."

Rationale, re-derived against 11-combat.md's recovery table rather than against the hit counts that no longer exist. CB-2110 fixes the asymmetry the whole rotation decision rests on: a unit at 40 strength is fifteen turns from full in the field and five in a city. An Engineer takes the field figure from fifteen turns to twelve, and a Carrier takes an air wing from fifteen to ten. Both are real improvements and neither closes the gap, which is the point — if a field repair unit could match a city, cities would lose the second purpose CB-2110 gives them and the front would never need to rotate. The modifiers are additive on the recovery rather than a separate repair pass because a second pass would need its own cap, its own supply rule and its own interaction with fighting, and would be the second place recovery could be got wrong.

14.3 Endurance

US-2000 Aircraft range MUST be modelled as endurance in turns, not as fuel measured in movement points.

Rationale: this was an open question in the design proposal and it is decided here. Turns are integer-clean, need no per-unit stock, and make airbase geography a positional question a player can see on the map — "can I get home from there" — rather than an arithmetic one they must compute. Movement-point fuel models range more honestly on very large maps and is rejected because it converts every sortie into a subtraction problem and adds a per-unit ledger entry to a subsystem that has none. The honest cost is that on the largest supported maps the endurance radius is coarser than reality would be; that is a price worth paying and it is recorded in §17.

US-2010 At the end of every Cascade, an air unit that did not end its activation on a friendly city, a friendly airfield, or a unit with airbase MUST have enduranceRemaining decremented by 1.

US-2020 An air unit whose enduranceRemaining reaches 0 while not at a base MUST be removed from the map at the end of that Cascade.

US-2030 An air unit that ends its activation at a base MUST have enduranceRemaining reset to definition.endurance.

Ruling, 2026-09-11, on building §14.3, and binding. "At a base" means occupying the tile, not being based on it. 01-game-rules.md GR-620 states the same reset in its own vocabulary — "an air unit's endurance MUST be reset when it ends a turn based in a City, in an Airfield installation, or embarked in a unit carrying the airbase capability" — and based is a distinct state there (GR-630: no Air layer, counts against air capacity, destroyed with its base). The two readings differ, and this document's governs, because GR-620 says in as many words that it "owns only the list of things that count as a base" while this section owns the counter and its consequences; US-2010, US-2030 and US-570 say "on", "at" and "aboard" and never say based. The practical half of the argument points the same way: nothing in the engine sets based — it is a declared state with no transition, an 01-game-rules gap recorded here because §14.3 is what made it visible — so reading GR-620's based literally would make every aircraft in the game starve at its own airfield. The three things that count as a base are GR-620's list and are implemented once, at airBaseAt.

Ruling, same date: a class endurance of 0 carries no endurance limit. The unit-set validator puts endurance in [0, 8] and requires 0 of every non-air class; it does not require 1 of an air class, so 0 on an aircraft is legal data. The only reading consistent with the other eighteen classes — where 0 means "this rule is not about me" — is that it imposes no limit, and US-2020 agrees on its own terms: it removes a unit whose counter reaches 0, which is a transition a unit that began there never makes. A class that should run out of fuel carries a positive endurance, as all five shipped air classes do.

US-2040 The client MUST display enduranceRemaining on every air unit at all times, and MUST warn before committing an order that would leave a unit unable to reach any base.

Rationale: losing an aircraft to an arithmetic error the interface could have caught is not difficulty, it is a missing feature. The decision — go deep or come home — stays with the player; the subtraction does not.


15. Custom unit sets

US-2050 A custom unit set MUST be expressible as a single JSON document conforming to §2, using only the flag vocabulary of §3, with no scripting of any kind.

Held 2026-09-23 as packages/core/test/units/content-boundary.test.ts, in three rows because "no scripting" can fail in three different places.

The document itself is walked to every leaf and every leaf is a primitive — a scripting hook has to arrive as some value, and no string, number or boolean executes. But a hook can arrive as a string whose contents are meant to be evaluated, which that row cannot see, so a second names the shapes: no field called formula, expression, onBuild, onDeath, hook or script. The failure mode here is not malice — it is a well-meant field added because one value needed to vary, by somebody who never read this section.

The third row is the one that makes the other two guarantees rather than descriptions: a schema that admits only data is undone by a loader that evaluates it, so nothing under core/src/units contains eval(, new Function, a dynamic import( or a require(.

US-2060 The WASM plugin ABI MUST remain the only escape hatch for behaviour the flag vocabulary cannot express. A unit set MUST NOT be able to reach it.

Held 2026-09-23 on both halves. The schema names no wasm, plugin, abi, dlopen or instantiate, so a unit set has no vocabulary to ask with; and the core holds no plugin host at all — no WebAssembly, no instantiate(, no loader — which is the structural reason the requirement holds, since AR-060 puts every host in the client or the server. That second row is the one worth keeping: it is what a future "just load it here, it is simpler" would break.

Two lessons from the guard itself, both found by mutation and both about matching. The first draft matched substrings and flagged abi inside cap-abi-lity — firing on the very vocabulary the requirement is about, which is how a guard gets deleted. Switching to \b boundaries fixed that and introduced a quieter hole: \b counts _ as a word character, so PLUGIN_FIELD slips straight past \bplugin\b — and a constant named exactly that way is how a plugin route would actually arrive. The boundary is now "not adjacent to a letter", which catches the underscore form and still keeps abi out of capability.

Rationale: the boundary matters. A data set is safe to load from anywhere, is deterministic by construction, and cannot desync a multiplayer game. A plugin is none of those things and belongs behind a separate, explicit trust decision. Blurring the two would mean every shared unit set becomes a code-execution question.

US-2070 The shipped roster MUST use only the vocabulary of §2 and §3.

Rationale: this is the only honest way to prove the vocabulary is sufficient. If the game's own twenty units needed one special case, every modder's unit would need it too and would not have it.

US-2080 A game MUST use exactly one unit set. Mixing sets within a game MUST be rejected at setup.

US-2090 The unit set identifier and a content hash of its definitions MUST be included in the canonical game hash, so that two clients running different versions of a set desync immediately and loudly rather than silently diverging.

US-2100 A saved game or replay referencing a unit set that is absent or whose content hash differs MUST refuse to load, naming the set and both hashes.

Built 2026-09-13, with US-2090. The unit set was in neither the game hash nor the save file. stateToCanonical carried a gameId, a rulesVersion, the setup, every unit and the whole world, and nothing about the definitions those units were built from; the save container's manifest carried five version fields and no unit set; standard.json has a setId and a schemaVersion and nothing recorded either. The canonical state now carries unitSet — the identifier, the schema version, and a content hash of the definitions — beside gameId and rulesVersion, which is the category it belongs to: what game this is rather than what is in it.

Why it matters with only one playable set, since that is the obvious objection: the standard set's own content changes between builds. Two builds that disagree about one unit's stat line hash identically until the first moment that stat affects the state, which may be turn forty or never — precisely US-2090's "silently diverging". Four stat fields were read by the engine for the first time on the day this landed.

US-2100's refusal is before the replay, in the CLI's openGame and the client's restore, and names the set and both hashes; before this the only symptom was a generic hash mismatch after a complete replay, which named neither. "Absent" is read two ways and both refuse: a save naming a set this build does not have, and a save naming no set at all because it predates the field. The check is deliberately not in parseContainer or verifyIntegrity, because the re-emission script uses exactly those two to bring committed evidence forward.

Recorded rather than built: US-2080 is vacuously met. "A game MUST use exactly one unit set; mixing sets MUST be rejected at setup" — every rule reads STANDARD_UNIT_SET directly, so a custom set can be loaded and validated by loadUnitSet but can never reach a game. Making custom sets playable means threading a set through every consumer, and it is a larger increment than this one.

US-2110 The encyclopedia (US-930) MUST be generated from the loaded unit set, not authored, so that a custom set is documented in-game to the same standard as the shipped one.


16. Contracts on other documents

Obligations this specification places on subsystems it does not own, recorded so that a change on either side is detectable.

US-2120 All prior US-NNN citations are stale. 02-unit-sets-content.md is void, and every requirement identifier it defined has been discarded. Sixty-eight distinct US-NNN identifiers are cited from 00-overview.md, 01-game-rules.md, 03-architecture.md, 04-ui-ux.md and 07-modding-content.md; none of them resolves to a requirement in this document, and the numbers have been reused for unrelated content. Every such citation MUST be re-resolved against this document before those files are considered current. The identifiers cited include US-020, US-050, US-055, US-057, US-060, US-070, US-073, US-075, US-077, US-140, US-150, US-180, US-190, US-200, US-220, US-227, US-390, US-400, US-420, US-440, US-450, US-455, US-460, US-465, US-475, US-490 and US-520.

Discharged 2026-09-11, and this requirement was wrong about its own subject. Measured across the five documents it names: 101 distinct US-NNN identifiers are cited and all 101 resolve against this document — 00-overview.md 4, 01-game-rules.md 6, 03-architecture.md 17, 04-ui-ux.md 36, 07-modding-content.md 63, with zero stale. The re-resolution this requirement demands has been done, incrementally, by the slices that rewrote those documents. Why it survived a green gate is worth recording: check-spec-refs.mjs enforces OV-070 by treating a paragraph that declares identifiers "retired", "stale", "void" or "discarded" as making declarations rather than dangling references — and this requirement's own wording trips that exemption, so the paragraph asserting the citations are broken is exactly what excused them from being checked. The measurement is now held at core/test/units/contracts-16.test.ts, which counts resolution rather than trusting the claim.

US-2130 12-economy.md MUST provide: Works generation per city before the Industry multiplier of US-840; the Manpower pool, levy, and the Awaiting Levy state referenced by US-1280; city classes and their base Works, matching the reference producer of US-870 or stating the integer factor by which §8's costs must scale; the city trait vocabulary referenced by requiresTraits, including shipworks; Integration, referenced by minIntegration; supply states, referenced by US-1970; garrison requirements, satisfied by the Militia of US-1420; the Administrative Cadre's Integration rate and cost-escalation basis; the Forward Depot's Reach; and a Ledger step at which the repairAdjacent and repairEmbarked modifiers of §14.2 are applied to 11-combat.md's recovery. It MUST apply class-scoped trait discounts after this document's escalation, per US-190. It MUST NOT define unit stat lines for the Militia, Administrative Cadre or Forward Depot; those are owned by §8.2.

It MUST additionally adopt three things this document governs and MUST NOT restate them with different values: the Industry multipliers of US-820 (US-825), the Works-to-Manpower bands of US-1810, and the absence of any independent repair rate (US-1940, CB-2780). Its Industry table at 1000/1500/2100/2800 and every worked example computed from it are stale as printed and MUST be recomputed against US-820's 1000/1750/2750/4000, not merely deprecated. Its worked vectors MUST reproduce from the shipped costs of §8; where they were computed from other anchors — a line infantry at 40 Works against this roster's 60, or an Administrative Cadre at a third of its shipped price — the vectors, not the roster, MUST move. It MUST gate capital hulls through requiresTraits {shipworks} (US-1610) and MUST NOT introduce a heavyHull capability flag; US-470 closes the flag vocabulary and a second data hook for one rule is one hook too many.

US-2140 11-combat.md MUST provide: the Clash procedure the roster of §8 is derived against (CB §5); the common scale and reference value for atk, def and tough (CB-150); the terrain, city, dig-in and Stance modifiers (CB-600, CB-1450, CB-1460, CB-1520, CB-1550); the amphibious attacker term that assaultLanding uses (CB-550); the disorder that bombardment applies (CB-1720); the attackerEdgeMods channel through which siege is expressed (CB-1740, US-615); the flak shock term of US-260 (CB-965); the intensityBack = 0 treatment of ambush (US-630); the four-value grade derivation from xp (CB-1600); the strength and disorder recovery table the repair modifiers of §14.2 add to (CB-2070); and the exact win predictor required by US-1730 (CB-2200).

The following obligations this document previously placed on 11-combat.md are withdrawn, because that document forbids what they asked for:

Withdrawn obligation Why, and where the intent went
A per-round resolution procedure CB-020 forbids any loop. A Clash is one exchange; §8's values are derived against that (US-1715).
A Suppressed condition applied by bombardment CB-140 forbids a separate suppression state. Bombardment applies disorder (CB-1720), which subtracts from resolve point for point (CB-710).
A flak exchange resolving before the attacker's strike No ordered sub-steps exist. flak is a shock term on the air attacker (US-260).
A rule that strikeRun does not bound air-to-air engagements There is no strikeRun (US-270). Air classes carry flak 0, so air-to-air gets no shock term and is decided on the exchange alone — the same asymmetry, with no rule.
Multiplicative entrenchment, city and terrain defence bonuses CB-540 makes every modifier additive per-mille.
The treatment of submerged and sonar in resolution These are observation, not combat; 01-game-rules.md §8–§10 owns them, and their combat consequence is that an unobserved attacker gets ambush.

US-2145 11-combat.md MUST NOT define a per-unit hit-point maximum, a suppression state, per-domain attack values, or a round limit, and this document MUST NOT ask it to. Where the two documents disagree about combat, 11-combat.md governs and this document is re-derived (US-1720).

Note, 2026-09-11, on where each of the four prohibitions actually lives, because a "MUST NOT" placed on another document is only as good as that document's own rule. Round limit: CB-020 — "A Clash MUST resolve as a single exchange. The engine MUST NOT loop, iterate rounds, sample repeatedly, or run any procedure whose step count depends on the units involved." Suppression state: CB-140 — "MUST NOT define a separate 'suppressed' or 'pinned' state", disorder being the sole representation; CB-1010 uses "suppress" as a verb for a break suppressing a falter, which is a rule and not a state. Per-unit hit-point maximum: CB-110's single 0–100 strength scale for every unit type, with CB-150's note that this document "gets tough instead, which does that job better". Per-domain attack values: carried by engages and flak instead (CB-235, CB-965), whose own rationale says those two fields are what "a per-domain attack vector used to carry". All four are now held mechanically rather than by reading: CB-300 closes the combatant record at exactly fourteen fields and CB-320's canonical form enumerates them, so a fifteenth field — a suppression flag, a hit-point maximum, a second atk — cannot be added without breaking core/test/units/contracts-16.test.ts.

US-2150 10-turn-model.md MUST provide: the Initiative formula consuming initiativeBase as the class base term, with creation sequence as the tiebreak; the global creation sequence counter referenced by US-410 and US-1310; Forming Up, which applies to every unit produced under §7; the entrenchment counter, its range and the conditions that increment and reset it (TM-700), which this document gates only through the entrench capability flag (US-510) and which 11-combat.md reads but MUST NOT redefine (CB-1560); and the guarantee that a unit with move = 0 still occupies an activation slot. It MUST read a unit's veterancy grade as 11-combat.md derives it from xp (CB-170, CB-1600); no grade is stored on the instance (US-440). It MUST provide the narrowed ordering guarantee of TM-945, which is what 10-turn-model.md can actually hold: two units whose every other active initiative term is equal activate in descending initiativeBase order, ties broken by ascending creation sequence; and a base gap strictly greater than the tier swing (28 at Tier 1, 42 at Tier 2, 56 at Tier 3) cannot be inverted by any combination of the other terms at that tier. Nothing stronger holds.

Amended 2026-09-11. This requirement previously asked for the unnarrowed form — "a unit with a higher initiativeBase activates earlier, so that fire support and lift precede assault" — and 10-turn-model.md has explicitly refused it in writing: TM-946's rationale names this requirement and says the guarantee "is false as stated and cannot be made true", because Stance alone spans 20 points and inverting low-initiative units is the point of TM-1310. That document owns activation order, so the narrowed form governs and this one is re-derived. §17 item 15 anticipated this and is updated.

The consequence is concrete and was measured rather than argued. Against the shipped bases, the Landing Ship at 56 is behind three of its own admissible cargo classes — Scout 90, Field Gun 74, Heavy Artillery 72 — which under TM-945 guarantee 1 activate before the carrier, and is within the Tier 1 swing of Armour (50), Heavy Armour (46) and Infantry (32), so no tier guarantees the ordering for six of the ten land classes a Landing Ship can carry. The ordering US-560 relies on therefore does not come from initiative at all. It comes from TM-1140, which puts in the activation roster "exactly those units that ... are not embarked in a transport": embarked cargo is absent from the roster and cannot act before its carrier under any initiative values whatever, which is the free and sanctioned answer TM-946 points to. US-560's justification is corrected accordingly.

US-2160 01-game-rules.md MUST provide: the terrain movement-cost table indexed by terrainClass, with foot, wheeled and tracked differing in at least three terrain types per US-170; water depth classes indexed by draft; the coastal test referenced by requiresCoastal; tile stacking limits, against which US-320 exempts cargo; the effects of road, airfield, fort and bridge constructions named in buildables; the fort presence referenced by US-1100; the fog and knowledge-view rules referenced by US-640 and US-970; and the observation fidelity ladder of GR-1260 to GR-1280, whose recon observer modifier reads the class enumeration value of US-090 and MUST NOT be changed to read a capability flag (US-470).

Status recorded 2026-09-11: satisfied as written, every clause checked against the engine. The movement-cost table is indexed by terrainClass and foot/wheeled/tracked differ in more than three terrain types; water depth is indexed by draft at the occupancy gate (GR-590); requiresCoastal has its coastal test; cargo is exempt from stacking (GR-650); the fort presence of US-1100 is read by GR-380's ×1400/1000 multiplier; the fog and fidelity rules of US-640, US-970 and GR-1260–GR-1280 exist, with the recon modifier reading the class enumeration and not a flag. And the four construction effects this requirement asks for are all present and all read their installation: road through the road move cost, fort through the defence multiplier, airfield through the air-base test of GR-620, and bridge through the edge-crossing surcharge and river multiplier — the last being an edge feature rather than a tile installation, which is why buildables spans two kinds of thing.

One boundary is worth stating precisely rather than quietly widening this requirement. Nothing can build any of them: there is no order in the command set that constructs an installation, so canBuildOn is reachable only from its own tests and the Engineer's buildables is read by no rule (recorded under US-1500). That is a gap in 13-command.md's order set, not a failure of this requirement, which asks for the effects and gets them. It is recorded here because §16 exists so that a change on either side is detectable, and a reader of this requirement should not conclude the constructions are usable.

US-2170 13-command.md MUST surface the ProductionStalled condition of US-1060 and the ProductionOverflow condition of US-1240 through the Dispatch. Its Requisition rows (CM-1240) and Doctrine defaults (CM-790) MUST key on the class enumeration of US-090. Its obligation that in-progress production is never cancelled (CM-1270) is satisfied by US-1210. Its request for movement allowance, movement class, vision radius, current and maximum strength, city occupancy and an optional command rating (CM-2170) is satisfied by move, terrainClass, vision, 11-combat.md's strength against the constant maximum of 100 (CB-110), mayOccupyCity and commandRating. Its Replenish Reflex has no fuel or ammunition thresholds to read, because this design has neither; the nearest analogue is enduranceRemaining for air units under §14.3, and CM-2180's request MUST be re-resolved against that.

US-2180 14-victory.md MUST decide whether Industry level, unit classes built, or possession of specific unit types are legal victory-condition terms. "First to build a Carrier" and "hold three Arsenals" are natural objectives that sit exactly on the seam between that document and this one.

*Decided 2026-09-11, from 14-victory.md's own admission test rather than by preference, and recorded as binding. A term is admissible iff it can be expressed in the Track fields of VC-350, and the binding field is sites(p, t) — "ordered set of city ids", because VC-2280 publishes a Track's sites as Bellwethers the moment a player enters the Alarm band, and VC-2290 grants every player live vision of them. That is the instrument which makes a nearly-won game fightable instead of a formality with a countdown, so a Track whose sites is empty raises an Alarm naming nothing to attack.*

So: Industry level is admissible — it is per-city, so sites is the cities at or above the level; it is monotone in the shipped engine, where the only write to industryLevel is an increment, so its kind is Accumulative and VC-370's maxGainPerSeal is bounded at setup by the city count under the one-completion-per-turn rule of US-1290. "Unit classes built" and "possession of specific unit types" are NOT admissible as stated, because neither is constituted by cities and both would publish an empty Bellwether set. Either may be admitted in a form that names cities — "hold three Arsenals" is exactly that, an Industry-level Track by another name, and is admissible; "first to build a Carrier" is not, and belongs in the Warrant and Tally instruments of §15 rather than the victory catalogue. Recorded here because 14-victory.md owns the catalogue and this document owns the terms it would read.

US-2190 04-ui-ux.md MUST own the presentation of the unlock card (US-950), the contact briefing (US-960), the encyclopedia (US-930), the greyed-build explanation (US-370), the endurance warning (US-2040), the win-probability display (US-1730), and the legible rendering of name and roleText required by US-076 — including the elision rule and the requirement that the full string remain reachable on the same screen. It MUST also present a city's whole production queue at once rather than as a scrolling list (UX-930), which is the legibility half of the bound US-1150 sets; where that surface cannot hold the bound, US-1150's value MUST be lowered rather than the queue made scrollable.

Partially held 2026-09-23: the core side only, and the id is deliberately left uncovered. This is an ownership claim on another document, and it has two halves. The half this package can hold is the negative one — the core owns none of the presentation assigned here, checked by scanning core/src for the unlock card, the contact briefing, the encyclopedia entry and the greyed-build explanation — and that row lives in content-boundary.test.ts. The positive half, that 04-ui-ux.md does own each of them, belongs to that document’s own sections and is where the coverage should be claimed from.

So the id is named in the test file under the coverage gate’s opt-out marker (00-overview.md OV-090 documents it, and is deliberately the only document in the set that names it) rather than counted. An ownership requirement covered from the side that is merely not violating it would report as met while the surface it assigns might not exist at all — which is the failure mode this repository already met once, when a section jumped to full coverage on the strength of three requirements nobody could satisfy.

US-2195 07-modding-content.md MUST restate US-070's name and roleText bounds in the unit US-076 fixes and MUST NOT present them as legibility rules; MOD-210 currently restates them as "characters". It MUST additionally record the rename of the prose role field to roleText, which every restatement of the schema must follow. Where MOD-210's name is a localization key rather than a literal, US-076's legibility obligation applies to the resolved string in each shipped locale, and US-070's bound applies to the authored value the loader sees.

*Confirmed unfixed 2026-09-11, measured against 07-modding-content.md as it stands. MOD-210's identity table still names the field role, still states the two bounds as "1–32 characters" and "1–160 characters", and the string roleText does not appear anywhere in that document. Both halves of this requirement are therefore outstanding: the rename is unrecorded, and the bounds are still presented in "characters" where US-076 requires UTF-16 code units and makes them loader input guards rather than legibility rules. One thing it gets right and should not be "corrected": MOD-210 cites US-1620 for the role field, and US-070 cites US-1620 for the same thing — the single-sentence constraint — so that citation resolves and points at the right requirement. The fix belongs in 07-modding-content.md; this document's side is already correct.*

US-2200 03-architecture.md MUST include the unit-set content hash in the canonical game hash per US-2090, and MUST enforce the no-branching-on-id lint of US-060.

Held 2026-09-23, and both clauses turned out to be genuinely checkable rather than merely assertable.

The content hash is read out of the canonical object itself rather than from the place it is computed, because the guarantee US-2090 wants — "two builds that disagree about a unit’s stat line desync at turn zero rather than at the first moment the stat matters" — only holds if the digest is in the hashed form. Removing it from stateToCanonical is a killed mutant.

The lint clause is held on the part most likely to rot, which is not whether the lint exists. A guard that is present and never runs is worse than none, because the requirement reads as satisfied while nothing checks it. So the rows assert that scripts/check-unit-id-branching.mjs exists, that pnpm verify calls it, and that it runs before the test suite — which is the difference between a gate people keep and one they route around when it costs six minutes to learn they broke it. Dropping it from verify, and moving it to the end, are both killed mutants.


17. What could go wrong

Metrics with target bands. Deterministic simulation makes AI self-play cheap; a nightly self-play tournament should emit all of these.

US-2210 The following telemetry MUST be emitted by self-play runs and MUST be treated as release gates.

Failure Metric Alarm
Ladder too expensive; tiers 3–4 are dead content Distribution of maximum Industry reached at game end >70% of Standard games end at max Industry ≤ 2
Ladder too cheap; tempo play non-viable Median turn of first Workshop upgrade Earlier than turn 10
Tech snowball Win rate of a scripted "rush industry" opening against "rush units" at equal AI skill Outside 45–55%
Dead units Share of Works spent on each unit within the window it is available Any unit below 3%
Dominant units Same metric Any unit above 30%
Scout griefing Share of city captures made by units with tough ≤ 60 Above 15%
Submarine window Naval Works loss ratio in the band where subs exist but sonar is rare Attacker-favourable beyond 3:1
Combined arms not landing Share of successful city assaults preceded by bombardment in the same turn Below 50%
Bombard mandatory rather than valuable Share of city captures where the defender's resolve was at the clamp floor of 5 before the assault Above 60%
Build-time band broken Turns-to-build for every unit at its unlock tier Outside 3–11
Capture degradation too punitive Share of captured cities whose Industry is rebuilt within 40 turns Below 25%
Works discarded by the Yard cap Share of city-turns raising ProductionOverflow Above 5%
Seniority penalty on tech Win rate of high-tier armies controlling for total Works Meaningfully below parity

Emitted 2026-09-24 (Claude Code), five of the thirteen rows, by scripts/full-game.mjs from one played game: dead and dominant units (Works paid for units built, over the classes whose minIndustry some owned city reached — coastal and trait requirements are not in the denominator), Scout griefing (a capture counts when every unit of the new owner standing in the city after the close has tough ≤ 60, because no event names the captor), the Yard cap (ProductionOverflow over owned city-turns) and maximum Industry at the end by seat. The other eight need a scripted opening, a matched-army comparison or per-assault records the engine does not emit yet. Not yet release gates: nothing fails a build on them, and the first readings (US-1910's note) measure the AI's build policy before they measure the roster.

US-2220 Two failures that no metric will catch MUST be watched in human playtest: whether the arrival of a new unit class is noticed and understood, and whether the per-city "unit or works?" question becomes tedious rather than interesting once an empire passes about a dozen cities. no-test-needed: an observation duty over human playtesters — “watch whether” binds people, not code. No executable behaviour exists to pin before the first full playtest happens; the row is discharged by watching, in US-2210’s own framing that these are exactly the two failures no metric will catch.

US-2230 The known hard edges of this design MUST be recorded rather than hidden, and MUST be revisited after the first full playtest: a veteran can out-tech a newcomer inside one game, mitigated only by a per-player cap someone has to remember to set; an Industry-2 army facing Heavy Bombers has exactly one answer; tier 4 will be under-tested because most games end before it; and capture-degrades-Industry is correct design and an unpopular moment. no-test-needed: the recording half is carried in the row itself — the four hard edges are stated here rather than hidden. What remains is “revisited after the first full playtest”, a post-playtest process decision, not an executable behaviour; and the per-player cap it cites as mitigation belongs to wherever tech caps are set, not to this row, which owns only the record and the revisit duty.


Open questions

  1. Should the Yard be per-city or per-order? A single per-city Yard makes cancellation free (US-1200) and the rules short, but it also means a player can accumulate against a Cruiser and, on the turn before completion, reorder the queue and spend the whole Yard on something else. That is arguably fine — the Works were always fungible — but it removes any commitment from a long build, and a per-order Yard would restore it at the cost of a refund rule, a partial-progress display, and an argument about what cancelling returns.

  2. Is one completion per city per turn too blunt at the top end? US-1290 does important work, but a Metropolis Arsenal with 12-economy.md's Works Expansion could produce 90+ Works a turn and be structurally unable to spend more than 51 of it on a Cruiser. The alternative — allowing multiple completions but forbidding two of the same unit — is uglier and may be more correct.

  3. Should the Industry multiplier be multiplicative on Works, or flat? Multiplicative concentrates industry in Metropolises and produces the industrial geography this document argues for (US-880). It also means a Town will rationally never reach Arsenal, so a player whose map gave them nine Towns and no City has a structurally worse game through no decision of their own. A flat +4 / +9 / +15 per level would equalise that and delete the geography. This is the decision most likely to be reversed.

  4. Is Industry memory (US-1000) too generous to the defender? Combined with 12-economy.md's Loyalty memory, a defender who retakes their own city gets full Integration, zero Unrest, and undamaged Industry — three restorations from one event. Each is individually justified and together they may make homeland recapture so strong that a contested border city is never worth taking at all.

  5. Is wheeled artillery a good idea or a trap? Making Field Gun, Heavy Artillery and Air Defence Battery road-dependent gives Engineers a purpose and makes artillery genuinely vulnerable in rough country. On a forested or mountainous map it may simply delete artillery from the game, which would take combined arms with it.

  6. Does bombardment need a consumable? Unlimited artillery fire may be too cheap. Ammunition tracking is exactly the fiddliness the direction brief warns against, and 12-economy.md rejected a third resource on the same grounds — but "the guns fire every turn forever" is the most likely place for the combat model to be exploited.

  7. Is tier 3 as the capability tier a real purchase or a trap? US-1800 states plainly that no tier-3 unit reaches its power ceiling. The Foundry pays for itself in Works alone in 15 turns, so the arithmetic holds — but a player who buys it and then finds their new units all lose fights per Works spent may reasonably conclude they were mis-sold, and the answer is a presentation problem this document cannot solve.

  8. Where does the boundary between class and unit identity sit for AI evaluation? 13-command.md's Requisitions key on class, which works. An AI evaluating whether to build a Submarine or a Bomber is making a decision class cannot express, and it must not make it by branching on id (US-060). The likely answer is a small set of scored capability tags derived from flags, and nobody has designed it.

  9. Should endurance scale with map size? US-2000 decides turns over movement-point fuel and accepts that the radius is coarse on the largest maps. A per-map endurance multiplier would fix it with one integer and would also be one more setup parameter nobody understands.

  10. Does the Missile survive contact with the victory system? A 120-Works, 2-Manpower, no-risk deletion of any undefended high-value target is exactly the tool that trivialises an objective-holding victory condition, and 14-victory.md has not yet been written against it.

  11. The Fighter lost a capability, not just a number. engages {air} (US-1460) means a Fighter may not attack a ground or sea target at all, where the previous roster gave it a deliberately feeble atkLand 3. The prose said fighters "do not win ground battles", and under a single atk the only faithful reading of that sentence is a prohibition — but a prohibition is a stronger statement than the sentence made, and no counter relationship in US-1640 depended on the old value. It may prove that a player wants a fighter to be able to strafe a column at bad odds, in which case the fix is to add land to its engages and lower its atk, which the single-atk schema cannot express without also weakening it in the air. That is a real limitation of the schema and it is recorded rather than hidden.

  12. The naval and air values are derived, not calibrated. 11-combat.md's own open question 5 says the assault intensity of 60 has not been calibrated for air or naval engagements, and the ladder of US-1715 was applied to those classes anyway, because a roster with holes in it is worse than a roster with provisional numbers. Every ship-versus-ship and aircraft-versus-aircraft figure in §8 should be treated as first-draft until that document sets its intensities.

  13. The def : atk job ratios of US-1715 are a judgement with four data points. The seven ratios were fitted to the four classes 11-combat.md's golden vectors fix and then extended by analogy. The "a hull fights about as well as it is fought" ratio of 0.80 in particular is asserted rather than derived, and it sets the entire shape of naval combat.

  14. The Missile sits exactly on 11-combat.md's tough floor. tough 20 is the minimum CB-150 permits, and that requirement's own rationale says a class at the floor "cannot take the field" — which is true of a missile and is why it is there. It does mean the shipped roster leaves no room below it, so a custom set wanting anything more fragile has nowhere to go.

  15. initiativeBase — both halves of this are now resolved (2026-09-11), and the entry is kept because the resolution is the interesting part. This item recorded that twelve of the twenty-three values fell outside the [24, 64] band TM-930 then required, and suspected the band rather than the values "since a band that two-thirds of the roster violates was plainly not derived from the roster it governs". That is exactly what happened: TM-930 now requires [8, 92] with five named commitment bands, and TM-935 clause 1 requires that range to contain the whole span of the shipped roster. All twenty-three shipped values are inside it, each in exactly one band — Opening 72–92 (Scout 90 down to Heavy Artillery 72), Positioning 52–71, Exploitation 38–51, Assault 20–37, Static 8–19 — and the check is now mechanical (core/test/units/contracts-16.test.ts). The second half, US-2150's request that a higher initiativeBase always activate earlier, was equally right to doubt: 10-turn-model.md refused it in TM-946 and offered TM-945's narrowed, checkable form, which US-2150 now adopts. The one casualty was US-560, which had been resting on the refused guarantee; it now rests on TM-1140's roster exclusion, which is stronger.


Built from source 47fa2827eff0 · VERSION.json