11-combat.md · 283 requirements

11 — Combat

This document is the normative specification of combat resolution in Every Last City: the Clash — a single exchange between two forces that produces losses and, independently, a decision about whether the ground changes hands — together with bombardment, reactive fire, disengagement, retreat and rout, encirclement, the persistent strength and disorder a unit carries between turns, post-combat recovery, and the pre-commitment odds readout. It owns every number that decides who loses what and who gives ground. It does not own the map, terrain vocabulary, movement, stacking capacity, fog, or the unit roster; it states precisely what it requires of each.

Status: Draft v0.2 · Owner: unassigned · Depends on: docs/design/00-direction.md, docs/design/01-decision-turn-model.md, docs/design/combat.md, 01-game-rules.md (map, terrain, fog, adjacency, stacking, city ownership), 02-units-and-industry.md (unit combat stats, roles, bombard parameters, retreat class), 10-turn-model.md (Initiative, activation order, Stance, reaction capacity, creation sequence), 12-economy.md (supply state, depots), 13-command.md (Postures, Sanctions, the win-probability contract CM-150/CM-160), 03-architecture.md (determinism, integer arithmetic, seeded PRNG, canonical hashing), 04-ui-ux.md (presentation).

Naming note, binding on this document. Stance always means the per-turn tactical setting owned by 10-turn-model.md with values March, Advance, Hold, Entrench. Posture always means the standing job owned by 13-command.md (CM-330). The source design proposal used the word posture for the tactical setting and named a fourth value Withdraw; that value is expressed here as the Disengagement procedure of §12, driven by the Withdraw Reflex of CM-1400, and is not a Stance.


1. Scope, vocabulary, and what this document owns

CB-010 A Clash MUST be the only mechanism by which one unit reduces another unit's strength as a direct consequence of an attack, a bombardment, or a reactive engagement. Strength loss from supply attrition, scuttling, or scripted scenario effects is owned by the specification that inflicts it and MUST NOT reuse the Clash procedure.

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.

Rationale: this is the load-bearing decision of the whole document. A single exchange is what makes the outcome distribution closed-form, which is what makes the pre-commitment readout exact, which is what makes committing orders before seeing them resolve (docs/design/01-decision-turn-model.md) a skill rather than a gamble. It is also what makes worst-case turn time flat regardless of army size.

CB-030 A Clash MUST produce two independent results: a loss result (integer strength removed from each side) and a break result (whether either side loses its nerve). The two MUST be computed from different inputs — losses from the odds ratio, breaks from shock against resolve — and MUST be permitted to disagree.

Rationale: separating "how much did it cost" from "did the line move" is what gives combat texture without adding dice. You can bleed an enemy badly without shifting them, and shift them without killing them, and those are different plans requiring different forces. A system where damage and displacement are the same number is a system where the only question is "do I have more?".

CB-040 Combat MUST NOT be fight-to-the-death. Most Clashes MUST be capable of ending with both units alive. Destruction MUST occur only when (a) a unit's strength reaches 0, or (b) a unit breaks and has no legal retreat destination.

Rationale: retreat is what creates a front — a line that can be pushed, bent and broken through, where taking ground and destroying an enemy are different achievements. If every engagement ends with a corpse the map is a sequence of duels and positioning collapses into arithmetic.

CB-050 Encirclement MUST NOT be a special case. It MUST emerge entirely from the conjunction of CB-040(b) and the retreat-destination rules of §6, with no rule that names "surrounded", "pocket", or "cut off" as a trigger for a distinct code path.

Rationale: two individually trivial rules — "a broken unit retreats" and "a unit that cannot retreat is destroyed" — produce fronts, pockets, breakthroughs, and the entire operational layer for free. The player achieves encirclement by taking away the roads, not by rolling well, and the engine needs no surrounded-unit logic at all.

CB-060 Every quantity in this document MUST be an integer. Fractional quantities MUST be expressed per-mille. Floating-point arithmetic MUST NOT appear anywhere in combat resolution or combat prediction. Q16.16 fixed point MUST NOT be used by this subsystem.

Rationale: the accumulate-wide-divide-once discipline of §5 means every named quantity is produced by exactly one division, so there is no compounding rounding to reason about and no fixed-point representation is needed at all. Using Q16.16 here would add a representation without adding precision and would give a second way to be wrong.

CB-070 Every division in this document MUST be floor division toward negative infinity. Truncation toward zero MUST NOT be used. Implementations in languages whose integer division truncates toward zero MUST implement an explicit floor division helper and use it everywhere.

Rationale: this is not a style preference. The volatility swing s is negative for draws below 500, and floor(−102.9) = −103 while trunc(−102.9) = −102. That one-unit difference changes break probabilities by whole per-mille and would make two conforming implementations disagree, which CB-2500 forbids. It is the single most likely source of a cross-platform desync in this subsystem.

CB-080 This document MUST own: the Clash procedure and all its constants; the terrain combat profile schema and its default values; the effect of Stance, grade, supply, works and dig-in on combat; retreat destination selection; recovery of strength and disorder; and the prediction function of §16. It MUST NOT define terrain types, movement costs, stacking capacity, fog rules, supply propagation, unit roster values, or city ownership transfer.

CB-090 The terms below are defined once and used throughout with exactly these meanings.

Term Meaning
Clash One resolution of the procedure in §5, of one of the kinds in §3
Lead attacker The unit whose order initiated the Clash; the only attacker that may advance
Supporter A friendly unit adjacent to the target tile that contributes reduced power
Attacking force Lead attacker plus supporters
Defender The single unit of the target stack that contributes defensive power
Defending stack Every unit on the target tile, defender included
Flankers Friendly units of the attacking side adjacent to the target tile, excluding the lead attacker
Break A side's shock reaching its resolve; for the defender this means giving ground
Falter An attacker break; the attack is thrown back
Rout A break with no legal retreat destination, which destroys the broken stack
Shock The pressure applied to a side's nerve: losses taken plus positional terms
Resolve The integer a side's shock must reach for it to break
Draw The single integer r ∈ [0, 1000) that is the only random input to a Clash

2. What a unit carries

CB-100 Every unit capable of combat MUST carry the following mutable per-unit combat state, which MUST persist across turns and MUST be part of authoritative game state.

Field Type Range Initial Meaning
strength integer 0–100 100 Proportion of the unit remaining; scales its power linearly
disorder integer 0–100 0 Accumulated shock; subtracts point-for-point from resolve
xp integer 0–999 0 Drives grade (CB-1600)
digIn integer 0–4 0 Complete turns spent stationary in Stance Entrench
shaken integer 0–2 0 Turns remaining of the post-break penalty of CB-1130

CB-110 strength MUST be expressed on the same 0–100 scale for every unit type, so that "this unit is at 60" means the same thing for every unit in the game.

Rationale: a normalised scale is what lets one bar in the UI mean one thing, lets tough express "hard to grind down" independently of "hard to hurt", and lets a player compare a rifle company and an armoured battalion without a lookup. It costs the roster designer the ability to express unit size as hit points; 02-units-and-industry.md gets tough instead, which does that job better.

CB-120 A unit's power contribution MUST scale linearly with strength (CB-470). A unit's resolve MUST fall by floor((100 − strength) / 2) (CB-710). No other automatic effect of strength on combat MAY be added without a combatLogicVersion bump (CB-2530).

Rationale: the two condition penalties compound deliberately. A unit at 60 strength with 30 disorder has lost 50 resolve before anything else is counted, so worn units break sooner and attrition snowballs locally. That is what makes a front wear out, what makes rotation a genuine decision, and what makes bombardment worth doing.

CB-130 strength MUST be clamped to [0, 100] and disorder to [0, 100] after every modification. A unit whose strength reaches 0 as a result of a Clash MUST be destroyed and removed immediately, before the next unit activates.

CB-140 This document MUST NOT define a separate "suppressed" or "pinned" state. disorder is the sole representation of a unit having been shaken, and every effect that a suppression flag would have carried MUST be expressed through disorder.

Rationale: one number instead of two states, with a continuous gradient instead of a cliff, and it recovers on a schedule the player can plan around. A boolean suppression flag would additionally need its own clear condition, its own display, and its own interaction with every other modifier.

CB-150 The static per-class combat stats this document consumes MUST be exactly: atk, def, tough, role, retreatClass, bombardRange, bombardShots. Their values are owned by 02-units-and-industry.md.

Stat Type Meaning
atk integer 0–255 Offensive base; 0 means the class may never initiate a Clash
def integer 0–255 Defensive base
tough integer 20–255 Divides incoming losses; 100 is the reference
role enum Foot | Armor | Gun | Recon | Naval | Air
retreatClass enum Ground | Detached (CB-1330)
bombardRange integer 0–4 0 means the class may not bombard
bombardShots integer 0–4 Bombardments permitted per turn

CB-160 tough MUST divide incoming losses and MUST NOT affect power, resolve, or volatility.

Rationale: it is the roster's lever for "hard to grind down" as distinct from "hard to hurt". A scout troop and an armoured battalion should not both lose 30 points in an even fight, and expressing that through def would also make the scout harder to displace, which is wrong.

CB-170 A unit's Grade MUST be derived from xp by CB-1600 and MUST NOT be stored independently.

CB-180 strength, disorder, xp, digIn and shaken MUST contribute to the canonical state hash defined by 03-architecture.md.

CB-190 The client MUST be able to render strength and disorder as a single composite condition badge (04-ui-ux.md owns the visual). This specification's requirement is that the two numbers are always available together and always mean the same thing; a UI that exposes them as two raw spreadsheet columns MUST be treated as failing CB-2410.

Rationale: two persistent numbers per unit is real bookkeeping, and at three hundred units it is the thing most likely to make this design worse than what it replaces. The composite badge is a promise the interface has to keep for the mechanical design to be worth its cost.


3. Kinds of engagement

CB-200 The engine MUST recognise exactly four kinds of engagement, and every strength loss caused by combat MUST arise from one of them.

Kind Initiated by Intensity out / back Break checks Advance possible
Assault An Attack order or a sanctioned automated attack 60 / 60 both sides yes
Bombardment A Bombard order, at range 22 / 0 neither side no
Reaction The turn model's reaction rule, out of activation 34 / 14 both sides no
Disengagement An attack landing on a unit under a Withdraw Reflex none — no draw none yes, unopposed

CB-210 Assault, Bombardment and Reaction MUST all resolve through the identical procedure of §5, differing only in the constants named in CB-200 and in the specific requirements of §10 and §11. A separate resolution path for any of them MUST NOT exist.

Rationale: one procedure means one place to be wrong, one function to prove monotone, one preview to verify, and no possibility of a bombardment preview disagreeing with a bombardment.

CB-220 Disengagement MUST be resolved before any exchange and MUST consume no draw (§12).

CB-230 A unit whose class has atk = 0 MUST NOT initiate an Assault or a Reaction. It MAY still be a supporter only if its atk > 0; a class with atk = 0 MUST NOT support.

CB-240 Initiating an Assault MUST cost the lead attacker one movement point. A unit with no movement point remaining MUST NOT initiate an Assault. Supporting MUST cost no movement.

Rationale: movement is the natural bound on attacks per turn and needs no separate per-turn attack cap. A unit with four movement points can make four attacks and arrive nowhere, which is a real and legitimate choice, and one the falter and disorder rules already price.

CB-250 A unit that has faltered this turn (CB-1150) MUST NOT initiate a further Assault or Bombardment this turn.

CB-260 A unit that has supported this turn MUST NOT bombard this turn, and a unit that has bombarded this turn MUST NOT support this turn. A unit MUST NOT support more than one Clash per turn.

Rationale: both are firing actions and a gun should not be able to do both. Without this a battery supports the assault it is adjacent to and then shells a second target, which doubles the value of every gun and collapses the positioning decision the support rule exists to create.

CB-270 All state changes produced by a Clash MUST be applied immediately and completely before the next unit activates, so that a later attacker in the same Cascade sees the reduced strength and raised disorder its predecessors caused.

Rationale: this is what makes the siege vector of CB-2040 work, what makes bombard-then-assault a plan rather than a coincidence, and what makes conditional orders evaluated at activation see a true board.


4. The Clash Specification

CB-280 The engine MUST construct a Clash Specification — a value containing every input the procedure of §5 reads — before evaluating any part of that procedure. The procedure MUST read nothing outside the Specification.

Rationale: this is what makes the prediction guarantee of §16 mechanically enforceable rather than aspirational. Resolution and prediction call the same function on the same value; a preview can disagree with a resolution only if the Specification differs, which is a data problem with an inspectable diff, not a logic problem hidden in two copies of the arithmetic.

CB-290 The Clash Specification MUST contain exactly the following fields and no others.

Field Type Source
kind enum (CB-200) The initiating order
terrain terrain combat profile (§7) The defender's tile, or the mover's halt tile for a Reaction
lead combatant record The lead attacker
supporters 0–2 combatant records CB-370
defender combatant record CB-400
stackExtra integer 0–3 CB-1930
flankers integer 0–255 CB-980
defenderInOwnCity boolean 01-game-rules.md
defenderEncircled boolean CB-1400
attackerEdgeMods integer per-mille CB-590
defenderExtraMods integer per-mille CB-620
hashKey (seed, turn, initiatorSeq, attackOrdinal) CB-800

CB-300 A combatant record MUST contain exactly: atk, def, tough, role, strength, disorder, grade, supplyTier, stance, digIn, works, shaken, creationSequence.

CB-310 Every field of the Clash Specification MUST be an integer, an enumeration value, or a boolean. It MUST NOT contain object references, entity handles, or map coordinates.

Rationale: a Specification that cannot reach back into the world is a Specification that can be serialised into a test vector, replayed, diffed against a golden master, and shipped to the client for a preview without shipping the world.

CB-320 The Clash Specification MUST be serialisable to a canonical byte form and MUST appear in the combat event emitted for every resolution (CB-2440).

CB-330 A Clash Specification MUST be constructed from the true world state when resolving, and from a player's knowledge view when predicting (§17). The construction function MUST be the same in both cases, parameterised only by the view it reads.

CB-340 A Specification whose lead.strength is 0, whose defender.strength is 0, or whose defender is absent MUST be rejected before resolution; the initiating order MUST be treated as unachievable at activation rather than as an error, per the turn model's handling of stale orders.

CB-350 The engine MUST expose buildClashSpec(view, order) → ClashSpec | null as a pure function of its arguments.

CB-360 Two Clash Specifications that are byte-equal MUST produce byte-equal outcomes on every conforming implementation.


5. The resolution procedure

Every step below is normative in the order given. An implementation that reorders steps, or that combines two divisions into one, will produce different integers and is non-conforming.

5.1 Step 0 — Assemble the force

CB-370 Up to two supporters MAY join an Assault. A supporter MUST satisfy all of:

  1. it belongs to the attacking player or a player allied to them;
  2. it is adjacent (Chebyshev 1) to the target tile;
  3. it has not yet been activated this turn;
  4. it has not supported or bombarded this turn;
  5. it is not embarked in a transport;
  6. its class atk is greater than 0 and its strength is at least 1;
  7. its order stack does not have a Withdraw Reflex active (CM-1400).

Rationale for "not yet activated": a unit that has taken its activation has spent its turn. The consequence is that support flows from later-activating units to earlier-activating ones, so a gun that is standing still — low Initiative, late activation — is the one available to support. That is exactly backwards from what it first looks like and exactly right: the battery that displaced forward this turn cannot also fire in support of the assault, and the one that sat in position can.

CB-380 Support MUST be opt-in on the Attack order, with a supportPolicy of Auto (the default) or an explicit list of unit ids.

CB-390 Under supportPolicy: Auto the engine MUST select supporters by repeating twice over the eligible candidates: prefer a candidate whose role is not already present in the attacking force; among those, prefer the highest Step-1 contribution; ties break by lowest creationSequence. The same selection MUST be used by the preview.

Rationale: role diversity first because the combined-arms bonus (CB-560) is usually worth more than the marginal power of a second unit of the same role, and because the preview must be able to explain the choice in one clause.

CB-400 The defender MUST be the unit of the defending stack with the greatest Step-1 defensive contribution; ties break by lowest creationSequence. Every other unit of the stack MUST contribute only through stackExtra (CB-1930) and MUST NOT contribute power of its own.

CB-410 Supporting MUST cost the supporter its support for the turn and +10 disorder, and MUST NOT cost it movement or its activation.

CB-420 Support MUST key off physical adjacency and eligibility only. Formation or Group membership, shared Posting, or shared orders MUST NOT affect whether a unit may support, how much it contributes, or whether it is selected. (Satisfies CM-2160.)

CB-430 Supporters MUST take losses (CB-900) and MUST NOT advance under any circumstance.

CB-440 A Bombardment MUST have no supporters. Concentration of artillery MUST be expressed by firing several batteries at the same target in the same turn, not by one bombardment aggregating several guns.

Rationale: several independent bombardments against a target whose disorder is rising between them is a strictly more interesting thing than one aggregated shot, because each subsequent shot faces a weaker defence and the player can stop when the resolve threshold is crossed. It also makes the artillery concentration threshold of CB-1750 emerge from arithmetic rather than from a stacking rule.

CB-450 A Reaction MUST have no supporters.

CB-460 The attacking force MUST be frozen at the instant the lead attacker's order is evaluated. A supporter destroyed later in the Cascade MUST NOT retroactively alter a resolved Clash.

5.2 Step 1 — Each unit's raw contribution

CB-470 Each unit's contribution MUST be computed as exactly one expression with exactly one division:

contribution = floor( base × strength × (1000 + gradePower + supplyPower + roleTerrain) × factor
                      / 100 000 000 )

CB-480 base MUST be the class atk for every member of the attacking force and the class def for the defender.

CB-490 factor MUST be 1000 for the lead attacker and for the defender, and 400 for a supporter.

Rationale: 400 makes a supporter worth about four-tenths of itself, which is enough that a second role is worth bringing and not enough that massing three of the same thing beats bringing two kinds. It is the number that makes the combined-arms bonus the reason to add a supporter rather than the supporter's raw power being the reason.

CB-500 gradePower MUST come from CB-1610 and supplyPower from CB-1650.

CB-510 roleTerrain MUST be looked up by the contributing unit's role against the Specification's terrain, per CB-1450. Both sides MUST use the same terrain: there is exactly one battlefield and it is the defender's tile.

CB-520 A_raw MUST be the sum of the contributions of the lead attacker and all supporters. D_raw MUST be the defender's contribution alone.

CB-530 Each contribution MUST be floored independently before summation. The engine MUST NOT accumulate un-floored terms and divide once at the end of Step 1.

Rationale: the accumulate-wide-divide-once discipline applies within a single named quantity, not across units. Flooring per unit is what makes a supporter's line item in the readout (CB-2400) equal to the number that actually entered the sum, which is what lets a player learn the rules by reading the panel.

5.3 Step 2 — Force-level modifiers

CB-540 Force-level modifiers MUST be additive in per-mille, never multiplicative.

attackerMods = clamp( Σ attacker terms, −700, +1800 )
defenderMods = clamp( Σ defender terms, −700, +1800 )
A = max(1, floor( A_raw × (1000 + attackerMods) / 1000 ))
D = max(1, floor( D_raw × (1000 + defenderMods) / 1000 ))

Rationale: multiplicative stacking is more "realistic" and produces unassailable turtles — forest × dug-in × works × stack is four to five times defence, and each further bonus is worth more the more you already have. Additive stacking makes every bonus worth the same wherever you find it, which makes the one you are missing worth pursuing, and it makes the arithmetic something a player can do in their head while looking at a map.

CB-550 The attacker's terms MUST be exactly:

Term Value Condition
Stance CB-1520 The lead attacker's Stance; Assault only
Prepared fire +200 Reaction only, replacing the Stance term (CB-1790)
Combined arms CB-560 Assault only
River crossing −180 The lead attacker's tile and the target tile are separated by a river edge
Uphill −100 The target tile's elevation exceeds the lead attacker's
Amphibious −250 The lead attacker is attacking from aboard a transport

CB-560 The combined-arms term MUST be determined by the count of distinct roles present in the attacking force:

Distinct roles Term
1 0
2 +120
3 +260

Rationale: +120 is roughly the value of a Trained grade, so bringing a second kind of unit is worth about as much as a level of experience — significant, learnable, not decisive. The jump to +260 for three is deliberately super-linear because a full combined-arms force is the thing the design most wants players to build, and because reaching three roles costs both supporter slots and a real positioning problem.

CB-570 A river-crossing or uphill term MUST apply only to the lead attacker's approach, and MUST NOT be recomputed per supporter.

Rationale: a supporter is firing across the same battlefield from wherever it stands; making each supporter's approach a separate modifier would multiply the panel's line items without changing any decision a player makes.

CB-580 River-crossing and elevation are map properties. 01-game-rules.md MUST supply the predicates crossesRiverEdge(from, to) and elevation(tile); where it defines neither, both terms MUST be 0 and the readout MUST NOT display them.

CB-590 attackerEdgeMods in the Specification MUST be the pre-summed total of the river, elevation and amphibious terms, computed by buildClashSpec.

CB-600 The defender's terms MUST be exactly:

Term Value Condition
Terrain cover CB-1460 Always
Stance CB-1520 The defender's Stance
Position +100 × max(digIn, works) CB-1550
Stack support +100 × min(3, stackExtra) CB-1930
Caught −200 Disengagement failed for want of a retreat tile (CB-1880)

CB-610 A fortification of works level W MUST NOT contribute a defence modifier of its own. It MUST instead set the occupant's dig-in floor to W (CB-600 Position term), add +6 × W resolve (CB-710), add min(16, 8 × W) attacker shock (CB-1000), and forbid retreat (CB-1330).

Rationale: keeping works out of the additive defence stack is what stops a fortified city defender pinning at the modifier cap, and it reframes fortification as time saved and stubbornness rather than as a fourth multiplier. A player who builds works gets the dug-in position instantly instead of waiting four turns, and gets a garrison that will not run — which is what a fortification is for.

CB-620 defenderExtraMods MUST carry only the Caught term. No other source MAY write to it without a combatLogicVersion bump.

CB-630 The maximum reachable defender additive total MUST be +1600 (Mountain cover 600 + Stance Entrench 300 + position 400 + stack 300) and the maximum reachable attacker total MUST be +510 (Stance Advance 250 + combined arms 260). CI MUST assert both bounds against the shipped tables (CB-2680).

Rationale: the clamps at ±1800 and −700 are a safety rail, not an operating range. If a new terrain type or Stance value pushes the reachable maximum into the clamp, players discover a dead zone where their next bonus is worth nothing, and that feels like being cheated. The assertion is what keeps that a build failure rather than a bug report.

CB-640 The max(1, …) floors on A and D MUST be applied. A force MUST NOT have zero power.

Rationale: nothing is ever mathematically impossible. A player told "0 per-mille" who then wins, or told "certain" who then loses, stops trusting the readout, and the readout is the whole product.

CB-650 The engine MUST export A and D as integers. The odds ratio MUST be presented by the client from those two integers; the core MUST NOT compute or store a decimal ratio.

5.4 Step 3 — Volatility

CB-660 Volatility MUST be computed as:

volX       = gradeVolatility[grade] + supplyVolatility[supplyTier]
volatility = clamp( floor((volLead + volDefender) / 2) + terrainChaos, volatilityFloor, 1500 )

where volLead uses the lead attacker's grade and supply and volDefender the defender's.

CB-670 volatilityFloor MUST be 250 by default and MUST be settable at setup to 600 by the rules option of CB-2630.

CB-680 Supporters MUST NOT affect volatility.

Rationale: volatility is how well the two commanders in contact can predict the fight. A supporting battery two tiles of frontage away does not make the assault more or less legible to either of them, and letting it do so would give supporters a second, invisible effect the panel could not explain.

CB-690 Terrain MUST contribute both cover (CB-600) and chaos (CB-660), and these MUST be independent columns of the terrain profile.

Rationale: this is the cheapest high-value idea in the design. Terrain does not only change the odds, it changes how much you can trust them. Open ground is predictable; forest, marsh and above all city fighting are gambles. One extra column per terrain buys a whole strategic dimension and gives experience and reconnaissance a job that is not merely "bigger number".

CB-700 The reliability figure shown to the player MUST be floor(volatility / 4) in per-mille, and MUST be displayed as a first-class number alongside the odds, never folded into them.

5.5 Step 4 — Resolve

CB-710 Resolve MUST be computed for both sides as:

Resolve = clamp( 40 + gradeResolve + stanceResolve + 6 × works + supplyResolve
                    + (inOwnCity ? 10 : 0)
                    − disorder
                    − floor((100 − strength) / 2)
                    − (encircled ? 20 : 0)
                    − (shaken > 0 ? 20 : 0),
                 5, 95 )

CB-720 The attacker's resolve MUST be computed for the lead attacker only. Supporters MUST NOT contribute to or receive a resolve value.

CB-730 inOwnCity MUST be true only for the defender, and only when the defender's tile holds a city owned by the defender's player.

CB-740 encircled MUST be true only for the defender, and MUST be computed by CB-1400.

CB-750 The base of 40 MUST be common to every unit in the game. Class-specific resolve MUST NOT exist; resolve differences MUST come only from grade, Stance, works, supply, city, and condition.

Rationale: making resolve a property of the situation rather than of the unit type is what makes condition legible. A player who learns that "dug in and fresh is about 60, worn and shaken is about 15" has learned it for every unit in the game at once. A per-class morale stat would make that knowledge non-transferable and would give the roster a second axis to be unbalanced on.

CB-760 The clamp bounds 5 and 95 MUST both be reachable, and the engine MUST NOT assume resolve lies strictly inside them.

CB-770 Resolve MUST be recomputed for every Clash from current state. It MUST NOT be cached across activations.

CB-780 The resolve of the two sides MUST be computed by the same function with the same terms, differing only in which side supplies each input.

CB-790 The engine MUST expose both resolve values in the combat event and in the preview.

Rationale: resolve is the number that decides whether the tile changes hands, and it is not visible anywhere on the map. If the panel does not name it, the two-track structure of CB-030 is invisible and players will read the odds ratio as the whole story — which is precisely the misconception the system exists to replace.

5.6 Step 5 — The draw

CB-800 Exactly one random number MUST be drawn per Clash:

h = coreHash(seed, turn, initiatorSequenceNumber, attackOrdinal)     // 64-bit
u = h & 0xFFFFFFFF                                                  // low 32 bits, unsigned
r = floor( u × 1000 / 4294967296 )                                  // uniform in [0, 1000)

CB-810 The hash MUST be positionally addressed — a counter-based function of the identity of the engagement — and MUST NOT be drawn from a stream cursor, a global PRNG sequence, or any state that depends on how many other combats have already resolved.

Rationale: this is not a nicety, it is what makes the value for a given attack independent of everything else in the turn. It means a preview computed during the Orders phase and the resolution computed during the Cascade address the same cell; it means a replay that skips an unrelated combat still reproduces this one; and it means the AI can evaluate a hundred thousand candidate attacks without perturbing a single draw.

CB-820 initiatorSequenceNumber MUST be the creation sequence number of the unit that initiated the engagement — the lead attacker for an Assault or Bombardment, the reacting unit for a Reaction.

CB-830 attackOrdinal MUST be the count of Clashes of any kind that the initiating unit has already resolved this turn, starting at 0, and MUST be incremented after each. A Disengagement MUST NOT increment it.

Rationale: the pair (initiator, ordinal) is unique within a turn by construction, so the four hash inputs address a unique cell with no domain-separation tag needed. Making the ordinal shared across kinds is what guarantees a unit that reacts and then attacks does not reuse a draw.

CB-840 Deriving r by multiply-shift as in CB-800 MUST be used. u mod 1000 MUST NOT be used.

Rationale: mod 1000 over a 32-bit range is biased — 1000 does not divide 2³², so 296 of the residues are very slightly more likely. The bias is about 2.3 × 10⁻⁷ and would never be noticed, and that is exactly why it should not be shipped: it is a free correctness win that costs one multiply, and a system whose entire selling point is an exact readout should not contain a known-biased draw.

CB-850 u × 1000 MUST be computed in at least 42 bits of integer precision. In an IEEE-754-double implementation this is exact, since u × 1000 < 2⁴² < 2⁵³.

CB-860 The draw MUST NOT be consumed for a Clash that is rejected under CB-340, and MUST NOT be consumed by any preview.

5.7 Step 6 — The exchange

CB-870 The exchange MUST be computed as exactly:

s      = floor( (r − 500) × volatility / 2000 )        // per-mille swing, ±volatility/4
A_eff  = max(1, floor( A × (1000 + s) / 1000 ))
D_eff  = max(1, floor( D × (1000 − s) / 1000 ))
shareA = floor( A_eff × 1000 / (A_eff + D_eff) )
shareD = 1000 − shareA
Ld     = min( defenderStrength, floor( intensityOut  × shareA × 100 / (1000 × toughDefender) ) )
La     = min( leadStrength,     floor( intensityBack × shareD × 100 / (1000 × toughAttacker) ) )

CB-880 intensityOut and intensityBack MUST be taken from CB-200 by kind.

CB-890 The single draw MUST push the odds both ways at once — A up and D down by the same s. Two independent draws MUST NOT be used.

Rationale: pushing both ways keeps the total heat of the battle roughly constant and moves only the split, so a good draw is a better trade rather than a bigger battle. It is also what makes every downstream quantity monotone in r, which is what makes the preview exact (CB-2150). The honest cost is that outcomes within one Clash are perfectly correlated: you cannot have "unlucky casualties but they broke anyway". Some players will find that flat. The exactness is worth more.

CB-900 For an Assault, La MUST be distributed across the attacking force: the lead attacker loses La, and each supporter loses floor(La × 400 / 1000).

Rationale: a supporter contributes at 400 per-mille and bleeds at 400 per-mille. Anything else makes support either free (and therefore always correct) or ruinous (and therefore never taken).

CB-910 For a Bombardment La MUST be 0 by CB-200's intensityBack = 0, and no special case MUST be written.

CB-920 Ld MUST be allocated to the defending stack by carry-over: the defender absorbs up to its current strength; any excess passes to the next unit of the stack in descending Step-1 defensive contribution (ties by lowest creationSequence); any excess beyond the last unit is discarded.

Rationale: this settles the source design's open question on casualty distribution. Putting all losses on the top defender makes stacking cheap right up to the moment the whole stack dies at once, which is a violent and unreadable cliff. Carry-over is one line of deterministic arithmetic, removes the cliff, preserves the exactness of the preview (the preview reports Ld and the allocation is a pure function of it), and makes a stack genuinely absorb more total strength than one unit — which is what justifies the modest +100-per-unit defence term rather than a larger one.

CB-930 The saturating shape of A/(A+D) MUST NOT be linearised or replaced. Overwhelming odds MUST have diminishing returns on damage and sharply increasing returns on safety.

Rationale: at 4:1 the defender loses 48 and the attacker 12; at 10:1 the defender loses 54 and the attacker 5. The payoff for overwhelming force is that your own losses collapse and the break becomes certain, not that the hammer gets bigger. That is deliberate anti-doom-stack design: massing buys safety, not throughput, so a second army somewhere else is usually worth more than a bigger one here.

CB-940 At even odds each side MUST lose 30 strength at tough = 100, so that a unit survives approximately three even fights. intensityOut = 60 for an Assault is calibrated to this and MUST NOT be changed without re-deriving the recovery table of §15.

Rationale: three even fights is long enough that a unit is a persistent asset with a history worth protecting, and short enough that a contested tile resolves in two or three turns rather than ten.

CB-950 Reference values at tough = 100, for CI and for the tuning record:

shareA Approx. odds Ld La (tough 100) La (tough 130)
500 1.00 : 1 30 30 23
600 1.50 : 1 36 24 18
700 2.33 : 1 42 18 13
800 4.00 : 1 48 12 9
909 10.0 : 1 54 5 4
950 19.0 : 1 57 3 2

5.8 Step 7 — The break checks

CB-960 The break checks MUST be computed as exactly:

shockDefender = Ld + flankShock + stanceShockToDefender
shockAttacker = La + stanceShockToAttacker + min(16, 8 × defenderWorks)

defenderBreaks = shockDefender ≥ resolveDefender
attackerFalters = shockAttacker ≥ resolveAttacker  AND NOT defenderBreaks

CB-970 flankShock MUST be 8 × min(4, flankers) for an Assault, 12 (a fixed ambush term) for a Reaction, and MUST NOT apply to a Bombardment.

CB-980 flankers MUST be the count of units belonging to the attacking player or a player allied to them that are adjacent (Chebyshev 1) to the target tile at the moment of resolution, excluding the lead attacker, with strength ≥ 1. Supporters MUST be counted.

Rationale: a unit standing next to the defended tile is exerting pressure whether or not it fired. Excluding supporters would mean adding a supporter reduces the flanking bonus, which is incoherent and would make the panel impossible to explain. Note that the illustrative summary tables in the source design proposal were generated with supporters excluded and consequently disagree with the proposal's own worked example; the worked example and the prose are correct and this requirement follows them.

CB-990 Flanking MUST contribute shock only, and MUST NOT contribute power.

Rationale: this is the single most important structural constraint in §5. Surrounding a unit does not make your shells hit harder; it makes the unit decide it cannot stay. Keeping flanking out of the power calculation is what stops "adjacency bonus" from degenerating into "bring more units", and it is what makes positioning worth more than force — the design's central claim.

CB-1000 stanceShockToDefender and stanceShockToAttacker MUST come from CB-1520. For a Reaction both MUST be 0, replaced by the fixed ambush term of CB-970.

CB-1010 A defender break MUST suppress an attacker falter.

Rationale: you are not thrown back from a position you have just taken. Without the suppression the same Clash could both take the ground and refuse it, which has no coherent consequence.

CB-1020 Neither break check MUST apply to a Bombardment.

Rationale: guns unstick, they do not take ground. A bombardment that could rout a defender would let artillery capture tiles at zero risk from beyond retaliation range, which is the single most degenerate outcome available in this design space.

CB-1030 Break MUST be a threshold comparison, never a probability roll. All uncertainty in the break outcome MUST enter through Ld/La and therefore through the single draw.

Rationale: a second roll would double the entropy, halve the legibility, and destroy the exactness of the readout for nothing. The break is already uncertain, because the losses that drive it are.

5.9 Step 8 — Consequences

CB-1040 The outcomes MUST be exactly these five, evaluated in this order.

# Condition Consequence
1 Defender's stack reduced to 0 strength throughout Every reduced-to-zero unit destroyed; survivors remain; attacker MAY advance if the tile is empty
2 defenderBreaks and a legal retreat destination exists The whole defending stack vacates to that tile with +10 disorder each and 0 movement remaining; the lead attacker MAY advance
3 defenderBreaks and the defender's retreat class forbids retreat (CB-1330) Not destroyed. shaken set to 2. The tile does not change hands
4 defenderBreaks and no legal retreat destination exists Rout: the whole defending stack is destroyed
5 attackerFalters Losses stand; the attacker does not advance, gains +25 disorder, and may not attack again this turn (CB-250)

CB-1050 When none of conditions 1–5 hold, losses MUST be applied and nothing MUST move. The lead attacker MAY initiate a further Assault if it has movement remaining.

CB-1060 The lead attacker MAY advance only when the target tile is empty of enemy units after the consequence step, its Stance is Advance or Hold, it has at least one movement point remaining, and the tile is legal for it to occupy under stacking and passability rules.

CB-1070 Advancing MUST cost one movement point.

CB-1080 Stance Entrench and Stance March MUST NOT advance.

Rationale: an entrenched unit that leaves its position to occupy the ground it has just cleared has thrown away the position it spent turns preparing; the player who wants the tile sets Advance. A marching column is not fighting for ground at all.

CB-1090 Supporters MUST NOT advance (CB-430). A Reaction MUST NOT produce an advance (CB-200).

CB-1100 A retreating stack MUST arrive with its movement allowance for the turn spent, MUST NOT act again that turn, and MUST retain its Posture and order stack intact per CM-220.

CB-1110 Retreat MUST move the entire defending stack to the same destination tile. Splitting a retreating stack across destinations MUST NOT occur.

CB-1120 A retreat destination MUST have stacking room for the entire retreating stack. Where no candidate does, the retreat MUST be treated as having no legal destination and outcome 4 applies.

Rationale: this makes crowding behind a line into a real vulnerability, which is the correct consequence of the design's stacking economics. It is also the sharpest edge in the document and the first thing to soften if playtest shows rout losses above the CB-2710 band.

CB-1130 shaken = 2 MUST subtract 20 from resolve while non-zero and MUST be decremented by 1 at the end of each turn, so a garrison that breaks in place fights the remainder of this turn and the whole of the next at reduced resolve.

Rationale: cities fall to strength loss, not to rout. A defender that cannot run has to be killed, and the shaken penalty is what makes the second and third assault of a siege progressively cheaper without ever letting a fortification be taken by morale alone.

CB-1140 A stack destroyed by rout MUST emit one combat event listing every destroyed unit and MUST raise a Routed Attention Event for the losing player naming the tile and the unit count.

CB-1150 A faltered attacker MUST gain +25 disorder and MUST be barred from initiating further Assaults or Bombardments that turn.

CB-1160 After a sanctioned automated capture resulting from an advance, the capture obligations of CM-730 apply. This document MUST NOT duplicate them.

CB-1170 A unit that advances MUST have its digIn reset to 0.

CB-1180 All consequences MUST be applied before the Clash's combat event is emitted, and the event MUST contain the resulting state of every unit it touched.

5.10 Step 9 — Bookkeeping

CB-1190 Disorder MUST be applied after consequences, in exactly these amounts, and MUST be clamped to [0, 100] after summation:

Event Disorder change
Initiated an Assault lead attacker +6 + floor(La / 2)
Was assaulted defender +floor(Ld / 2)
Was bombarded defender +3 × Ld + 5
Was engaged by a Reaction mover +floor(Ld / 2) + 4
Reacted reactor +8
Broke and retreated broken unit +10 (whole stack)
Broke in place (outcome 3) defender +10
Attack faltered lead attacker +25
Supported an attack supporter +10
Disengaged disengaging unit +15

CB-1200 The bombardment coefficient of 3 × MUST be the mechanism by which artillery removes resolve. It MUST NOT be applied to any other kind.

Rationale: this one row is the entire combined-arms argument for a third role. A bombardment that takes 7 strength and adds 26 disorder has removed 26 resolve, and resolve is what decides whether the tile changes hands. Guns do not kill; guns unstick. That is a mechanically distinct job no amount of extra armour can do, and it is expressed entirely through the two-track structure of CB-030 rather than by giving artillery a bigger number.

CB-1210 Experience MUST be awarded as:

xpGain = 2 + floor(damageDealt / 4) + (opposingSideBroke ? 10 : 0)

for the lead attacker and for the defender, and floor(xpGain / 2) for each supporter, where damageDealt is Ld for the attacking side and La for the defender.

CB-1220 Total xp gained by a unit MUST be capped at 25 per turn from all sources.

Rationale: the cap is what stops a unit being farmed on a helpless target. Twenty-five per turn means the fastest possible path from Green to Elite is 28 turns of continuous heavy fighting, which no unit survives — so Elite is genuinely rare and is a story about a formation rather than a grind.

CB-1230 xp MUST be capped at 999 and MUST NOT decrease.

CB-1240 No xp MUST be awarded from a Disengagement, to either side.

Rationale: nobody learns anything from a contact broken before the exchange, and awarding it would make a screening force an experience farm.

CB-1250 digIn MUST be incremented by 1, to a maximum of 4, at the end of each turn in which the unit began and ended the turn on the same tile in Stance Entrench and did not initiate a Clash.

CB-1260 digIn MUST be reset to 0 whenever the unit changes tile for any reason, including retreat, disengagement, transport, and ReturnToPost.

CB-1270 A unit is considered to have fought this turn if it initiated, defended, supported, reacted to, was bombarded by, or disengaged from any engagement this turn. This flag MUST be cleared at the start of each turn.

CB-1280 The bookkeeping of §5.10 MUST be applied to every participant of every Clash, including participants that were destroyed, before the destroyed units are removed, so that the combat event reports final values.

CB-1290 Every quantity in §5.10 MUST be emitted in the combat event with its source named.

CB-1300 The engine MUST NOT apply any disorder, experience, or dig-in change not enumerated in §5.10.


6. Retreat, rout, and encirclement

CB-1310 A retreat destination MUST be selected deterministically from the tiles adjacent (Chebyshev 1) to the defender's tile that satisfy all of:

  1. passable to every unit of the retreating stack under its movement class;
  2. contains no unit hostile to the retreating player;
  3. is not the lead attacker's tile;
  4. has stacking room for the entire retreating stack;
  5. is legal for the retreating units' domain (a land unit may not retreat into open water).

CB-1320 Among the candidates the engine MUST select the lowest score under:

score = 4000 × (count of units hostile to the retreating player adjacent to the candidate)
      − terrainCover(candidate)
      − (a friendly unit is present on the candidate ? 500 : 0)
      − (candidate is strictly closer to the nearest friendly city than the current tile ? 2000 : 0)

with ties broken by lowest tile index (row-major), consistent with 03-architecture.md's canonical tile ordering.

Rationale for the weights: avoiding further contact dominates everything, so 4000 per adjacent enemy sits an order of magnitude above the rest. Falling back toward home (2000) outranks falling back into cover (at most 600) because a unit that retreats deeper into a pocket to sit in a forest has made its situation worse; company (500) is the smallest term because it is a preference, not a survival requirement. Every weight is separated from the next by more than the maximum of the terms below it, so the ordering is a strict lexicographic preference expressed as one integer — which is what makes it explainable in the readout as a ranked list of reasons.

CB-1330 Every unit MUST have a retreatClass:

Class Behaviour on break
Ground Retreats per CB-1310; destroyed if no candidate exists (rout)
Detached Disengages immediately to its nearest reachable friendly basing site; destroyed if none is reachable within its remaining endurance

CB-1340 A unit occupying a city or a works of level ≥ 1 MUST NOT retreat regardless of its retreatClass, and MUST take outcome 3 of CB-1040 instead.

CB-1350 The retreat destination MUST be computed against the world as it stands at the instant of resolution, using true state.

CB-1360 The preview MUST compute the retreat destination from the viewing player's knowledge view, and MUST mark it provisional whenever any input to CB-1310 or CB-1320 is unobserved.

Rationale: the exactness guarantee of §16 is a guarantee about the resolution function given its inputs, not a claim that a player can see the whole board. Showing a retreat tile computed from partial knowledge without saying so would be the one lie the readout must never tell.

CB-1370 The predicted retreat destination MUST be displayed on the map in the attack preview before the player commits.

Rationale: this is what turns "cut their line of retreat" from a thing that happens to you into a thing you plan. The mechanic of CB-050 is worthless if the player cannot see the roads they need to take away.

CB-1380 A retreating stack MUST NOT pass through or over any intervening tile. Retreat is a single step to an adjacent tile.

CB-1390 Zone-of-control rules, where 01-game-rules.md defines them, MUST be applied as an additional exclusion in CB-1310 and MUST NOT alter the scoring of CB-1320.

CB-1400 A defender MUST be considered encircled for the purposes of the resolve term in CB-710 when the candidate set of CB-1310 is empty at the moment resolve is computed.

Rationale: encirclement is not a state to track, a flag to set, or a subsystem to write. It is the name of a condition the retreat rule already computes, and reusing that computation is what makes CB-050 true rather than merely claimed. The −20 resolve means a cut-off unit breaks sooner, and breaking with nowhere to go destroys it — so the same geometry both causes and punishes the break.

CB-1410 Encirclement MUST be visible before it is fatal: the client MUST mark any unit whose candidate set is empty, for both owners, whenever the marked unit is visible to the viewing player.

CB-1420 Rout destruction MUST NOT be reduced, split, or partially applied. If the stack cannot retreat, all of it is destroyed.

Rationale: this is the mechanic the source design was least sure of, and it is also the one that makes the whole manoeuvre game worthwhile. Four units vanishing in one Clash is enormous swing, and softening it pre-emptively would remove the reason to encircle anything. CB-2710 puts a measured band on it so that a tuning failure is a failing number rather than an argument.

CB-1430 A unit destroyed by rout MUST count as a combat loss for scoring and victory purposes on the same terms as any other destruction; 14-victory.md owns the terms.


7. Terrain

CB-1440 Every terrain type defined by 01-game-rules.md MUST carry a combat profile consisting of: cover (per-mille), chaos (per-mille), and one roleTerrain per-mille value for each of the six roles.

CB-1450 The roleTerrain table MUST be:

Terrain family Foot Armor Gun Recon Naval Air
Open 0 +150 0 +100 0
Hills +100 −150 +100 0 0
Forest +150 −300 −150 −100 −100
Marsh 0 −400 −250 −150 0
Mountain +200 −500 −200 −150 −150
Urban +200 −250 −100 −200 −150
Water 0 0

Rationale: this one table is the whole combined-arms argument. Armour is 150 per-mille better than foot in the open and 700 worse in mountains. There is no terrain where a single role is correct, so a force that is all one thing has a map full of tiles it cannot profitably touch — and that is a structural incentive to mix rather than a bonus bolted on to reward mixing.

CB-1460 The cover and chaos table MUST be:

Terrain family Cover (defender force mod) Chaos (volatility)
Open 0 −150
Marsh +250 +250
Hills +300 +50
Forest +400 +200
Urban +500 +350
Mountain +600 +150
Water 0 0

Rationale: marsh is the deliberate oddity — only moderate cover, but the highest chaos short of a city. It is a bad place to be and a worse place to plan, which gives the map a terrain type whose danger is informational rather than numerical. Urban has both the second-highest cover and the highest chaos because city fighting should be the thing a careful player is most reluctant to gamble on, and that is a different feeling from "hard".

CB-1470 01-game-rules.md MUST assign every terrain type it defines to one of these families, or supply an explicit profile of its own. A terrain type with neither MUST use the Open profile, and CI MUST fail the content build with a named error rather than defaulting silently.

CB-1480 A terrain profile MUST NOT vary by attacker, by turn, by weather, or by any dynamic state. Where a scenario needs seasonal or weather effects, they MUST be expressed as a separate additive term in CB-550/CB-600 with its own requirement, not by mutating the profile.

Rationale: a static profile is what makes the terrain overlay of CB-2470 honest and what lets a player learn the map once. A mutating profile turns every learned fact into a maybe.

CB-1490 The engine MUST export defensiveValue(unitClass, terrain) = cover(terrain) + roleTerrain(role(unitClass), terrain) as an integer per-mille, higher being better. (Satisfies CM-1410.)

CB-1500 The terrain overlay in the client MUST be computed from the same profile data the core resolves with. A separate presentation table MUST NOT exist.


8. Stance, dig-in, and works

CB-1510 Combat MUST read the unit's Stance as defined by 10-turn-model.md, with values March, Advance, Hold, Entrench. Combat MUST NOT define its own tactical posture vocabulary and MUST NOT modify a unit's Stance.

CB-1520 The Stance combat table MUST be:

Stance Attack force mod Defence force mod Resolve Shock to defender Shock to attacker May advance
Advance +250 −200 −10 +6 0 yes
Hold 0 0 0 0 0 yes
Entrench −400 +300 +15 0 +10 no
March −250 −300 −5 0 0 no

Rationale for the March row, which the source design did not have: the turn model grants Initiative and forfeits reaction capacity for March, and something has to make that a trade rather than a free win. A column caught on the road is bad at fighting and much worse at being fought, which is what makes reactive fire (§11) and screening forces mechanically real. The resolve penalty is only −5 because a unit that is merely moving has not lost its nerve; the punishment is positional, not moral.

CB-1530 A unit in Stance March MUST NOT initiate an Assault or a Bombardment. It MAY be the defender of one, and MAY be the mover in a Reaction.

CB-1540 The Advance resolve penalty of −10 MUST be retained in combination with the strength penalty of CB-120.

Rationale: together they produce a specific and intended behaviour — a unit below about 60 strength falters out of any marginal attack, while a good attack still goes in. The rule is therefore "worn units cannot make bad attacks", not "worn units cannot attack", and it is intentional culmination rather than an accident of two penalties meeting. It will nonetheless be felt as "my units stopped working" before it is understood as "rotate your line", so CB-2400 requires the falter risk to be named explicitly in the preview.

CB-1550 The defender's position term MUST be +100 × max(digIn, works), so that works sets a floor on dig-in rather than adding to it.

CB-1560 digIn MUST accrue by CB-1250 and reset by CB-1260, with a maximum of 4 (+400).

CB-1570 works MUST be an integer 0–4 supplied per tile by 01-game-rules.md. A city MUST supply its own works level.

CB-1580 Stance MUST be derivable from Posture by the rules of 13-command.md §4 and MUST be overridable per turn by the player (CM-560). Combat MUST accept whatever value it is given.

CB-1590 The preview MUST name the Stance of both sides and the numeric contribution of each.


9. Grade and supply

CB-1600 Grade MUST be derived from xp by these thresholds and MUST NOT be stored separately:

Grade xp threshold
Green 0
Trained 100
Veteran 300
Elite 700

CB-1610 The grade table MUST be:

Grade Power Resolve Volatility
Green +0 +0 1000
Trained +80 +5 850
Veteran +180 +12 700
Elite +300 +20 550

Rationale: Elite is +30% power, not +200%. Veterans should be reliably better, not individually decisive — an Elite unit that beat four Green ones would make combat a race to a few super-units and would make every loss catastrophic. The volatility drop from 1000 to 550 is arguably the larger effect and is certainly the one that matters to a planner: an Elite formation's outcomes cluster, so you can build an operation around it.

CB-1620 Combat MUST read a supply tier with four values: Cut, Strained, Adequate, Full.

CB-1630 The supply tier MUST be derived from the supply state owned by 12-economy.md by exactly:

Economy supply state Additional condition Combat supply tier
Isolated Cut
Extended Strained
Supplied unit is not on a supply source tile Adequate
Supplied unit occupies a supply source tile (a supplying city or an emplaced depot) Full

Rationale: the economy owns the geography of supply and this document owns what supply does in a fight, so the mapping belongs here and the inputs belong there. The fourth tier is derived rather than requested because "standing on the depot" is a fact the economy already computes for its own purposes, and giving a unit a combat edge for fighting on top of its own supply head is both true to life and a real reason to place depots where the fighting will be.

CB-1640 12-economy.md MUST NOT define combat power multipliers for supply. The values below are the normative ones.

CB-1650 The supply table MUST be:

Supply tier Power Resolve Volatility
Cut −400 −15 +300
Strained −150 −5 +100
Adequate 0 0 0
Full +50 0 −100

Rationale: −150 for Strained is within three per-mille of the ×7/8 the economy proposal reached independently, so the two designs agree about extension. Cut is deliberately harsher than the economy's ×3/4, at −400 plus −15 resolve plus +300 volatility, because cutting a road is meant to be the strongest thing in the game, and the supply table of §23 shows it working: against a defender that no assault can shift, severing supply produces certain break. That is the operational game this system exists to create, and it should not be a marginal effect.

CB-1660 Supply MUST affect only power, resolve and volatility. It MUST NOT affect losses, break thresholds, or retreat directly.

CB-1670 The supply tier of both sides MUST be shown in the preview.


10. Bombardment

CB-1680 A Bombardment MUST be legal only when: the firing unit's class has bombardRange ≥ 1; the target tile is within bombardRange (Chebyshev) of the firing unit; the firing unit has not moved this turn; the firing unit has fired fewer than bombardShots times this turn; and the firing unit has not supported this turn.

CB-1690 A Bombardment MUST forfeit all of the firing unit's remaining movement for the turn.

Rationale: guns either displace or fire. This is what makes the position of a battery a decision a turn in advance rather than a thing that resolves itself, and it is what stops artillery from being able to both keep pace with an advance and support it.

CB-1700 A Bombardment MUST use intensityOut = 22 and intensityBack = 0, and MUST perform no break check for either side (CB-1020).

CB-1710 A Bombardment MUST NOT reduce a unit's strength below 1.

Rationale: artillery softens; it never takes ground and it never takes the last point. Without this floor a gun line kills at zero risk from beyond retaliation range, which is both degenerate and thematically wrong. It also means the last strength point of every defender must be paid for by somebody who walked into contact.

CB-1720 A Bombardment MUST apply +3 × Ld + 5 disorder to the target (CB-1190) and MUST NOT apply disorder to the firing unit.

CB-1730 A Bombardment MUST target the defending stack's top defender under CB-400, and its Ld MUST be allocated by carry-over under CB-920 subject to the floor of CB-1710 applying to each unit individually.

CB-1740 Bombardment against a city or works MUST use the same procedure. Where 02-units-and-industry.md gives a class a siege property, its effect MUST be expressed as an additional attackerEdgeMods term declared in that document and MUST NOT alter this procedure.

CB-1750 The equilibrium between bombardment and recovery MUST be treated as a designed quantity. One bombardment against a defender in a city produces roughly +23 disorder, against that city's −45 disorder recovery (CB-2070): two shots per turn roughly break even, and three or more make progress. 02-units-and-industry.md MUST set bombardShots against this threshold — a class whose bombardShots alone exceeds two can reduce a city single-handed, and a class with bombardShots 1 requires a gun line of three.

Rationale: artillery only works concentrated, and this is the arithmetic that makes it so. A gun line is a thing you build, move and protect as a unit rather than a per-unit habit, and the threshold falls directly out of the recovery table without any rule that mentions concentration. The reason the threshold is stated in shots rather than in batteries is that bombardShots is the roster's dial: whether concentration is required is decided there, and this requirement is what makes that decision visible to whoever turns the dial.


11. Reactions

CB-1760 10-turn-model.md owns when a reaction occurs and who gets it: reaction capacity, eligibility, and the interruption of the mover. This document owns only what the reaction does.

CB-1770 A Reaction MUST resolve as a Clash with the reacting unit as the attacking side and the moving unit as the defender, on the mover's halt tile, with intensityOut = 34 and intensityBack = 14.

Rationale for the asymmetry: a reaction is a firefight begun by a unit that is in position against one that is deployed for movement. The reactor deals a real blow and takes a glancing one. Symmetric intensity would make overwatch a losing trade against any tougher unit — a dug-in rifle company would deal 13 and take 16 to an armoured battalion — and a mechanic that costs you more than it costs your enemy will simply never be used, which would waste the reaction rule the turn model paid for.

CB-1780 The mover MUST halt regardless of the Clash outcome; the halt is the turn model's, not this document's. The break check determines only whether the mover is additionally thrown back.

CB-1790 In a Reaction the reactor's Stance attack modifier MUST NOT apply. A flat +200 prepared fire modifier MUST apply in its place, and the combined-arms term MUST NOT apply.

Rationale: Entrench gives −400 attack, and a reaction is precisely the reward for having entrenched. Applying the assault modifier to prepared fire would make the best-positioned unit the worst reactor, which inverts the mechanic.

CB-1800 In a Reaction the mover's own Stance defence modifier MUST apply, so that March (−300) is punished and Advance (−200) is punished less.

CB-1810 In a Reaction flankShock MUST be the fixed ambush term of 12 and the works and Stance shock terms MUST be 0 (CB-1000).

CB-1820 A mover that breaks MUST retreat from its halt tile under §6, and MUST be destroyed if no candidate exists.

Rationale: overwatch on a chokepoint is lethal, and it is lethal by the same rule that makes encirclement lethal. No new rule is needed for "ambushed in a defile".

CB-1830 A reactor that falters MUST gain +25 disorder and MUST forfeit its remaining reaction capacity for the remainder of the Cascade. It MUST NOT move or retreat.

CB-1840 A Reaction MUST NOT be predictable in the attack preview, because reaction capacity is not observable. The preview MUST instead state, as a warning without a probability, when the target tile or the planned route is adjacent to a visible enemy that could hold reaction capacity.

Rationale: an honest "you may be intercepted here" is worth more than a fabricated percentage. This is the one place the readout deliberately declines to give a number, and saying why is part of the contract.


12. Disengagement

CB-1850 When an Assault would resolve against a unit whose order stack has an active Withdraw Reflex (CM-1400), and that unit has not already disengaged this turn, the engine MUST resolve a Disengagement instead of a Clash.

CB-1860 A Disengagement MUST consume no draw, MUST perform no break check, and MUST resolve as:

  1. select a retreat destination by §6;
  2. if one exists, the disengaging unit loses floor(1000 / tough) strength, gains +15 disorder, vacates the tile, and its movement for the turn is spent;
  3. the attacker takes no losses and MAY advance under CB-1060;
  4. if no destination exists, the unit is Caught and a normal Assault resolves with a −200 defender force modifier (CB-600).

CB-1870 Parting losses at the calibration tough values MUST therefore be: 16 for tough 60, 14 for 70, 10 for 100, 7 for 130.

CB-1880 The Caught modifier of −200 MUST apply only to the Clash that follows a failed disengagement, and MUST NOT persist.

CB-1890 A unit MUST disengage at most once per turn. A second attack on a unit that has already disengaged this turn MUST resolve as a normal Clash.

Rationale, and this settles the source design's open question on infinite screening: a screening unit trades strength and disorder for tempo, and both meters run against it. Disengaging counts as having fought (CB-1270), so the unit recovers no strength at end of turn and only half its disorder — +15 gained against −10 recovered is a net +5 disorder per turn, while strength falls by the parting loss with no recovery at all. A scout troop screening every turn is dead in about six turns. The once-per-turn cap closes the other half of the hole, which is one unit absorbing an unbounded number of attacks in a single turn by walking backwards.

CB-1900 A disengaging unit MUST NOT gain or grant experience (CB-1240).

CB-1910 The attacker's preview MUST show, when the target is under a Withdraw Reflex and that fact is observable, that the attack will yield the tile without an exchange, together with the parting loss the defender will take.

CB-1920 A Detached unit MUST NOT disengage under this section; CB-1330 governs it.


13. Stacks

CB-1930 stackExtra MUST be the count of units on the defending tile other than the defender, with strength ≥ 1, and MUST contribute +100 × min(3, stackExtra) to the defender's force modifier.

CB-1940 01-game-rules.md owns stacking capacity. The cap of 3 in CB-1930 MUST be independent of that capacity, so that raising the stacking limit does not raise the defensive ceiling.

Rationale: the reachable-maximum assertion of CB-630 must survive a stacking-rule change made by another document. Decoupling the two is what makes that possible without a cross-document renegotiation.

CB-1950 Only the defender MUST contribute defensive power (CB-400). Other stack members MUST NOT contribute power, resolve, volatility, or break terms.

CB-1960 Losses MUST be allocated to the stack by carry-over (CB-920).

CB-1970 A break MUST move or destroy the whole stack (CB-1110, CB-1420).

CB-1980 The attacking side MUST NOT stack: an Assault is one lead attacker plus at most two supporters, each on its own tile.


14. Cities, works, and sieges

CB-1990 A defender in a city or works MUST NOT retreat (CB-1340) and MUST take the shaken consequence of CB-1130 instead.

CB-2000 A city MUST therefore fall to strength loss, never to rout.

Rationale: cities are sieges, not skirmishes. They are where the game's big decisions land and they are the one place where fight-to-the-death is the rule — which is only tolerable because CB-040 makes it the exception everywhere else.

CB-2010 The +10 own-city resolve term (CB-710) and the +6 × works term MUST both apply, and MUST stack additively with the Stance and grade terms.

CB-2020 A player MUST be able to order a garrison to abandon its place during the Orders phase. An abandoning unit MUST move out under normal movement at its activation, and the place MUST be left undefended.

Rationale, and this settles the source design's open question on city retreat: the pathology it worried about is a player feeding units into a hole they cannot leave, and the fix is to let them leave — deliberately, in advance, as a decision. Making it an automatic break-triggered behaviour instead would take the decision away and would let a fortification be lost to a morale check, which CB-2000 forbids. As an order it is legible, it is the player's, and it costs them the city they chose to give up.

CB-2030 Abandonment MUST NOT be reachable through any Sanction and MUST require an explicit player order, consistent with CM-410.

CB-2040 The following siege sequence MUST be reproduced exactly by any conforming implementation and MUST be a golden-master test vector. Defender: Rifle Company (atk 30, def 44, tough 100, role Foot), Trained, Adequate, Stance Entrench, in its own works-3 city on Urban terrain, alone. Attacker for the assaults: Armoured Battalion (atk 62, def 38, tough 130, role Armor), Veteran, Full, Stance Advance, strength 100, three flankers. Bombardment by Field Battery (atk 48, tough 70, role Gun), Trained, Adequate, strength 100, at median draw.

Defender state A D Resolve They lose (min/med/max) Break
str 100, dis 0 75 117 88 16 / 23 / 31 0‰
str 81, dis 69 75 94 10 18 / 26 / 34 1000‰
str 59, dis 55 75 69 13 22 / 31 / 39 1000‰
str 28, dis 80, shaken 75 31 5 28 / 28 / 28 1000‰, destroyed

Three sequential battery shots from strength 100 at the median draw: A 47 against D 117, 109 and 102, removing 6 strength and adding 23 disorder each; end of turn strength 82, disorder 69, city recovery −45 leaves disorder 24.

CB-2050 The intended shape is two turns of a three-battery gun line plus two armoured assaults to take one entrenched company out of a level-3 fortification. CB-2710 measures deviation from it.

CB-2060 Combat MUST NOT transfer city ownership. It vacates or destroys the defenders; the advance of CB-1060 into an empty city is what 01-game-rules.md acts on.


15. Recovery

CB-2070 At the end of every turn, after all combat and before production, each unit MUST recover according to the base row that matches its situation:

Situation Disorder Strength
In the field −20 +4
Stance Entrench, in the field −35 +4
In a friendly city −45 +12
In a friendly city with a depot −45 +20

CB-2080 The base row MUST then be modified, in this order:

  1. if supply tier is Strained, halve both values by floor division and set strength recovery to 0;
  2. if supply tier is Cut, set both to 0;
  3. if the unit fought this turn (CB-1270), set strength recovery to 0 and halve the disorder recovery by floor division.

CB-2090 The order of operations in CB-2080 MUST be exactly as given. Halving twice MUST produce floor(floor(x/2)/2).

CB-2100 Strength MUST be capped at 100 and disorder floored at 0 after recovery.

CB-2110 The recovery asymmetry MUST be preserved: a unit at 40 strength is 5 turns from full in a city, 3 in a city with a depot, and 15 in the field.

Rationale: rotation is a real decision with a real cost, and this is where it lives. Fifteen turns in the field is long enough that leaving a worn unit forward is a genuine sacrifice; five in a city is short enough that pulling it back is a plan rather than a write-off. It also gives cities a purpose beyond production — they are where armies are repaired — which is a second reason to want them and a second thing to lose when one falls.

CB-2120 Recovery MUST NOT be applied to a unit destroyed during the turn.

CB-2130 Attrition damage from being out of supply is owned by 12-economy.md and MUST be applied after this section's recovery, so that a Cut unit neither recovers nor is double-counted.


16. The prediction contract

This section is the reason the rest of the document is shaped the way it is. Under Orders and the Cascade a player commits before seeing, so a prediction that can disagree with the resolution is worse than no prediction at all.

CB-2140 The engine MUST expose exactly one resolution function

resolveExchange(spec: ClashSpec, r: int) → Outcome

and the prediction function MUST be implemented by calling it. A second implementation of the arithmetic, in any form — an estimator, an approximation, a client-side copy, an AI-specific evaluator — MUST NOT exist.

Rationale: this is the requirement the whole subsystem is built to make satisfiable. Single-exchange resolution (CB-020), a self-contained Specification (CB-280) and monotonicity (CB-2150) exist so that an exact preview is a handful of calls to the real function rather than a model of it. The class of bug where the AI plays a different game from the player, or where the preview is right in the common case and wrong at the edges, is not mitigated here — it is made unrepresentable.

CB-2150 Every downstream quantity MUST be monotone in r, specifically: s, A_eff, shareA, Ld and shockDefender are non-decreasing; D_eff, shareD, La and shockAttacker are non-increasing.

Rationale, stated as a proof obligation rather than an observation: s is non-decreasing in r by construction; A_eff is non-decreasing and D_eff non-increasing in s; shareA = floor(1000 · A_eff / (A_eff + D_eff)) is increasing in A_eff and decreasing in D_eff, so it is non-decreasing; Ld and La are monotone transforms of shareA and shareD; min with a constant preserves monotonicity, as does the max(1, …) floor. The property therefore holds for every intensity pair and every clamp setting, not merely for the shipped constants — but CB-2670 requires the sweep anyway, because a future non-monotone modifier would silently make every preview a lie.

CB-2160 The loss bands MUST be computed by evaluating resolveExchange at r = 0, r = 500 and r = 999, and taking minimum, median and maximum by value rather than by position.

CB-2170 The break threshold rDef MUST be the least r ∈ [0, 1000) for which defenderBreaks holds, or 1000 if none does, found by binary search over the closed range. P(defender breaks) MUST be reported as 1000 − rDef per-mille.

CB-2180 The falter threshold rAtt MUST be the least r ∈ [0, 1000) for which shockAttacker < resolveAttacker, or 1000 if none does. P(attacker falters) MUST be reported as min(rAtt, rDef) per-mille.

Rationale: the attacker breaks for r < rAtt because its shock is non-increasing, and CB-1010 suppresses the falter whenever the defender breaks, which happens for r ≥ rDef. The intersection is exactly r < min(rAtt, rDef), so the answer is one subtraction and no further search.

CB-2190 The survival threshold rSurv MUST be the least r for which La < leadStrength. P(attacker destroyed) MUST be rSurv per-mille.

CB-2200 winPermille MUST be defined as 1000 − max(rDef, rSurv), floored at 0: the count of draws for which the defender breaks and the lead attacker survives.

CB-2210 capturePermille MUST be winPermille when CB-1060's positional preconditions hold for the attacker, and 0 otherwise. (Satisfies the capture-probability requirement of CM-1370.)

CB-2220 The binary search MUST use exactly the bracket [0, 1000) and MUST terminate in at most 10 probes.

CB-2230 A full preview MUST cost at most 33 evaluations of resolveExchange: 3 for the bands, 10 each for rDef, rAtt and rSurv.

CB-2240 Every reported probability MUST be an exact count of favourable draws out of 1000, expressed in per-mille. Monte Carlo sampling, expected-value approximation, and curve fitting MUST NOT appear anywhere in prediction.

Rationale: "roughly 60%" is the specific lie that makes players distrust odds displays. 597‰, and it is right, is a different kind of object.

CB-2250 The command layer's win-probability predictor (CM-150) MUST be winPermille from this function, evaluated over the knowledge view.

CB-2260 The predictor MUST set estimated = true whenever the knowledge view lacks a current observation of the target's strength, and in that case MUST return 400‰, per CM-160.

Rationale: this document owes the command layer a conservative single number under uncertainty, and owes the player a band and a pivot for the same situation (§17). Those are two projections of the same computation, not two predictors, and the difference is what the caller can act on: automation needs a threshold test, a person needs to know which unknown is doing the damage.

CB-2270 The actual outcome of a resolution MUST always lie within the displayed loss bands, by construction. A violation MUST be a build-failing CI assertion, not a playtest observation (CB-2660).

CB-2280 The preview MUST be a pure function of the Clash Specification. It MUST NOT consume the PRNG, mutate state, emit events, or depend on how many previews have already been computed.

CB-2290 The AI MUST use this preview function and MUST NOT be given a cheaper approximation.

Rationale: at the cost bounds of §20 there is no efficiency argument for a separate estimator, so the only thing a separate estimator could buy is a divergence between what the AI believes and what the game does. That divergence is a whole class of bug and it is being designed out.


17. Prediction under fog

CB-2300 Combat prediction MUST be computed over the viewing player's knowledge view and MUST NOT read true state for anything the viewing player has not observed, consistent with CM-130.

CB-2310 01-game-rules.md MUST supply, per observed enemy unit, an observation level with exactly three values, and this document requires the following fields at each:

Level Name Fields available
L1 Sighted Owner, unit class, tile, terrain, stack count
L2 Adjacent L1 plus Stance, works level, supply tier, and strength to within ±15
L3 Identified L2 plus exact strength, disorder, grade, digIn, shaken

CB-2320 A Recon-role unit adjacent to an enemy MUST grant Identified on that enemy. Any unit that fought a given enemy during the previous Cascade MUST hold Identified on it until the end of the current turn.

Rationale: reconnaissance's job in this system is not to add a bonus, it is to collapse a band into a number. The table in CB-2360 shows a prediction that spans the entire 0–1000‰ range at L1 and resolves to a single value at L3, so scouting is the difference between knowing and not knowing rather than a few percentage points of edge.

CB-2330 When an input is unobserved, the preview MUST evaluate over the full domain of that input and report the resulting range of each output, together with a provisional flag naming every unobserved field.

CB-2340 The preview MUST perform pivot analysis over each unobserved field with an enumerable domain (grade 4 values, stance 4, supplyTier 4, digIn 5, works 5). For each such field it MUST determine whether the break verdict is constant across the domain; where it is not, it MUST report the threshold value at which the verdict changes.

CB-2350 The preview MUST display at most two pivots, ordered by the span of breakPermille each induces, largest first.

Rationale: a readout that says "0 to 1000 per-mille" is useless. A readout that says "they break unless they are Veteran or better — grade unobserved, a Recon unit adjacent would tell you" names the risk, names the threshold, and names the fix, and it is a sentence a player can act on. It costs four more preview calls over the unobserved dimension, which at the bounds of §20 is free.

CB-2360 The following pivot table MUST be reproduced exactly and MUST be a golden-master test vector. Attack: Armoured Battalion (Veteran, Full, strength 92, Stance Advance) with a Field Battery supporting (Trained, Full, strength 100), against a Rifle Company at strength 78, disorder 15, Stance Entrench, digIn 2, Adequate, in Forest, one flanker. A = 97 throughout.

Defender grade D Resolve They break They lose
Green 74 29 1000‰ 26 / 34 / 41
Trained 79 34 1000‰ 25 / 33 / 39
Veteran 85 41 885‰ 25 / 31 / 38
Elite 93 49 125‰ 24 / 30 / 36

CB-2370 The preview MUST make explicit that the attacker's own losses are well-known even when the outcome is not, because La depends on the defender's power and not on its resolve.

Rationale: the cost of attacking is roughly knowable from a distance while the result is not, and that is exactly the right asymmetry for a game about committing forces. The panel should say so rather than leaving the player to infer it from two bands of different widths.

CB-2380 A preview MUST NOT be cached across turns and MUST be recomputed whenever the knowledge view changes.


18. The readout

CB-2390 The attack preview MUST present its content in this order, and 04-ui-ux.md MUST implement that ordering:

  1. The verdict, in plain language, first — "They break — certain."
  2. Both loss bands as min ── median ── max, with the resulting strength values.
  3. Odds and reliability as two separate figures.
  4. Every contributing line item, with its source named.
  5. The consequence: where they will retreat to, whether they are cut off, whether you take the tile.
  6. The alternative: a one-click preview of the same attack without each supporter.

CB-2400 A single expected value MUST NOT be shown in place of a band, and the falter probability MUST always be shown even when it is 0‰.

Rationale: a single expected value is the specific lie that makes players distrust odds displays, and a falter risk that appears only when non-zero is a risk players never learn exists until it happens to them. CB-1540's culmination behaviour is only fair if the panel names it every time.

CB-2410 Because support and flanking are automatic, the panel MUST enumerate every unit it counted and every modifier it applied.

Rationale: automatic contributions that are not itemised are indistinguishable from magic. The panel is where a player learns the rules, and a rule that fires without appearing on it will be learned as superstition or not at all.

CB-2420 The panel MUST name the retreat destination and MUST state explicitly when the defender has none.

CB-2430 A hostile-tile overlay MUST be available, colouring each enemy-occupied tile adjacent to the player's units by the breakPermille of the best available attack, computed with the same function.

CB-2440 After every resolution the client MUST show a reconciliation line stating the prediction and the actual result — "predicted 26–41, actual 35".

Rationale: this builds trust in the readout faster than any tutorial, because it is evidence rather than a claim, and it makes CB-2270's invariant visible to the person it protects.

CB-2450 The core MUST emit a combat event for every resolution containing: the Clash Specification, r, every intermediate quantity of §5, the outcome, and the resulting state of every participant.

CB-2460 The combat event MUST be sufficient to reconstruct the resolution without access to the world state at the time of resolution.

CB-2470 The client MUST NOT compute any combat number itself. Every figure it displays MUST come from a core export.

CB-2480 Every per-mille figure shown to a player MUST be shown in per-mille or as a rounded percentage derived from it, and MUST NOT be re-derived from a percentage.


19. Determinism and arithmetic

CB-2490 Combat MUST be the only subsystem that consumes randomness, consistent with CM-110.

CB-2500 Two conforming implementations given the same Clash Specification and the same r MUST produce byte-identical outcomes on every platform.

CB-2510 Every intermediate product MUST fit within a signed 64-bit integer, and every intermediate in §5 MUST be below 2⁴⁷ in magnitude, so that an IEEE-754-double implementation is exact.

Rationale: the largest intermediate is a Step-1 contribution numerator at 255 × 100 × 1550 × 1000 ≈ 3.95 × 10¹⁰, about 2³⁵·². The 2⁴⁷ bound leaves eleven bits of headroom for a roster that raises the stat caps, and it is the assertion that makes "a TypeScript number is fine here" a checked claim rather than a hope.

CB-2520 Iteration over stacks, adjacency, supporters and flankers MUST be in a canonical order: tiles by lowest tile index, units by lowest creationSequence, consistent with CM-170.

CB-2530 A combatLogicVersion MUST be recorded in the game record. Any change to a constant, a table, a formula, or an ordering in this document MUST bump it.

CB-2540 Combat MUST NOT read wall-clock time, entity allocation order, hash-map iteration order, or object identity.

CB-2550 A Clash MUST NOT depend on the number of Clashes previously resolved this turn other than through attackOrdinal (CB-830) and through the state changes of CB-270.

CB-2560 The seeded hash function MUST be the one 03-architecture.md defines, used in its counter-based form.


20. Performance

CB-2570 One resolution MUST be O(1) in the number of units in the game and MUST perform no allocation on the hot path.

CB-2580 A full exact preview MUST cost at most 33 resolutions (CB-2230); a preview with pivot analysis over one unobserved enumerable field MUST cost at most 33 × 5 = 165.

CB-2590 The engine MUST sustain at least 500 resolutions per Cascade and 100,000 previews per second on the reference hardware of 03-architecture.md, with no Monte Carlo anywhere and no loop whose length depends on the units involved.

Rationale: the second figure is what makes CB-2290 affordable. An AI that can evaluate a hundred thousand candidate attacks in well under a second can use the player's exact preview rather than an approximation, and the hostile-tile overlay of CB-2430 can be repainted every frame.

CB-2600 Worst-case Cascade combat time MUST be flat with respect to force composition, terrain, and odds.

CB-2610 Preview computation MUST be safe to run concurrently and off the simulation thread, being a pure function (CB-2280).


21. Rules options

CB-2620 Combat rules options MUST be fixed at setup, MUST be part of the game record, and MUST contribute to combatLogicVersion resolution.

CB-2630 The option volatilityFloor MUST offer exactly two values: Measured (250, the default) and Chaotic (600).

Rationale, and this addresses the source design's open question on whether one draw is enough drama: the design's position is that the surprise should come from not knowing what you are attacking rather than from the resolution, and §17 is the argument for it. But it is a taste question that only playing answers, and raising the floor is a one-constant experiment that changes nothing structural — the preview stays exact, the bands simply get wider. Shipping both and measuring which is preferred is cheaper than arguing about it.

CB-2640 No rules option MAY alter the shape of the procedure, only the value of a named constant.

CB-2650 The client MUST display the active options in the attack preview header whenever any is not at its default.


22. CI invariants and playtest metrics

CB-2660 CI MUST assert, over the golden-master corpus, that every resolved outcome lies within the loss band the preview displayed for it. A violation MUST be a build failure.

CB-2670 CI MUST re-run the monotonicity sweep of CB-2150 whenever any constant in this document changes. The reference sweep covers every terrain, grade, supply tier, Stance, three strength levels, two disorder levels, two dig-in levels and all pairings of the calibration cast, over all 1000 draws: 86,016,000 evaluations, zero violations at the constants specified here.

CB-2680 CI MUST assert that the maximum reachable additive defender total is at most +1600 and the maximum reachable attacker total at most +510, against the shipped tables (CB-630).

CB-2690 CI MUST verify the binary-search prediction against a brute-force oracle over all 1000 draws for a spanning corpus of Specifications. The reference corpus is 5,376 cases: zero mismatches.

CB-2700 CI MUST assert that no combat code path calls a floating-point operation, consistent with the determinism lint rules of 03-architecture.md.

CB-2710 The automated playtest harness MUST measure the following, with the stated targets, so that a balance regression is a failing number rather than an opinion.

Risk Metric Target Response if it fails
Encirclement swing dominates Share of total unit losses from rout 20–35% Reduce CB-1420 to the top two units of the stack
Fronts freeze under entrenchment Share of turns on a contested front with no tile changing hands < 40% Reduce the Entrench defence term or raise the bombardment coefficient
Artillery dominance Ratio of Bombardments to Assaults ≤ 2:1 Lower intensityOut for Bombardment or raise the recovery floor
Artillery uselessness Same ratio ≥ 0.5:1 Raise the bombardment disorder coefficient from 3×
Disorder death spiral Share of unit-turns above 70 disorder < 10% Raise field disorder recovery from −20
Combat too grindy Mean turns to resolve a contested tile 2–4 Raise intensityOut for Assault from 60
A role is never worth building Build share by role No role below 15% Retune CB-1450
Fog band unusable Share of previews where pivot analysis names a single dominant unknown > 70% Show two pivots (already permitted by CB-2350)
Screening is free Mean turns a screening unit survives continuous disengagement 5–8 Raise the disengagement disorder from +15

CB-2720 Preview trust MUST NOT appear in the playtest table as a tuning metric. It is an invariant asserted by CB-2660.

CB-2730 The reference worked example of §23 MUST be a golden-master test vector.

CB-2740 Every table in this document MUST be generated from the same constant definitions the engine compiles, and CI MUST assert that the document's tables match the shipped constants.

Rationale: hand-edited numbers in a specification drift from the code within one release, and this document's entire value is that its numbers are the real ones.


23. Worked example, normative as a test vector

The situation. Belvoir Wood, a Forest tile. The 3rd Rifles hold it: Green, strength 78, disorder 15, Stance Entrench, digIn 2, supply Adequate, alone on the tile. Attacking are the 2nd Armoured Battalion (Veteran, strength 92, supply Full, Stance Advance) from the west, and the 1st Field Battery (Trained, strength 100, supply Full) adjacent to the north, joining as a supporter. Both attackers are adjacent to the target, so flankers = 1.

Step 1 — contributions.

Working Result
2nd Armoured 62 × 92 × (1000 + 180 + 50 − 300) × 1000 / 10⁸ 53
1st Field Battery (supporter, factor 400) 48 × 100 × (1000 + 80 + 50 − 150) × 400 / 10⁸ 18
A_raw 71
3rd Rifles 44 × 78 × (1000 + 0 + 0 + 150) × 1000 / 10⁸ 39

Step 2 — force modifiers. Attacker: Advance +250, combined arms (Armor + Gun) +120 → +370; A = floor(71 × 1370 / 1000) = 97. Defender: Forest cover +400, Entrench +300, position +200 → +900; D = floor(39 × 1900 / 1000) = 74. Odds 97 : 74.

Step 3 — volatility. Attacker 700 − 100 = 600; defender 1000 + 0 = 1000; mean 800; Forest chaos +200 → 1000. Reliability ±250‰.

Step 4 — resolve. Defender 40 + 0 + 15 − 15 − floor(22/2) = 29. Attacker 40 + 12 − 10 − floor(8/2) = 38.

Step 5 — the draw. r = 617.

Step 6 — the exchange. s = floor(117 × 1000 / 2000) = 58; A_eff = 102; D_eff = 69; shareA = floor(102000 / 171) = 596; shareD = 404; Ld = floor(60 × 596 × 100 / 100000) = 35; La = floor(60 × 404 × 100 / 130000) = 18.

Step 7 — the break checks. Defender shock 35 + 8 + 6 = 49 ≥ 29the 3rd Rifles break. Attacker shock 18 + 10 = 28 < 38, and suppressed regardless by CB-1010.

Step 8 — consequences. 3rd Rifles: 78 → 43 strength, disorder 15 → 42, retreat to the lowest-scoring adjacent tile. 2nd Armoured: 92 → 74, disorder +15, advances into Belvoir Wood if it has a movement point. 1st Field Battery: disorder +10, support spent.

What the player was shown before committing — every figure below is the exact output of the preview function, and the actual result above falls inside every band:

┌─ ASSAULT ─ 2nd Armoured Battalion → Belvoir Wood (Forest) ──────────────────┐
│  YOUR FORCE                                        attack power     97      │
│    2nd Armoured    Advance    92   Veteran          53   base 62            │
│                    +180 veteran  +50 full supply  −300 armour in forest     │
│    1st Field Bty   support   100   Trained          18   base 48 × 400‰     │
│                    +80 trained   +50 full supply  −150 guns in forest       │
│    force           +250 advance  +120 combined arms (armour + guns)         │
│                                                                             │
│  THEIR FORCE                                       defence power    74      │
│    3rd Rifles      Entrench    78   Green           39   base 44            │
│                    +150 foot in forest                                      │
│    position        +400 forest cover  +300 entrench  +200 dug in ×2         │
│                                                                             │
│  ODDS 97 : 74                   RELIABILITY  ±250‰  (broken ground, green)  │
│                                                                             │
│  THEY LOSE     26 ── 34 ── 41   →  78 drops to 37–52                        │
│  YOU LOSE      14 ── 19 ── 25   →  92 drops to 67–78                        │
│                                                                             │
│  ▸ THEY BREAK — certain (1000‰).  Resolve 29 against shock 40–55.           │
│    They fall back to Ford Seven. They are not cut off.                      │
│  ▸ Your attack falters: never (0‰).                                         │
│  ▸ You take Belvoir Wood if you have a movement point left.  You do.        │
│                                                                             │
│  [ Attack ]   [ Attack without 1st Field Battery ]   [ Cancel ]             │
└─────────────────────────────────────────────────────────────────────────────┘

The same attack against a fresh defender — strength 100, disorder 0, everything else identical:

Worn (78 / 15) Fresh (100 / 0)
Defence power 74 95
They lose 26 / 34 / 41 22 / 30 / 37
You lose 14 / 19 / 25 17 / 22 / 28
Their resolve 29 55
They break 1000‰ 0‰

The identical attack is decisive against one and pointless against the other. What changed was not the odds — 1.31:1 against 1.02:1, a modest shift — but the resolve, and resolve is driven by condition. So change the condition first. The battery bombards instead of supporting: A 47 against D 95, taking 4 / 7 / 10 strength and adding 17 / 26 / 35 disorder. Seven strength is almost nothing; 26 disorder is 26 points of resolve. The armour then assaults alone at A 66 against D 89 — worse odds than the attack that achieved nothing — and the defender, now at resolve 26, breaks 882‰ of the time. The armour pays a median 26 strength to do it, more than the 22 it would have lost failing. Winning is more expensive than losing. Good.

Three terrains and two Stances, lone Veteran Armoured Battalion in Advance at full strength and Full supply against a Trained Rifle Company at full strength and Adequate supply, no supporters, no flankers:

Terrain Their Stance A D Reliability They lose You lose Resolve They break You falter
Open Hold 106 47 ±143‰ 37/41/45 11/14/17 45 858‰ 0‰
Open Entrench 106 61 ±143‰ 33/38/41 13/16/20 60 0‰ 0‰
Forest Hold 71 75 ±231‰ 22/29/36 18/23/29 45 0‰ 0‰
Forest Entrench 71 91 ±231‰ 19/26/33 20/25/31 60 0‰ 0‰
Mountain Hold 56 89 ±218‰ 17/23/29 23/28/33 45 0‰ 0‰
Mountain Entrench 56 106 ±218‰ 15/20/27 25/30/34 60 0‰ 297‰

A lone tank breaks infantry caught in the open 858 times in a thousand. It cannot break infantry that has dug in anywhere, including in the open. In mountains it has a 297‰ chance of being thrown back with nothing to show for 30 strength. The same six cases with a Field Battery supporting and two more units adjacent (flankers = 3):

Terrain Their Stance A D They lose You lose They break
Open Hold 145 47 42/45/48 9/11/13 1000‰
Open Entrench 145 61 38/42/45 11/13/16 1000‰
Forest Hold 102 75 27/34/41 14/19/25 1000‰
Forest Entrench 102 91 24/31/38 16/21/27 625‰
Mountain Hold 84 89 22/29/35 18/23/28 1000‰
Mountain Entrench 84 106 20/26/33 20/25/30 239‰

A battery and two adjacent units turn 0‰ into certainty in three of the six cases, and 0‰ into a real chance in the other two, while barely moving the odds — Forest Entrench goes from 71:75 to 102:91, a shift of a third of a point of odds, and from never to 625‰. Positioning is worth more than force. The hard cell remains entrenched infantry in mountains at 239‰ — a bad bet with the whole apparatus applied. That cell is not a bug; it is the answer to "how do you take a mountain?", and the answer is that you do not assault it. Same defender at digIn 2, same supported attack, varying only supply:

Their supply D They lose Their resolve They break
Full 121 18/24/30 60 66‰
Adequate 117 18/25/31 60 127‰
Strained 102 20/27/34 55 653‰
Cut, and encircled 79 23/30/38 25 1000‰

Cutting the road does more than three extra battalions, and it emerges from the interaction of supply, resolve and the break check rather than from any rule that mentions "siege".


24. Contracts on other documents

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

CB-2750 01-game-rules.md MUST provide: a terrain combat profile for every terrain type (CB-1440, CB-1470); Chebyshev adjacency and a canonical row-major tile index; stacking capacity per tile; passability per movement class; city ownership and works level per tile; the observation-level ladder of CB-2310; and, optionally, crossesRiverEdge and elevation (CB-580). It MUST NOT define combat resolution, terrain defence multipliers, or retreat behaviour.

CB-2760 02-units-and-industry.md MUST provide, per class: atk, def, tough, role, retreatClass, bombardRange, bombardShots (CB-150). It MUST NOT define a combat resolution model of its own, MUST NOT express unit durability as hit points (CB-110 makes strength normalised and tough the durability lever), and MUST NOT define terrain, entrenchment, or suppression multipliers — CB-140 replaces suppression with disorder.

CB-2770 10-turn-model.md MUST provide: a Stance per unit with the four values of CB-1510; a totally-ordered creation sequence number; a stable per-unit per-turn attack ordinal keyed positionally rather than by stream cursor (CB-810 depends on this, and it is a hard requirement, not a preference); reaction eligibility and capacity, and the halt of a moving unit when a reaction fires (CB-1780); and the guarantee that combat state changes are visible to units activating later in the same Cascade (CB-270).

CB-2780 12-economy.md MUST provide: a per-unit supply state in its three-value vocabulary and a per-unit flag for occupying a supply source, from which CB-1630 derives the combat supply tier; depot locations for the recovery table of CB-2070; and out-of-supply attrition applied after recovery (CB-2130). It MUST NOT define combat power multipliers for supply (CB-1640) and MUST NOT define an independent repair rate — strength recovery is CB-2070.

CB-2790 13-command.md's CM-150, CM-160, CM-1370 and CM-1410 are satisfied by CB-2250, CB-2260, CB-2210 and CB-1490 respectively. CM-2160's requirement that co-ordinated attacks key off adjacency and timing rather than Formation membership is satisfied by CB-420.

CB-2800 13-command.md MUST provide the Withdraw Reflex state that §12 reads, and the Risk Sanction that pushes it. This document MUST NOT decide when a unit chooses to disengage.

CB-2810 14-victory.md MUST own how combat losses, city captures and rout destruction score.

CB-2820 03-architecture.md MUST provide the counter-based coreHash of CB-800, the canonical state hash that CB-180 contributes to, the determinism lint rules that CB-2700 asserts against, and the golden-master corpus infrastructure that CB-2660, CB-2690 and CB-2730 use.

CB-2830 04-ui-ux.md MUST own the presentation of the attack preview (CB-2390), the condition badge (CB-190), the hostile-tile overlay (CB-2430), the encirclement marking (CB-1410) and the reconciliation line (CB-2440), implementing the content requirements this document states.


Open questions

  1. Is the whole-stack rout the right severity? CB-1420 destroys every unit on a tile that breaks with nowhere to go, which can remove three units in one Clash. It is the mechanic that makes the entire manoeuvre game worthwhile and it is also the largest single swing in the design. CB-2710 bands it at 20–35% of total losses; if the measured share sits above that, the named response is to destroy only the top two defenders and let the rest survive at heavy loss. What is not settled is whether that response would leave encirclement worth attempting at all.

  2. Should a stack that cannot fit its retreat destination be destroyed? CB-1120 says yes, which makes crowding behind a thin line a genuine vulnerability and is the sharpest edge in the document. The alternative — the units that fit retreat and the remainder are destroyed — is more forgiving and much harder to preview honestly, because the preview would have to name which units die. The current rule is chosen partly because it is explainable.

  3. Is two supporters the right number? CB-370 allows two, which makes a three-role combined-arms force reachable and therefore makes the +260 term of CB-560 attainable. One supporter would make three roles impossible and would turn combined arms into a two-role decision, which is simpler and possibly better. There is no data either way; the metric that would settle it is the build share by role in CB-2710.

  4. Does the "not yet activated" support rule read as arbitrary? CB-370's rationale argues that it correctly rewards a battery that stayed put, because staying put lowers Initiative and lowers Initiative means activating later and activating later means being available to support. That is an elegant chain and it is also four inferential steps long. If players experience it as "my gun sometimes cannot support and I cannot tell why", the rule is wrong however good the reasoning is, and the fix is to make support available regardless of activation at the cost of a larger disorder charge.

  5. Air and naval combat are only partially specified here. CB-1330's Detached retreat class and the Naval/Air rows of CB-1450 make the procedure applicable to them, but interception, basing, endurance, submerged concealment and shore bombardment are not designed in this document and the intensity constants have not been calibrated for them. The assault intensity of 60 is almost certainly wrong for an air strike, which should be sharper and shorter.

  6. Is tough this document's stat or the roster's? CB-160 keeps it here as a pure combat divisor. It might be better expressed as a unit size that also drives production cost, transport slots and supply draw, in which case it belongs to 02-units-and-industry.md and this document should read it rather than define its meaning.

  7. Does the once-per-turn disengagement cap create a new exploit? CB-1890 stops one unit absorbing unlimited attacks in a turn, but it also means a screening unit's second attacker gets a Caught Clash at −200 defence, which is usually fatal. A player who screens with a single unit against two attackers therefore loses it outright, which may make screening a two-unit minimum in practice. That might be correct; it was not designed.

  8. Should reactive fire be previewable at all? CB-1840 declines to give a probability because reaction capacity is unobservable, and shows a warning instead. The opposing case is that a player who knows an enemy is in Stance Hold can infer reaction capacity perfectly well, so the warning is coy rather than honest, and the preview should show the reaction as a conditional outcome. The counter-argument is that inferring capacity requires knowing the enemy's Stance, which is only available at observation level L2.