# Logic standard: game simulation and code

Status: draft, 2026-10-20. Written for dolphin4 by a read-only subagent. The owner decides every item in `OPEN-DECISIONS.md`.

This standard is for JCOM and Conquer, the browser RTS in TypeScript and Three.js. It covers:

- how the simulation is built;
- what every unit must be able to do;
- how to add a mechanic.

Designers, ChatGPT and other outside builders should design to it.

Companion files in this folder:

| File | What it is |
|---|---|
| `logic-standard.json` | Machine-readable list of unit capabilities, unit states and rules-file conventions. |
| `OPEN-DECISIONS.md` | Places where sources disagree, with values, file:line and a recommendation. |

Sibling standards:

- `../standards_units/` covers models, rigs, animation clips, visual damage and dimensions.
- `standards_maps` covers maps.

This file does not repeat them.

## How to read the citations

Every rule cites where it comes from. A rule marked **PROPOSED** has no source yet and needs owner approval. Path prefixes:

| Prefix | Root |
|---|---|
| `GAME:` | Live game tree, `M:\z4yin-work-overflow\hermes-share\codex-lanes\asset-split\sol-maps\work` (`skirmish/`, `data/`). This is the tree served at port 4380. |
| `SKILL:` | `D:\z4yin\z4yin-files\.claude\skills\game-logic-rules\SKILL.md`. A byte-identical copy is at `hermes-share\codex-lanes\game-logic-rules\SKILL.md`. |
| `ADR:` | `hermes-share\codex-lanes\ships-fix\work-apx9\docs\DECISIONS.md`. This is a lane copy of the game repo's ADR log. Two other lane copies are the same size (45,100 bytes). |
| `SHARE:` | `M:\z4yin-work-overflow\hermes-share` |
| `INBOX:` | `D:\z4yin\.inbox\dolphin4` |
| `MEM:` | Owner rulings recorded in `C:\Users\MaxGlasser\.claude\projects\D--z4yin-z4yin-files\memory\` |
| `BRIEF:` | This lane's brief from dolphin4, 2026-10-20 (owner 16:31: "standards for everything"). |

Line numbers were read on 2026-10-20. The tree changes daily.

---

## 1. Architecture at a glance

| Layer | Where | Rule |
|---|---|---|
| Simulation (authoritative) | `GAME:skirmish/src/sim.ts`, `GAME:skirmish/src/core/`, `GAME:skirmish/src/mechanics/<area>/`, `nav/`, `ai.ts`, `fog.ts`, `collision.ts`, `simHash.ts` | Decides all game state. It has no wall clock, no `Math.random` and no rendering (`GAME:skirmish/src/sim.ts:55-56`). |
| Rules data | `GAME:data/rules/*.json` (138 files), and also `GAME:skirmish/src/data/*.json` (24 files) | Every tunable value lives here (ADR-010, `ADR:221-252`). See OD-L02 for the two locations. |
| Rosters | `GAME:data/roster/*.json` | Unit ids, costs and stats per faction. Unsourced values carry "PROPOSED" or "Assumed default (ADR-010)" notes in their source map, for example `GAME:data/roster/jslc.json:1572`. |
| Network | `GAME:skirmish/src/net/` (`lockstep.ts`, `relay.ts`, `desync.ts`, `rejoin.ts`) | Deterministic lockstep over a LAN WebSocket relay. The relay never runs game logic (`ADR:481-492`). |
| Render | `GAME:skirmish/src/main.ts`, `render/`, `models.ts` | Reads sim state. It never writes it, except by issuing orders (`GAME:skirmish/src/sim.ts:56`). |
| HUD | `GAME:skirmish/src/ui/`, `hud.ts`; design system in `SHARE:codex-lanes/hud-design` | See section 9. |

The stack is TypeScript 5.7, three ^0.170, Vite ^6 and Vitest ^2.1 (`GAME:skirmish/package.json`). The game is one Vite app, `skirmish/`. It keeps internal module boundaries and has no monorepo (`ADR:60-75`).

**Concepts are binding, implementations are not.** Torch's game concepts are normative, including determinism and 30 Hz lockstep. His code is reference only (`ADR:42-58`).

**One sim, any front end.** The game is finished in TypeScript first (ADR-026, `ADR:548-556`). Unity may later host the same TS sim in V8, with no logic fork (ADR-025, `ADR:527-546`). C++ is a parked proposal behind a benchmark gate (`SHARE:CPP-SIM-CORE.md`; `SHARE:ADVICE-sol-cpp.md`). Write the sim so it can be bundled without the DOM or three.js (`ADR:535`). OD-L12 notes one place that breaks this today.

---

## 2. The simulation

### 2.1 Fixed tick

- The rate is `TICK_HZ = 30` (`GAME:skirmish/src/sim.ts:310`, ADR-004 superseded to 30 Hz, `ADR:97-111`).
- Durations are stored in **seconds** in rules data. They are converted to ticks with `secondsToTicks` (`GAME:skirmish/src/sim.ts:316-318`), which rounds.
- Speeds are stored per second and converted once (`GAME:skirmish/src/sim.ts:575-585`).
- A gameplay job that repeats runs inside `Sim.step` and counts ticks. Never use `setInterval` for it (`GAME:skirmish/src/core/MECHANICS-tick.md`, "How to change it").
- Use `periodDue(tick, periodTicks)` and `ticksForPeriodMs` (`GAME:skirmish/src/core/tick.ts:67-81`).
- Presentation timers may use the browser clock: HUD paint, panel refresh and autosave polling. They must not change sim state (`GAME:skirmish/src/core/MECHANICS-tick.md`).

**Order of work in `Sim.step`** (`GAME:skirmish/src/sim.ts:3268-3420`). A new system must name the slot it runs in:

1. `tick++`, then admit due commands (`applyDueCommands`).
2. Garrison safety, stealth timers, tunnel heal and rehost.
3. World module, construction (`stepConstruction`), economy (`stepEconomy`).
4. AI controllers in ascending player id (`:3287`), then EW prep and the sight cache.
5. Per unit, in id order (`unitsInIdOrder`, `:3094`): cooldown, `stepUnit`, vision observer, target grid.
6. Garrison entries, tunnel attach, UNREP, transport, tractor, then **collision**.
7. HELIOS defence, weapon flights, power spacing, subsystem fire and flood.
8. Kill pass, economy toggles (PR, wear, upkeep), compaction of dead units.
9. Ops, subterranean, factions, docks and sea gate, air module, carrier ops, native runway, flight finish, aircraft heading.
10. Script, EW, weapon effects, kill pass, portal doors, naval win, `publishEvents`.
11. In `finally`: `stepPowers` once per tick (`:3413-3416`).

### 2.2 Determinism

These rules come from `SKILL:16`, `GAME:skirmish/src/sim.ts:55`, `GAME:skirmish/src/simHash.ts:9-12` and `ADR:42-58`.

- Never call `Math.random`, `Date.now` or `performance.now` in sim code.
- Iterate in a stable order. Units walk in numeric id order, and AI runs in sorted player id order. Never let a `Map` or `Set` insertion order decide an outcome unless the insertion order is itself deterministic.
- Sort strings by UTF-16 code units. Never use `localeCompare` (`GAME:skirmish/src/simHash.ts:12`).
- Do not accumulate floating-point values across ticks without a fixed step.
- Every field that can change a future tick must be serialized and included in `simHash`. Hashed floats keep their raw IEEE-754 bits (`GAME:skirmish/src/simHash.ts:9-11`). Mechanic state lives on `Unit` or in a hashed section. See `GAME:skirmish/src/mechanics/economy/MECHANICS-economy.md`, "Fixed-tick order", last paragraph, and `GAME:skirmish/src/mechanics/aistate/README.md`, "Root hash integration".
- Presentation must not write hashed state. A HUD write to hashed state caused a real multiplayer desync (`SHARE:CPP-SIM-CORE.md`, "Desync" row).
- When a system is off, its step must return immediately. It must not change the hash (for example `stepSubModule`, `GAME:skirmish/src/core/MECHANICS-tick.md`, edge cases).

### 2.3 Seeded RNG

- The only gameplay random source is `Sim.rng()` (`GAME:skirmish/src/sim.ts:1607`). It draws mulberry32 through `mulberry32Step` in `GAME:skirmish/src/core/rng.ts:43-50`.
- The state is a uint32, masked with `>>> 0` after every add.
- `setSeed` masks the seed and seeds the AI controllers (`GAME:skirmish/src/sim.ts:1600-1603`).
- Visual effects and civilians keep their **own** streams, so an effect cannot move the unit hash (`GAME:skirmish/src/core/MECHANICS-rng.md`, "How to change it").
- The increment and the 2^32 scale belong to the algorithm. They are not balance knobs and do not go in rules JSON (`GAME:skirmish/src/core/MECHANICS-rng.md`, "Data").
- Change the algorithm only with a replay-version bump and a new stored 5,000,000-draw checksum (`GAME:skirmish/src/core/MECHANICS-rng.md`).
- Known defect: `ai.ts` still uses the unmasked add. See OD-L07.

### 2.4 Replay and save versions

There are three separate numbers. Do not confuse them (`GAME:skirmish/src/core/REPLAY-VERSION.md`):

| Number | Value | Where | Bump when |
|---|---|---|---|
| `REPLAY_STREAM_VERSION` | 2 | `GAME:skirmish/src/core/replayVersion.ts:7` | The logical RNG or command stream changes. |
| `simHash` `SCHEMA` | 8 | `GAME:skirmish/src/simHash.ts:34` | Hashed sections are added or changed. |
| `saveFormatVersion` | 1 | `GAME:data/menu/menu-save.json` | Saved-state layout changes. Add a `migrateSave` arm. This number is part of the rules hash, and raising it refuses every existing save. |

### 2.5 Lockstep

- Commands are plain data: `{tick, playerId, seq, kind, payload}` (`GAME:skirmish/src/sim.ts:561-567`). There are 24 command kinds (`:443`).
- Input from a player or the AI becomes a command. The sim admits it on its tick. A mouse callback never mutates the sim. Second-row actions must go through the queued dispatcher (`GAME:skirmish/src/mechanics/secondrow/MECHANICS-secondrow.md`, "This adapts at39's ...").
- Network: a command issued on frame F runs at F + `turnDelay`. `turnDelay` is 2, checksums run every 30 frames, and `tickHz` is 30 (`GAME:data/rules/multiplayer.json:11,12,19`). A mismatch pauses the game and shows the desync report (`GAME:skirmish/src/net/desync.ts`). A dropped seat becomes AI (`GAME:skirmish/src/net/lockstep.ts:3-5`).
- Local input delay: `rules.lockstep.inputDelayTicks` is 0 (`GAME:data/rules/game-rules.json:302-305`). See OD-L06.
- Reference: "1500 Archers on a 28.8" (`SKILL:51`).

---

## 3. Rules as data

Sources: ADR-010 (`ADR:221-252`), `SKILL:14`, `GAME:data/rules/schema.ts:1-8`.

- **"ALWAYS VARIABLES, NEVER HARDCODE WHENEVER POSSIBLE."** This covers every gameplay scalar, radius, duration, probability, cost, cooldown, multiplier and capacity. All of them live in JSON under `data/`, and TypeScript reads them.
- Each value cites a `source`:
  - a real-world public figure, with the citation (for example `GAME:data/rules/unit-joints.json:676`, CH-53E NATOPS);
  - or the Generals equivalent scaled to our map (for example `GAME:skirmish/src/data/weapons-rules.json`, `weaponsByUnitType.Rifleman.source`);
  - or the words **"Assumed default (ADR-010)"** or **"PROPOSED"**, recorded in `docs/RULES-DEFAULTS-PENDING.md` (examples: `GAME:data/roster/jslc.json:1180,1572`; `GAME:data/roster/penguins.json:1294-1295`).
- Units go in field names or in a `unit` key. Use metres, seconds, m/s, degrees, credits, tonnes and fractions 0 to 1. For example: `durationSec`, `rangeM`, `speedMps`, `payloadTonnes`, `minHpFraction`. `TunableNumber` is `{value, unit, description}` (`GAME:data/rules/schema.ts:10-14`).
- Convert metres to tiles **once**, at the sim adapter, using the map's metres per tile (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 2). World scale is 5 m per tile (`GAME:data/rules/world-scale.json`; README `GAME:skirmish/README.md`, "1 tile = 5 m").
- Validate rules on load and freeze them. Reject bad values with a thrown error. Example: `readProfile` in `GAME:skirmish/src/mechanics/weapons/index.ts:26-33`. `aistate` rules are "validated and recursively frozen, with no module-global mutation API" (`GAME:skirmish/src/mechanics/aistate/README.md`).
- Every mechanic has an on/off switch in `GAME:data/rules/mechanics-toggles.json` or its own `enabled` flag. An unconfigured match keeps the old behaviour (`GAME:skirmish/src/mechanics/aistate/README.md`, line 3).
- **Do not ask the owner about balance numbers.** Pick an assumed default, mark it, and move on (`ADR:229`, `ADR:247-249`).

Known gaps:

- Unit base stats are still TS literals in `sim.ts`. See OD-L04.
- 82 of 159 rules files have no `source` key. See OD-L03.
- Rules live in two folders. See OD-L02.

---

## 4. Code layout and hooks

Sources: `SKILL:15`, `SKILL:19`.

A new mechanic lives in `skirmish/src/mechanics/<area>/`:

| File | Holds |
|---|---|
| `types.ts` | Plain-data state and payload types. They must serialize. |
| `rules.ts` | Loads, validates and freezes the rules JSON. |
| `logic.ts` | Pure functions over state, rules and ports. No imports from `main.ts` or the renderer. |
| `ports.ts` | The narrow interface the mechanic needs from the sim. Live example: `GAME:skirmish/src/mechanics/economy/` (`index, logic, ports, rules, types`). |
| `index.ts` | Public API. |
| `MECHANICS-<name>.md` | Doc for the C++ port (section 10). |

**Shared files:** `sim.ts`, `main.ts`, `models.ts` and the UI files take only small marked hook lines:

```ts
// HOOK <lane code>: <purpose>
```

- Inline form: `/* HOOK aus9: per-order part intent. */` (`GAME:skirmish/src/sim.ts:440`).
- Today `sim.ts` has 151 hook lines, `main.ts` has 157 and `models.ts` has 16 (counted 2026-10-20).
- Never reformat or refactor a shared file.
- Adapt to the existing authority rather than adding a parallel controller. The integrated mechanics say which module stays authoritative. For example: "ship helm and aircraft remain authoritative" (`GAME:skirmish/src/sim.ts:33`), and "Original pure helper modules remain available ... they are not hooked as parallel engine controllers" (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 4).

**Coding style:**

- TypeScript strict, ES modules.
- TSDoc on every exported type and function (`SKILL:19`). Comment the *why* and the units. See `GAME:skirmish/src/core/rng.ts` and `tick.ts` for the house style.
- Name constants that are not tunables, for example `AUDITED_BROWSER_PERIOD_MS` in `GAME:skirmish/src/core/tick.ts:10`.
- No GLB paths in logic. Resolve models through the asset registry's authoritative version (`SKILL:20`).

---

## 5. Unit behaviour contract: what a unit must be able to do

The owner asked: "what does a unit need to be able to do". This section answers from the live sim. Each capability lists its data and its authority. Status is **live**, **partial** or **PROPOSED**. The same list is in `logic-standard.json` → `unitCapabilities`.

### 5.1 Identity

- `id` (number, stable), `player` (-1 is neutral), `type` (`UnitType`, `GAME:skirmish/src/sim.ts:417-419`) and `rosterId` (data variant) (`GAME:skirmish/src/sim.ts:622-640`).
- Internal ids and save identities are stable and must not change. Display names go through wrappers (`SHARE:ARCHITECTURE-BRIEFING.md:51`).
- The model comes from the asset registry. There is one model per unit (`SKILL:20`).

### 5.2 Orders and commands (live)

- Order kinds (`GAME:skirmish/src/sim.ts:439`): `move`, `attack`, `attackMove`, `attackGround`, `guard`, `guardHold`, `guardAir`, `patrol`, `capture`, `rally`, `gather`, `returnCargo`, `follow`.
- Orders queue on `unit.orders`, and Shift adds to the queue (`GAME:skirmish/README.md`, Controls).
- `stop` and `scatter` are commands.
- A replacement order or Stop clears pursuit, patrol and yield state. Queued orders survive a temporary yield (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`; `GAME:skirmish/src/mechanics/aistate/README.md`).
- Contained units cannot change stance (`GAME:skirmish/src/mechanics/aistate/README.md`).

### 5.3 Movement (live, partial for naval turning)

- Paths come from A* or the navgrid over passability per layer (`GAME:skirmish/src/nav/`, `GAME:skirmish/src/mechanics/navgrid/MECHANICS-navgrid.md`).
- Ground vehicles accelerate and brake, steer a bounded chassis, and reverse to a nearby rear goal. Infantry may step while turning. The orientation budget is spent once per tick (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 1).
- A friendly blocker yields: it moves aside and returns, with no teleport.
- Patrol runs as a loop or as ping-pong.
- A formation moves at its slowest member's pace (`Unit.formationPace`, `GAME:skirmish/src/sim.ts:658`).
- Ships use the ship helm. Aircraft have their own controller (`mechanics/locomotor/aircraft.ts`, `mechanics/flight`).
- Naval turning circles and `attackPathing` are **off** (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 4).
- Numbers are in `GAME:skirmish/src/data/locomotorRules.json`. They are "declared gameplay assumptions" (same doc, paragraph 5).
- Real turn rates, accelerations and formation algorithms are **pending research**. See section 13.
- Damage slows a unit through `speedFactor` per body state (`GAME:skirmish/src/data/weapons-rules.json:161-190`). Flooding halves speed (`Unit.flooding`, `GAME:skirmish/src/sim.ts:688`).

### 5.4 Attack (live)

- Each unit has a weapon profile (`GAME:skirmish/src/mechanics/weapons/index.ts:8-13`):
  - `damageType`;
  - domain flags `antiAir`, `antiGround`, `antiShip`;
  - `minRangeTiles`, `clipSize`, `clipReloadSec`, `preAttackDelaySec`;
  - `delivery` (`instant` or `projectile`);
  - splash;
  - `deliberateOnly`.
- Damage type times armour class uses the SAGE-style matrix (`GAME:skirmish/src/mechanics/contracts.ts:20-50`).
- **Stance** is one of `aggressive`, `guard`, `hold` or `passive` (`GAME:skirmish/src/mechanics/aistate/types.ts:16`).
- **Fire discipline** is one of `free`, `tight` or `hold` (`GAME:skirmish/src/mechanics/weapons/index.ts:5`). Labels are in `GAME:skirmish/src/data/tacticalCommands.json`. Hold suppresses automatic fire, but an explicit Attack still fires.
- Auto-acquire uses the acquisition range. Attack-move anchors at acquisition, has a chase timeout, and rejects a target until cooldown and forward progress (`GAME:skirmish/src/mechanics/aistate/README.md`).
- Owner rulings (`SKILL:26-27`):
  - Turrets slew independently, and the hull turns only when no mount can bear (`GAME:skirmish/src/data/weapons-rules.json`, `shipTurrets.source`).
  - Defensive weapons fire automatically, while Tomahawk and SM-3 are deliberate. See OD-L16 on Hold fire.
- Part targeting: `Order.aimMode` and `subsystemTargetId` (`GAME:skirmish/src/sim.ts:440`; `mechanics/parttarget`).
- A shot reveals the shooter to its target (`Unit.lastAttackerId`, fog.json `attackerRevealSec`, `GAME:skirmish/src/sim.ts:701`).
- Anti-overkill and focus fire are **partial**: `FocusFireRegistry` is not integrated (`GAME:skirmish/src/mechanics/aistate/README.md`).

### 5.5 Transport and containment (live)

- One attachment contract covers all of them: `containedIn`, `slungBy`, `transportManifest` (`GAME:skirmish/src/sim.ts:645-646`).
- Rules (`GAME:skirmish/src/mechanics/transport/transport-rules.json`):
  - slot cost by size class: infantry 1, light 2, medium 4, heavy 8;
  - per-transport seats, deck slots, maximum class, payload tonnes, fire ports and sling;
  - load and unload range and interval;
  - unload scatter;
  - casualty survival chance.
- A transfer between hosts mutates the existing passenger record. It never spawns or teleports (`GAME:skirmish/src/mechanics/transport/TRANSFER.md`).
- Garrison: `garrisonedIn`, capacity and evacuation of damaged buildings (`GAME:skirmish/src/garrison.ts`, `SHARE:...work/docs/mechanics/SYS-garrison.md`).

### 5.6 Carrier operations (live, partial)

- Each carrier keeps a book (`CarrierBook`): its inventory, CAP point and named cooldowns (`GAME:skirmish/src/carrierOps/index.ts:21-32`).
- A CAP flight's state is `deck`, `station`, `return` or `recover`. It tracks `fuel01` and `ammo01` (`GAME:skirmish/src/carrierOps/index.ts:33-41`).
- Second-row named actions (`GAME:skirmish/src/mechanics/secondrow/MECHANICS-secondrow.md`):
  - LCAC out;
  - Scout E-2D;
  - Scramble;
  - Strike, which is atomic: the whole package or nothing.
- **LCAC in** is unavailable (same doc).
- The deck machine (`airops`) owns elevators, catapults, landing queues and well-deck gates.
- Ships start fully stocked (`GAME:data/rules/carrier-ops.json` `metadata.governingPolicy`).
- Aircraft that run out of ammunition return to base, reload by the missing fraction and keep the pending command (`GAME:skirmish/src/mechanics/flight/MECHANICS-flight.md`).

### 5.7 Production and construction (live)

- Each facility holds a FIFO queue of up to `maxQueue` items (`GAME:skirmish/src/mechanics/production/MECHANICS-production.md`).
- The cost is paid per tick against the cumulative rounded price. A queue head without money waits.
- Hold keeps progress and spending.
- Cancel refunds credits spent × `refundFraction`. Destruction discards the queue with no refund.
- The exit is a physical path from inside the factory. The rally applies on exit (`GAME:skirmish/src/mechanics/scaffold/MECHANICS-scaffold.md`).
- Construction advances one integer progress step per tick. An incomplete structure neither fights nor produces (`GAME:skirmish/src/sim.ts:3305`).

### 5.8 Economy and supply (live)

- Supply workers carry integer boxes. They gather from a finite warehouse and deliver to a depot, which credits the box value once (`GAME:skirmish/src/mechanics/economy/MECHANICS-economy.md`).
- Oil derricks pay out on a periodic timer. A capture reward is paid once per object.
- Stock is conserved: warehouse stock + cargo + delivered value stays constant.
- Base income is 0 on supply maps. Legacy maps keep 50 credits per second.
- Upkeep, wear and PR legitimacy run behind toggles (`GAME:skirmish/src/sim.ts:3353-3363`; `GAME:data/rules/mechanics-toggles.json`).
- PR never locks weapons (`GAME:skirmish/src/mechanics/contracts.ts:11`).

### 5.9 Damage and death (live)

- `hp` and `maxHp`, `armorClass` and optional `subsystems` (`GAME:skirmish/src/sim.ts:622-716`).
- Body states are `pristine`, `damaged`, `reallyDamaged` and `rubble`. Their thresholds and effects are in `GAME:skirmish/src/data/weapons-rules.json:161-190`. See OD-L08: these thresholds differ from the visual thresholds in standards_units.
- Fire and flood spill hull damage at a fixed sub-rate (`GAME:skirmish/src/sim.ts:3337-3350`).
- EMP sets `disabledUntil`. Infantry can be `surrendering`, and are then captured, not killed.
- Death goes through `killUnit`. Dead units are compacted after the kill pass.

### 5.10 Vision, stealth and capture (live)

- Sight range, fog and the sight cache (`GAME:skirmish/src/fog.ts`).
- Camera coverage and HQ see through fog. Weather may only reduce ranges (`SKILL:24`; `MEM:hq-ideal-conditions-by-default.md`).
- Stealth, detector pulses, capture and tunnels: `GAME:skirmish/src/mechanics/infiltration/`.

### 5.11 Veterancy (live)

- Progression state, kill credit and power bonus (`GAME:skirmish/src/mechanics/progression/MECHANICS-progression.md`). Rules are in `GAME:skirmish/src/data/progression-rules.json`.

### 5.12 Unit states

There are two kinds of state. Keep them apart:

- **Sim states** decide behaviour and are hashed. They are the `Unit` fields listed in `logic-standard.json` → `simStates`:
  - `alive`;
  - order queue, `engageId`, `stance`, `fireDiscipline`;
  - `containedIn`, `garrisonedIn`, `embarkedIn`;
  - `airHold`, `standby`, `construction`, `disabledUntil`, `surrendering`, `flooding`;
  - body damage state.
- **Presentation states** pick animation poses. They live in `GAME:data/rules/unit-states.json`:
  - carrier aircraft: `hangar-deck`, `flight-deck`, `airborne`, `landing`;
  - land aircraft: `parked`, `taxi`, `airborne`;
  - vehicles: `travel`, `deployed`, `combat`;
  - ships: `moored`, `underway`, `action-stations`;
  - buildings: `idle`, `active`, `alert`.

  The pose is derived from sim facts through the `situation` table (`GAME:data/rules/unit-states.json:1295`). It never feeds back into the sim. Clip conventions belong to `../standards_units/`.

---

## 6. AI

- Computer players live in `GAME:skirmish/src/ai.ts`. They run once per tick, in player id order. Their tunables are in `GAME:data/rules/ai.json` and `difficultyScale`.
- AI uses its own seeded stream, `aiRng` (see OD-L07).
- The AI issues the same commands a player does. Build lists per match, faction and general come from `mechanics/matchai` (`GAME:skirmish/src/ai.ts:6`).
- Unit-level autonomy (stance, guard leash, smart targeting) is in `mechanics/aistate`. An unconfigured unit keeps the old nearest-target behaviour (`GAME:skirmish/src/mechanics/aistate/README.md`).
- Open AI findings from the ChatGPT audit are input, not canon (`SHARE:logic-inputs/chatgpt-project1-20261020/6ad64b1e_jslcnc_game_logic_audit_2026-10-19.md`, section 3). Examples: defence reserving units that immediately retreat, and economy ignoring harvesters already in production.

---

## 7. Clean room

1. **Never copy EA code or pseudo-code** (`SKILL:17`).
2. **Game-code agents never read Thyme or EA engine source.** Generals behaviour may be described in our own words from:
   - public modding documentation (the Generals Wiki, C&C Labs, the guides in `SHARE:MODDING-RESOURCES.md`);
   - INI field names;
   - play.

   The source of this rule is BRIEF (dolphin4 2026-10-20) and `C:\Users\MaxGlasser\.claude\jobs\44b57189\tmp\lanes\movement-research\BRIEF.md:27`.

   This **conflicts** with `SKILL:17` and `SKILL:45`, which name the EA GPL source as a reference. Twelve existing `MECHANICS-*.md` files cite EA source files. See OD-L01.
3. Cite the INI field or public page you learned a behaviour from, in the mechanic doc.
4. Retail and mod assets are private test placeholders. Never commit or publish them (ADR-005, `ADR:113-130`). The owner treats the game as personal, non-commercial use, and asset licence is not a filter (`MEM:game-personal-use-licensing.md`). That covers assets only. It does not relax the code clean-room rule.

---

## 8. Naming and identity (owner rulings)

- The factions are **JSLC**, **AoI** and **Penguins**. `coalition`, `network` and `directorate` are legacy internal keys only (ADR-007, `ADR:152-174`). The product is "JCOM and Conquer" (`ADR:516`).
- Ships take their real class name. Individual names come from the approved pool with JNS, JAF, JDF or JSF prefixes. Ensigns fly on masts only (`SKILL:28-29`).
- The HQ admiral is **Bill "Maccabee" Adama**, everywhere. J.D. Vance is a different, real person and is never renamed (`MEM:admiral-is-bill-adama.md`).
- **There are no banned names.** Never add a name-scrub step. Rename only on a specific owner request (`MEM:no-banned-names.md`).
- Internal ids and save identities never change. Display names go through wrappers (`SHARE:ARCHITECTURE-BRIEFING.md:51`).
- No emojis. Use Unicode line glyphs only (ADR-016, `ADR:349`).
- The concept test is the tiebreaker: "If it is done in real life and it is terrible, then we include it" (ADR-009, `ADR:196-219`).

---

## 9. HUD and GUI boxes

- Every new or changed box is built from the HUD design system (`SHARE:codex-lanes/hud-design`). Its levels are tokens, then primitives, components, layout and screens. In the same change, the box adds itself to the component registry and to the reference page (`SKILL:21`).
- No inline styles. No hard-coded colours or px sizes.
- Run `node tools/check.mjs` in `hud-design` and paste the output into the checkback. You can point it at a game file (`hud-design/README.md`, "How it nests").
- HUD code reads sim snapshots and issues commands. It never writes hashed state (section 2.2).
- Every UI must work at 1440x900 and on iPhone 15 (393x852), including standalone PWA mode (`SHARE:ARCHITECTURE-BRIEFING.md:53`).
- The design system's own README says "PROPOSED ... not wired into the game yet". See OD-L13.

---

## 10. Mechanic documentation (for the C++ port)

Each mechanic folder has a `MECHANICS-<name>.md` (`SKILL:19`). The live examples are `economy`, `production` and `scaffold`. Each file covers:

1. what the mechanic does;
2. the data fields and their units;
3. how to change it;
4. the per-tick order of operations;
5. edge cases;
6. clean-room references (section 7);
7. tests.

---

## 11. Testing

- Every mechanic has Vitest tests (`SKILL:18`). Record the suite counts before and after, with no new failures.
- Test real behaviour, not flags. Measure actual displacement, lateral movement and return, queued-order survival, and replay hashes. Checking only that a flag is set does not prove behaviour (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, "New steps").
- Every mechanic that touches state needs a **determinism test**:
  - same commands give the same hash;
  - save, load, then advance gives the same hash as the original (`GAME:skirmish/src/mechanics/economy/MECHANICS-economy.md`, last section).
  - Existing suites: `tests/core-determinism.test.ts`, `core-rng.test.ts`, `hash.test.ts`, `lockstep.test.ts`, `replay.test.ts`.
- **Mission and campaign tests:** `tests/missions-*.test.ts` and `tests/campaign*.test.ts`. The browser playthrough is `npm run test:campaign-play`. It plays every mission, never uses Record win, and fails on a console error. A subset is never reported as full acceptance (`GAME:skirmish/README-campaign-playthrough.md`).
- **Where tests run:**
  - Only through `C:\lanes\dolphin4\game\lane-test.ps1`.
  - It runs on SV3 (`--maxWorkers=3`) or SV4 (`--maxWorkers=2`). That is one run per box and at most 5 workers in total.
  - Never on DSK, never on SV2.
  - A newer request from the same lane supersedes its waiting one, with exit code 98.
  - Prefer targeted files while iterating, and run the full suite once before DONE.
  - Sources: `C:\lanes\dolphin4\game\lane-test.ps1:1-9`, `INBOX:lanes/design-repo/TOOLS-SV3-SV4.md:52`, `MEM:dsk-off-limits-full-use-sv2-4.md`.
  - Local Vitest is clamped to 1 worker (`GAME:skirmish/vitest.config.ts`).
- **Prove it in the running build.** Take before and after screenshots from the 4380 build or from a dev server on your own copy. Report `what_made_the_difference` and `new_steps` (`SKILL:30`). An effect is "done" only once it is observed running (ADR-019, `ADR:405-418`).

---

## 12. Integration and patch process

1. Work in an isolated lane copy with junctioned `node_modules` (`SKILL:58`). Never edit `public/` or `node_modules/`. Assets come as `public-add/` lists (`SKILL:34`).
2. Deliver:
   - `PATCH.diff`: `diff -ruN` of the baseline against `skirmish`, plus a data diff against `_base/data`;
   - `REPORT.md` with `new_steps` and `what_made_the_difference`;
   - tests;
   - screenshots.

   Source: `INBOX:launch/brief-au99.md:26-29`.
3. Shared files show only hook lines. Reviewers check this and the rest of the review checklist (`SKILL:32-41`).
4. An independent model reviews the patch. Only PASS turns an item green (ADR-019, `ADR:405-418`). Review each packet as it lands. Never leave finished patches waiting (`SKILL:41`).
5. After review, the integrator applies the patch to the live tree and deploys, keeping a backup of every replaced file. Then they test the running build on desktop and iPhone 15, and fix forward or roll back (`MEM:push-then-test.md`).
6. Never kill in-flight runs unasked (`MEM:let-running-runs-finish.md`).

---

## 13. Pending research (not yet rules)

The movement research lane is **open**. Its results are proposals until the owner adopts them (`C:\Users\MaxGlasser\.claude\jobs\44b57189\tmp\lanes\movement-research\BRIEF.md`). It covers:

- land, naval and air formation algorithms;
- HPA*, JPS, navmesh and flow fields;
- deterministic local avoidance (RVO, ORCA);
- multi-layer and large-footprint movement;
- real turn rate, acceleration and tactical diameter for named ships, ground vehicles and aircraft;
- the behaviour of the Generals Locomotor fields, from modding docs only;
- scaling real values for play.

Its outputs will be `MOVEMENT-DESIGN-NOTE.md` and `movement-rules.proposal.json`. Until then, `locomotorRules.json` values stay "declared gameplay assumptions".

Other non-canonical inputs:

- `SHARE:logic-inputs/chatgpt-project1-20261020/DEEPRESEARCH_d154960d_Shared_RTS_Unit_Logic_Architecture...md`: command, snapshot and event APIs, auto-fire and target selection.
- `DEEPRESEARCH_b16cf0b9_...Grouping_Formations...md`.

---

## 14. NEW MECHANIC CHECKLIST

Copy this list into the lane brief and tick each item in the checkback.

- [ ] **Owner concept named.** The mechanic is quoted from an owner ruling, canon or the gap list (`codex-lanes/gap/*/GAP-*.md`). It passes the concept test (ADR-009).
- [ ] **Clean room.** Behaviour is described in our own words, from public modding docs, INI field names or play. No Thyme or EA source was read or copied. The reference is cited.
- [ ] **Folder.** `src/mechanics/<area>/` has `types.ts`, `rules.ts`, `logic.ts`, `ports.ts` and `index.ts`.
- [ ] **Rules JSON.** Every number is in data, with units in the key, and a `source` for each value or group. Unsourced values say "Assumed default (ADR-010)" or "PROPOSED" and are added to `RULES-DEFAULTS-PENDING.md`. There are no literals in logic.
- [ ] **Rules validated and frozen** on load. Bad data throws.
- [ ] **Toggle.** It has an `enabled` flag or a `mechanics-toggles.json` entry. When off, the hash is unchanged.
- [ ] **Tick slot named.** The doc names where in `Sim.step` it runs, and why it runs before or after collision and the kill pass.
- [ ] **Commands.** Player and AI actions arrive as queued `Command`s and are validated for ownership and enums. No mouse callback mutates the sim.
- [ ] **Determinism:**
  - only `Sim.rng()` or its own seeded stream;
  - no wall clock;
  - id-ordered iteration;
  - no `localeCompare`;
  - all future-relevant state serialized and in `simHash` (bump `SCHEMA` when you add a section).
- [ ] **Authority.** It reuses the existing authority (helm, aircraft, transport manifest, `followPath`, damage pipeline). It adds no parallel controller.
- [ ] **Hooks.** Shared files carry only `// HOOK <code>: <purpose>` lines. Nothing is reformatted.
- [ ] **Unit contract.** It says which capabilities in section 5 it adds or changes and which `Unit` fields it touches.
- [ ] **Model.** It uses no GLB path. The model resolves through the registry.
- [ ] **HUD.** Any box is built from `hud-design` and added to its registry and reference page. `node tools/check.mjs` output is pasted.
- [ ] **Owner standing rulings** are respected (`SKILL:22-29`): HQ ideal conditions, cameras see through fog, bow-out berthing, independent turrets, defensive auto-fire, real-class ship names.
- [ ] **TSDoc** is on every export. `MECHANICS-<name>.md` is written (section 10).
- [ ] **Tests:**
  - behaviour tests that measure outcomes;
  - a determinism and save/load hash test;
  - mission tests when a mission uses it;
  - suite counts before and after;
  - run through `lane-test.ps1` only.
- [ ] **Proof.** Before and after screenshots from the running build. `what_made_the_difference` and `new_steps` are in `REPORT.md`.
- [ ] **Deliver** `PATCH.diff`, the data diff, `public-add/` (if any) and `REPORT.md`. Request an independent review.

---

Signed 2026-10-20 16:52 EDT (observed `date` before writing). Author: read-only subagent of dolphin4 (aqv9_opu_5.5_md). The subagent is not separately registered.
