Every Last City Specification — Overview
Every Last City is an original turn-based strategy game in the Empire tradition: you start with a city, cities produce units, units take cities, and the fog hides the rest. It is not a port, a remake, a remaster, or a reproduction of any existing product. It is a clean-room design that takes the idea the genre has been running on for fifty years — which is not anybody's property — and builds a better game on top of it.
This document set is the specification for that game. It is the front door: what the product is, what it owes its players, which document owns which subject, and what every shared word means.
Status: Draft v0.2 · Owner: unassigned · Depends on: docs/design/00-direction.md and
docs/design/01-decision-turn-model.md — binding sources, not requirement documents. No document in
docs/spec/ is a prerequisite for this one; it is the root.
Note on rights. Every Last City is an original work developed clean-room. Mechanics and ideas are not copyrightable — "cities produce units, units capture cities, no diplomacy, fog of war" is a design space, not a possession — while specific expression is, and we take none of it. No asset, number, name, table, or line of text in this specification is derived from another product, and no contributor obtains, installs, decompiles, or reads another game's data files (
docs/design/00-direction.md§2). The roster, terrain set, economy, combat model, turn model, command vocabulary and victory system are ours and are argued on their own terms in the documents that own them. Shipping is not gated on a licence, a partnership, or an expression-replacement pass. What remains is editorial hygiene, not a legal blocker: documents 03–09 were written under the withdrawn premise and still carry inherited citations and terminology that must be scrubbed (Open question 1).
1. What the game is
A player begins with one city on a fogged map. Cities produce units. Units explore, fight, and take more cities. Everyone wants everything and nobody can talk their way out of it. That single loop is the whole game, and its purity is the reason the shape has outlived every technology it was ever built on.
What Every Last City adds is not more loop. It is depth in the places the genre has always been thin:
- Turns that do not make you wait. All players issue orders concurrently against a stable board; the engine then resolves every unit in Initiative order with no input from anyone (10-turn-model.md TM-020). A six-player game costs the maximum of six players' thinking time rather than the sum of it.
- Orders that are policies, not keystrokes. A unit carries a standing job, a set of dials governing when it may fight and how far it may stray, and a reflex layer that suspends rather than destroys its orders (13-command.md CM-200, CM-330, CM-570). Three hundred units become a dozen command objects.
- A front, not a sequence of duels. Combat resolves as a single exchange producing two independent results — what it cost, and whether the line moved (11-combat.md CB-020, CB-030). Units usually survive. Encirclement is not a special case; it is what happens when a broken unit has nowhere to go (CB-040, CB-050).
- A brake on growth you can see, budget for, and beat. Six named economic brakes, each with an in-world referent, exact displayed integers, a forecast, and stated counterplay (12-economy.md EC-020, EC-030). No rule anywhere may reduce a player's output as a function of how much they hold (EC-010).
- A reason to keep scouting after the map is drawn. Terrain is the only thing you can finish learning; everything else is graded knowledge that decays and must be refreshed by putting something at risk (01-game-rules.md §10).
- Winning as a system rather than a checkbox. Fourteen victory Tracks in one uniform shape, combinable at setup, legible as two integers and a countdown at every turn, with published progress, deliberate endgame instruments, and a comeback path (14-victory.md VC-020, VC-030).
Games scale from a two-player skirmish on a small map to many-player wars on very large ones.
Complexity escalates through the Industry ladder — a per-city rating in [1, 4] that gates which
units a city may build and multiplies its output (02-units-and-industry.md US-810, US-820) — rather
than through the player choosing a difficulty of ruleset up front. A player who never leaves Industry
1 has played a complete, coherent, winnable game with eight unit types.
2. Product pillars
These six are what the product owes its players. Every requirement in every document is answerable to them.
OV-010 A requirement in this document set that cannot be traced to a pillar below MUST be justified in its own rationale or removed. Where a requirement and a pillar conflict, one of the two MUST change, and the change MUST be recorded here.
OV-020 The pillar numbers MUST be stable. Other documents cite pillars by number (pillar #3),
so a pillar MUST NOT be renumbered, reordered, or deleted; a withdrawn pillar MUST be replaced in
place and every citation of it re-checked.
- The loop is the product, and nothing is inherited. Cities produce units; units take cities; fog hides the rest; the game is adversarial by default and rewards patience over reflex. Every subsystem in this specification MUST pay for itself against that loop or come out. Correspondingly, nothing is in the design because some other game did it that way — "the original did X" is not an argument, here or anywhere in this set. Where we found a better way, we took it.
- Modern expression, exact rules. The interface changes what a player can say, never what the engine computes. Batch order entry, staging and retraction before commit, proactive warnings, GPU map rendering at any zoom, real onboarding and full accessibility are all obligations — and all of them are obligations on how intent is authored, never on the arithmetic behind it.
- Play anywhere, with anyone. Windows, macOS, Linux, iOS, Android and the browser; correspondence (async) play, real-time online and LAN, hot seat, and solo — all cross-play, all on one deterministic core.
- Radically open. Data-driven content, sandboxed WASM plugins, self-hostable servers, documented file formats, an in-game workshop. Nothing about the game is locked away from the people who play it.
- Built for the long haul. A headless deterministic simulation core, event-sourced games, replays, exhaustive automated testing, and versioned formats with migration paths.
- An opponent worth playing. A strong, legible, honest AI is a headline feature of this product and not a fallback for the times nobody is online. §7 states what that commits us to.
OV-030 Pillar 2 MUST NOT be read as a fidelity obligation. There is no reference implementation to be faithful to; the requirement is that the client and the core never disagree about what the rules compute, which is a determinism property (03-architecture.md AR-030, AR-080), not a compatibility one.
Rationale: the withdrawn pillar 1 made fidelity to another product the highest obligation in the design. It was never asked for by the project owner, it foreclosed every improvement in §3 below, and it made "is this good?" un-askable about half the specification — the answer was always "it is what it is." Replacing it with the loop puts the burden back where it belongs: a rule is in this game because it makes this game better, and it has to say why.
3. The modernization thesis
The genre's weaknesses are well known to anyone who has played it long enough to hit them. They are listed here so that they are fixed on purpose rather than reproduced by accident, and so that every answer has an address.
3.1 What the design fixes
| The genre's known weakness | Every Last City's answer | Owned by |
|---|---|---|
| You spend most of a multiplayer game watching. Strict sequential turns mean a six-player game is five-sixths spectating, and a correspondence game is a network round-trip per unit. | Orders and the Cascade. All players order concurrently against a board that cannot change under them; the engine then resolves every unit in Initiative order with no input at all. One submission per player per turn. | 10-turn-model.md TM-020, TM-030, TM-050 |
| Late-game micromanagement. Three hundred units each demanding an order by turn 200. | Orders as standing policy. A three-level order stack (Posture / Task / Reflex), six Postures, four orthogonal Sanctions, Formations, and a bounded Dispatch that surfaces the seven things actually needing a decision. Interruption suspends an order; it never destroys it. | 13-command.md CM-200, CM-330, CM-570, CM-1640 |
| Opaque anti-snowball economics. Production silently slows as you grow, with no in-world explanation and nothing to plan against. | Six named brakes, each visible by test. Every brake must be nameable in one sentence, show every integer term it consumes, be forecastable from committed state, and admit stated counterplay — and no rule may key off a player's total city count, unit count, income, score or rank. | 12-economy.md EC-010, EC-020, EC-030 |
| Flat attrition combat. Repeated coin-flips at fixed odds; the only question is who has more. | One exchange, two results. Losses come from the odds ratio, breaks from shock against resolve, and the two are allowed to disagree. Combat is not fight-to-the-death; retreat is what creates a front. | 11-combat.md CB-020, CB-030, CB-040 |
| Encirclement needs a special case, and usually doesn't get one. | It has none. "A broken unit retreats" plus "a unit that cannot retreat is destroyed" produces pockets, breakthroughs and the whole operational layer with no surrounded-unit code path. | 11-combat.md CB-050 |
| The mop-up. Everyone knows who won; an hour goes into hunting the last three units across a large map. | Capitulation, Concession, arithmetic elimination, and the Verdict — four named, dignified exits, plus a published Out of Contention status. | 14-victory.md VC-040 (F2, F3), §14 |
| Victory is a checkbox. Chosen in ten seconds at setup, invisible thereafter. | The Track abstraction. Fourteen conditions in one shape — progress, threshold, Vigil, sites — so there is one readout, one combination rule, one estimator, one linter and one AI evaluator. A condition that cannot be stated as two integers at every turn is not admitted. | 14-victory.md VC-020, VC-030, VC-350 |
| The runaway leader is unanswerable except by a hidden tax. | The Vigil. A Track wins only after being held across V consecutive evaluations, in public, at a published address. The leader must be airtight for V turns; the challenger needs one successful attack. |
14-victory.md VC-670, VC-690, VC-730 |
| Exploration ends when the map is revealed. | The Intelligence Picture. Terrain is the only thing you finish learning. Everything else is a graded Contact whose fidelity is set by how close you got, decays with time, and degrades from a tile to a region — refreshable only by putting something at risk. | 01-game-rules.md §10, GR-1200, GR-1260 |
| A big empire is just a small one with more of it. | Two resources and an industrial geography. Works are per-city and transfer on capture; Manpower is a national pool that does not. Which army you can field is dictated by the shape of the empire you hold, and per-city Industry means a frontier town and a rear Arsenal are different places. | 12-economy.md EC-180, EC-290; 02-units-and-industry.md US-830 |
3.2 What the technology fixes
The genre's classic implementations were shaped by the machines of their era. None of those constraints survive, and the differences are worth naming because they change what the product can promise.
| The era's constraint | Every Last City | Enabled by |
|---|---|---|
| A native client for one desktop OS at fixed resolutions | One TypeScript client — WebGL tile map, DOM for everything else — running in the browser and wrapped by Tauri 2 for desktop and mobile | The web platform as a portable runtime |
| Turn passing by email attachment or a shared folder | Cloud async games with push notifications, plus a signed, self-contained turn bundle for the airgapped spirit of play-by-mail | Ubiquitous connectivity, cheap servers |
| Direct TCP hosting: port forwarding, passwords, restart-as-server | Lobby/relay service with reconnection and host migration; LAN discovery retained; fully self-hostable | Cloud infrastructure, NAT traversal |
| Native DLLs for AI players and map generators, loaded unsandboxed | Sandboxed WASM plugins, any source language, stable ABI, deterministic instruction-metered budget | WebAssembly sandboxing |
| Copying files into game directories to install a mod | Mod packages with manifests, dependency resolution, and an in-game workshop | Content addressing, app-store patterns |
| Local per-machine accounts and history | Local-first profiles with optional free cloud sync | Cloud identity done politely |
| Single-threaded turn processing, interruptible mid-resolution because it was slow | The Cascade computes in milliseconds on a worker thread and is played back as animation; no prompt, confirmation or opportunity dialog exists during resolution, in any mode, for any reason | Multi-core CPUs, worker isolates |
| Scripted per-persona AI, difficulty bought with cheats | Layered planning and search, difficulty from search depth and doctrine rather than from hidden information, offline self-play tuning | Modern AI techniques, parallel compute |
| Saves keyed by a game name | Event-sourced saves: initial state plus the order log. Replays, spectating and desync detection come free | Event sourcing, cheap storage |
OV-040 No requirement in this document set MAY be justified by reference to the behaviour of another game. Every threshold, cost, cadence and default MUST carry a design reason stated on its own terms; where the reason is a considered guess awaiting self-play data, the requirement MUST say so and MUST still state a concrete number.
OV-050 This document set MUST NOT contain a flag meaning "check this against another game."
Every previously-flagged item MUST have become either a design decision with a stated rationale or an
entry under a document's ## Open questions section naming the owner who will settle it. A document
MUST NOT carry an unresolved verify-against-the-original marker into review, and the marker
vocabulary MUST NOT be reintroduced under another name.
Rationale: the flag was a promise to check something later against a source this project does not
have and will not obtain (docs/design/00-direction.md §2). Left in place it reads worse than an open
question — it looks like a known gap with a plan behind it, so nobody triages it, and the number it
guards ships unexamined.
3.3 Cross-document discipline
Fifteen documents and 2,761 requirements were written largely by AI agents working one document at a time. That is why this subsection exists: the failure mode of the method is not a bad requirement, it is two documents that are each internally excellent and quietly disagree. The four requirements below are the ones that make disagreement detectable.
OV-060 Every subject in this specification set MUST have exactly one owning document, and the owner MUST be named in that document's scope section. A document that needs a value, formula, or rule another document owns MUST cite it by requirement identifier and MUST NOT restate it as a second normative statement. Where restatement genuinely helps the reader, it MUST be marked non-normative and MUST name the owner, and where the restatement and the owner disagree, the owner governs.
Rationale: a paraphrase is a copy, and a copy drifts. Two normative statements of the same rule produce a specification with no answer to "which is right", and under AI authorship the copy is what gets implemented, because it is the one in the file the agent was working in. The naming notes at the top of 10-turn-model.md, 11-combat.md, 12-economy.md and 13-command.md are this requirement working: each names a word two documents wanted, says who owns it, and binds itself to the owner.
OV-070 Every citation MUST resolve: the cited document MUST exist, the cited identifier MUST exist in it, and the cited requirement MUST say what the citing text claims it says. A requirement identifier MUST NOT be renumbered, reordered, or reused while any citation of it exists; a withdrawn requirement's identifier MUST be retired, never reissued, because a reused number silently redirects an existing citation instead of breaking it (07-modding-content.md's replacement notice retires MOD-920 through MOD-980 on exactly this ground). Citation resolution MUST be checked mechanically, and a dangling or misdirected citation MUST fail the check.
Rationale: a hallucinated identifier is the characteristic defect of this authoring method, and this set has been bitten by one already. It is also the cheapest defect to catch: identifiers are regular, the extraction is a script, and a set that cannot pass its own link check cannot be trusted about anything else it claims.
OV-080 A term defined in the glossary (§9) MUST carry exactly one meaning across the whole set. Where two subsystems want the same word for different things, one MUST choose a different word, and the choice MUST be recorded in a binding naming note in the document that yielded. The settled collisions are: grade means veterancy and nothing else (11-combat.md CB-1600); the three-value supply vocabulary is supply state, not supply grade (12-economy.md); a Contact's quality is its fidelity (01-game-rules.md GR-1260); a Contact's positional extent is its uncertainty area, never a region (GR-1330); Region means only the named public map division with a regional capital (GR-1641); Posture is a unit's standing job and Stance is its per-turn tactical setting (13-command.md CM-330, 10-turn-model.md TM-630); and an AI difficulty level is a Rank, not a Tier, Grade, Class or Band (06-ai.md AI-015).
Rationale: one word with two meanings is not a documentation problem, it is a design problem wearing a documentation costume — it means two subsystems believe they agree and do not. It is also the failure that survives review longest, because each document reads correctly on its own.
OV-090 Every requirement in this set that constrains the behaviour of @everylastcity/core MUST
map to at least one named test, with the requirement identifier embedded in the test title and a
CI job failing the build on any core-constraining requirement with zero covering tests
(03-architecture.md AR-935). A requirement that constrains a surface outside the core — an interface
affordance, a platform obligation, a content format — SHOULD name its verification, and where none
exists the requirement SHOULD say so rather than imply one.
Rationale: §8 explains why this is the load-bearing control in the whole project. If the implementation is written primarily by agents, review is not the safety net — the suite is — and a requirement with no test is a requirement nothing enforces, however carefully it was argued.
4. Non-goals
Stated as flatly as the goals, because a non-goal that is not written down is re-proposed every three months and eventually accepted by someone with a deadline. Each entry names the requirement that enforces it, so none of these is merely an opinion held by this document.
| Not doing | What we do instead | Enforced by |
|---|---|---|
| A fidelity mode, a "Classic mode", or any reproduction of another product. The withdrawn pillar 1 is not coming back in a smaller form. | An original design argued on its own terms, clean-room throughout. | docs/design/00-direction.md §1–§2; OV-030, OV-040, OV-050 |
| An invisible brake on growth. No rule anywhere may reduce a player's output as a function of how much they hold. | Six named brakes, each with an in-world referent, displayed integers, a forecast and stated counterplay. | 12-economy.md EC-010, EC-020, EC-030, EC-040 |
| A mid-resolution prompt. No confirmation, opportunity dialog, or "commit the escort?" interrupt during the Cascade or the Reckoning, in any mode, for any reason. | Everything a prompt would ask is said in advance through Sanctions; where it cannot be, the answer is a richer Sanction vocabulary, not a prompt. | 10-turn-model.md TM-050, TM-1960 |
| Diplomacy or negotiated peace. No treaties, alliances formed in play, tribute, trade, mid-game realignment, or shared armies. | Fixed Teams set at setup are the whole co-operation model: membership never changes, teams share no ownership, production or stacking, and shared vision is a setup flag. | 01-game-rules.md GR-040; 06-ai.md AI-340 forbids emergent AI coalitions for the same reason |
| An always-online requirement. Nothing may ever make an account, a connection, or telemetry a condition of playing. | Guest-first: solo, hot seat, LAN, local saves, local mods and both editors work offline with no account. | 08-services-platform.md SVC-010, SVC-970 |
| Ads or monetization. No advertising, ad SDKs, microtransactions, paid currencies, loot boxes, or paid workshop marketplace. | A workshop that hosts content, and nothing that processes a payment. | 08-services-platform.md SVC-960 |
| 3D. No perspective camera, no isometric rebuild, no third rendering technology. | Exactly two presentation layers: a Phaser 3 WebGL canvas that draws the tile map and nothing else, and DOM for everything else. | 04-ui-ux.md UX-010 |
| MMO ambitions. No persistent shared world, no cross-instance identity or matchmaking, no social platform. | Games of 2 to 8 players; a room is an ephemeral in-memory view onto a durable game, materialised on demand from that game's log. | 01-game-rules.md GR-030; 08-services-platform.md SVC-403, SVC-980, SVC-990 |
| Engine-managed state replication. A networking framework's automatic state-synchronization feature MUST NOT carry game state. | Orders up, events and fog-filtered view deltas down — always, in every mode. Colyseus is used for rooms, matchmaking, presence, connection lifecycle and reconnection, and for nothing else. | 03-architecture.md AR-250, AR-500; 08-services-platform.md SVC-402 |
| A technology tree. No empire-wide tech level, research points, or research. | Per-city Industry in [1, 4], which gives the map an industrial geography instead of a global upgrade path. |
02-units-and-industry.md US-830 |
| A third resource, and per-turn upkeep. No fuel, materiel, ammunition, food or money; no maintenance charged against a unit for existing. | Two resources — Works and Manpower — with Manpower priced once at commissioning, and Reach doing the job a fuel economy would have done. | 12-economy.md EC-180, EC-070 |
| Native code plugins. No unsandboxed DLLs for AI players or map generators. | Sandboxed WASM behind a stable ABI, any source language, with a deterministic instruction-metered budget. | 03-architecture.md AR-060 |
5. The document map
Fifteen documents. Each owns a subject completely and cites rather than restates the rest (OV-060). Counts below are mechanical — bolded requirement identifiers at the start of a line — as of this revision.
| # | Document | Prefix | Reqs | What it owns |
|---|---|---|---|---|
| 00 | 00-overview.md (this document) | OV |
12 | The pillars, the non-goals, the document map, the cross-document discipline, the AI commitments, and the glossary that binds the shared vocabulary. |
| 01 | 01-game-rules.md | GR |
198 | The world every other rules document acts on: the five entity kinds, the map and its coordinates, terrain, movement and stacking, zones of control, cities and the capture procedure, line of sight, fog and the knowledge record, setup and generation, the Intelligence Picture, and the public map furniture — Regions and Landmarks. |
| 02 | 02-units-and-industry.md | US |
223 | What a unit is as data — the attribute schema and closed capability-flag vocabulary — plus the Industry ladder, the production queue, Industry degradation on capture, and the shipped roster of twenty manoeuvre units and three auxiliaries. |
| 03 | 03-architecture.md | AR |
123 | Machinery, not rules: the @everylastcity/core package, the determinism and exact-arithmetic contract, the Order/Event model, the PRNG policy, per-player fog-filtered views, the save/replay container, performance, the headless runner, the WASM boundary, versioning, and the testing strategy. |
| 04 | 04-ui-ux.md | UX |
209 | The client: the canvas/DOM split, the Orders board, the Cascade as a first-class screen, the map, the HUD, command at scale, Industry and economy screens, the combat preview, the Watch and the Board, input, onboarding, accessibility, and mobile. |
| 05 | 05-multiplayer.md | MP |
159 | How order records and observation streams travel — hot seat, live play, correspondence, and the signed turn bundle — plus the wire protocol, authority modes, deadlines and substitution, spectating, fog enforcement and anti-cheat, and self-hosted servers. It changes no rule. |
| 06 | 06-ai.md | AI |
218 | The Staff: the layered decision architecture, the Assay, belief under fog, commit-before-you-see, budgets, the six Ranks and seven personalities, the plugin interface, seat substitution, legibility, the self-play pipeline, the adversarial programme, and the quality gates. |
| 07 | 07-modding-content.md | MOD |
138 | The content boundary — data is safe to load from anywhere, code runs sandboxed behind a trust decision — with the mod package format, content-identity hashes, every data format, asset packs and localization, the WASM ABI, the editors, and the workshop. |
| 08 | 08-services-platform.md | SVC |
126 | The platform: accounts and profiles, cloud save and settings sync, the game service, notifications, statistics built on Standing and Tenure, the simulation farm, telemetry and privacy, self-hosting, and operations. |
| 09 | 09-roadmap.md | — | 0 | Non-normative. Sequencing only: eight phases with exit criteria, the dependency structure, the risk register, and what "done" means. Where it conflicts with any specification, that specification wins. |
| 10 | 10-turn-model.md | TM |
275 | The spine: the three phases (Orders, the Cascade, the Reckoning), Initiative and the total activation order, Stance, reactions, the creation sequence, order evaluation at activation, birth and death mid-Cascade, orders as data in the log, deadlines and stalls, and the fog rules per phase. It owns when orders are evaluated. |
| 11 | 11-combat.md | CB |
283 | The Clash and everything downstream of it: losses and breaks as independent results, bombardment, reactive fire, disengagement, retreat, rout and encirclement, the persistent strength and disorder, terrain and dig-in, grade and supply, sieges, recovery, and the exact pre-commitment prediction. |
| 12 | 12-economy.md | EC |
235 | Works and Manpower, what a city is worth, and the five systems that price conquest — Integration, Unrest, the Administrative Cadre, Reach and War Damage — plus the Levy, Population, the garrison burden, supply states and attrition, razing, Loyalty, and the Ledger phase. It owns the Visibility Test every brake must pass. |
| 13 | 13-command.md | CM |
221 | The order vocabulary: the three-level order stack, six Postures, four Sanctions, Reflexes, Doctrines, Formations and Groups, Postings, Consignments and Requisitions, the interruption model, and the Dispatch. It owns what an order can say. |
| 14 | 14-victory.md | VC |
341 | How a game ends and how ending it becomes part of play: the Track abstraction and its fourteen-condition catalogue, Standing and Tenure, the Vigil, the Ebb, combination and per-player objectives, Warrants and Tallies, the Terms and the Watch, the four dignified exits, and the comeback path. |
Total: 2,761 requirements across 15 documents.
Documents 03 through 09 were drafted under the withdrawn fidelity premise and have since had the reconciliation pass; what remains of that work is recorded in Open question 1 rather than in this table. Documents 01, 02 and 10 through 14 were written after the direction change and have never carried inherited material.
5.1 The binding design documents
Two documents in docs/design/ are binding sources but not requirement documents: they carry no
requirement identifiers, nothing cites them for a value, and no test is written against them. They are
cited for authority over direction, and where a specification contradicts one of them, the
specification is wrong until the contradiction is argued out.
| Document | What it settles |
|---|---|
docs/design/00-direction.md |
The withdrawal of the fidelity premise; the clean-room rules binding on every document and contributor; the improvement mandate ("the original did X" is not an argument); what the design keeps — the pure loop, adversarial by nature, fog, escalating complexity, patience — and the four known weaknesses it must fix on purpose. |
docs/design/01-decision-turn-model.md |
The choice of Orders and the Cascade over interactive activation on a creation-order queue, the three arguments that decided it, what it costs, and two binding amendments: the small game must be able to run the Cascade instantly with no Orders deadline, and initiative ships with three terms rather than nine. |
The remaining files in docs/design/ — units.md, economy.md, combat.md, command.md,
turn-model.md, victory.md and the four AI proposals — are superseded source material. They are
the proposals the specification set was written from, they are retained for provenance, and they are
not binding on anything: where a proposal and its specification differ, the specification governs.
6. Architecture at a glance
One deterministic core, imported unchanged by everything that computes the game. Every other choice in the stack is downstream of that.
┌──────────────────────────────────────────────────────────────────────────┐
│ @everylastcity/client · one TypeScript bundle, three deliveries │
│ browser tab (primary target) · Tauri 2 desktop · Tauri 2 mobile │
│ │
│ Phaser 3 · WebGL canvas ····· the tile map, and nothing else │
│ React 18 · DOM ·············· every other surface (UX-010) │
└────────────┬────────────────────────────────────────────▲────────────────┘
│ │
orders up │ one submission per player per turn, │ views down
(TM-020, │ never a per-unit round trip │ fog-filtered view deltas
AR-240) │ │ and the Cascade's
│ │ observation stream
▼ │ (AR-410, AR-495)
┌─────────────────────────────────────────────────────────┴────────────────┐
│ @everylastcity/server · Node 22 LTS · TypeScript strict │
│ │
│ Fastify HTTP API PostgreSQL 16+ · blob store │
│ Colyseus rooms — rooms, matchmaking, lobby and Orders-phase │
│ presence, connection lifecycle, reconnection, │
│ and nothing else. Never the transport for game │
│ state. (SVC-402, AR-500) │
└──────────────────────────┬───────────────────────────────────────────────┘
│ imports the same package the client imports (SVC-401)
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ @everylastcity/core │
│ │
│ pure TypeScript · no DOM · no Node built-ins · no framework │
│ zero runtime deps outside a pinned pure-TS allowlist (AR-010) │
│ │
│ three mutation entry points and no others (AR-030): │
│ createGame(setup, seed) applyOrder(order) loadFrom(container) │
│ everything else read-only: PlayerView(p), hashes, encoding │
│ │
│ the rules — turn · units · combat · economy · command · victory │
│ Int only (Fixed has no consumers) · seeded PRNG · canonical hash │
│ initial state + the append-only order log IS the game (AR-250) │
└────────────┬──────────────────────────────────────┬──────────────────────┘
▲ ▲
┌───────────┴────────────┐ ┌─────────┴──────────────────────┐
│ @everylastcity/cli │ │ @everylastcity/plugin-sdk │
│ the headless runner │ │ sandboxed WASM: AI players and │
│ and the nightly farm │ │ map generators, hosted by the │
│ (AR-810, AR-970) │ │ client or server, never core │
└────────────────────────┘ └────────────────────────────────┘
TypeScript throughout, strict: true, one language from the tile shader's host code to the
migration scripts. That is not a preference for uniformity — it is what makes one core importable
everywhere, and it is why §2 of 03-architecture.md is the load-bearing section of that document:
TypeScript has exactly one numeric type and it is float64, so determinism is an engineering
discipline here rather than a language guarantee (AR-080, AR-085).
@everylastcity/core is pure. No DOM, no Node built-ins, no Phaser, React, Colyseus or Fastify,
and no ambient clock. It imports identically and behaves identically in a browser tab, a web worker
and a Node process, which is what lets the server import the exact package the client imports rather
than a second implementation of the same rules (AR-010, AR-020; 08-services-platform.md SVC-401,
which prohibits a separate server-side implementation of any rule outright). There is no server-side
rules code, so there is no server-versus-client rules divergence to test for — only arithmetic
divergence, which the cross-platform determinism CI covers (AR-960).
The client is one bundle with two presentation layers. Phaser 3 draws the tile map on a WebGL canvas; every button, table, panel, dialog and readout is real DOM under React 18. The split is strict in both directions, and there is no third UI technology (UX-010). Tauri 2 wraps that same bundle for Windows, macOS, Linux, iOS and Android — a shell around the identical client, never a second client.
The server is deliberately boring. Node 22 LTS, Fastify for the HTTP API, PostgreSQL 16+ as the only database, a blob store, and Colyseus for rooms, matchmaking, presence, connection lifecycle and reconnection. Colyseus's headline feature — schema state synchronization — is explicitly declined (SVC-402). Adopting it would make a framework-pushed snapshot, rather than the order log, the thing clients depend on, and that would destroy replay, spectating, desync detection and mode switching in one move (AR-500).
Data flows one way in each direction. Orders travel up; fog-filtered views and the Cascade's observation stream travel down. The core exposes exactly one read surface per player, filtered structurally rather than by hiding fields on objects it sends anyway, so hidden state cannot be reconstructed from anything transmitted (AR-410, AR-420). The seed never appears in a player view at all, because a client holding it could compute every Clash before making it (AR-460).
The turn model is what makes one core and many transports possible. A turn is one submission per player and one deterministic computation of everything that follows. There is no per-unit round trip, no interactive activation, and no input accepted between the close of Orders and the end of the Reckoning (TM-020, TM-050). Correspondence play and live play therefore differ in exactly one respect — how long the Orders phase stays open — and hot seat, LAN, cloud async and the signed turn bundle are four schedules and carriers for the identical computation (05-multiplayer.md MP-010). A mode switch moves a game between them without touching a rule.
7. An opponent worth playing
Pillar 6 is the project owner's. Advanced AI opponents were named as one of the most important advancements wanted from this project, and most people who play a game of this shape will play it mostly alone — for them the AI is not a substitute for the game, it is the game. That makes the three requirements below product requirements rather than engineering preferences. 06-ai.md discharges all three at length.
OV-100 The AI MUST play the same game as a human, through the same vocabulary, into the same log. An AI position MUST be a seat: its decisions MUST enter the simulation as ordinary order records in the authoritative order log, in the same envelope as any human order, submitted during that seat's Orders phase, and resolved by the same Cascade. It MUST express itself in the Posture and Sanction vocabulary of 13-command.md, MUST NOT be part of the simulation core, MUST NOT be reachable from rules code, and MUST NOT be able to alter any state except through an order the authority accepts (06-ai.md AI-010).
Rationale: this is the design's strongest structural advantage and nobody designed it for the AI's benefit. Because an order is a persistent conditional policy evaluated at activation rather than a stream of keystrokes, the AI and the player write in the same language, into the same log, evaluated by the same code. Three things fall out of that for free. The AI cannot cheat by having a private channel into the engine, because there is no channel. Its turn is auditable and replayable like anyone else's. And an AI that cannot express what it wants to do has found a gap in the player's vocabulary — which is a bug report about 13-command.md, not about the AI.
OV-110 Difficulty MUST come from quality of play, never from hidden information or resource bonuses. No shipped preset MAY grant an AI position a production, economic, movement, vision, combat, supply or victory-side advantage of any kind. An AI seat MUST receive exactly the fog-filtered player view a human seat receives and nothing else (06-ai.md AI-020, AI-030). Every handicap that exists — for any position, AI or human — MUST be an explicitly configured setup option, MUST apply identically to human seats, MUST NOT be bundled into a difficulty level, and MUST be disclosed in three places: the setup screen at the moment it is chosen, the in-game rules review, and the replay header (AI-100, AI-105, AI-110). A full-vision option MAY be offered; where it is, it MUST be labelled a cheat, disclosed, recorded in the game configuration and therefore visible in replays, and off by default in every shipped preset (AI-330).
Rationale: the shallow reason is fairness; the real reason is epistemic. A player who beats a handicapped AI and does not know it has been told something false about their own skill, and the game spent their evening teaching it. Everything else in this design exists to make the player's model of the world correct — the exact combat preview, the published Ebb schedule, visible Integration — and a hidden AI bonus is the same sin as an invisible production tax (12-economy.md EC-010), committed against the player's model of themselves rather than of the economy. Two practical consequences follow: an undisclosed advantage is indistinguishable from a bug, so every unexplained AI success becomes a support ticket; and in any game with more than one human it is a rigged contest.
OV-120 AI strength MUST be measured, not asserted. No difficulty level, personality, or strength claim MAY ship without measurement by a headless self-play pipeline: nightly runs across a matrix of setup parameters, map tiers, seeds and opponents, with the opponent pool including prior shipped versions so that a strength regression appears as a change in result rather than in a review, and deliberately adversarial scripted opponents, at least one per known exploit class. A level whose claimed strength has not been measured MUST NOT appear in the difficulty selector (06-ai.md AI-080; 03-architecture.md AR-970 is the pipeline, and 06-ai.md §17 and §20 own what is measured and what gates a promotion).
Rationale: "the AI is strong" is the single easiest false claim in this genre to make and the hardest for a player to check. Self-play alone is not enough either — it measures an agent only against opponents that share its blind spots, and it will report perfect health while a cheap repeatable exploit is live, which is why the adversarial roster is part of the requirement rather than a nice-to-have. The same farm serves as the engine's endurance soak, so this obligation costs infrastructure the project already needs.
8. Built by AI agents
This is worth stating plainly because it changes what the specification has to be. The implementation will be written largely by AI agents. That is not a caveat; it is a fact about the method that several requirements in this set exist to accommodate, and pretending otherwise would leave those requirements looking like unexplained fastidiousness.
The consequence that matters: verification is the safety net, not review. Human review of AI-written code is real but shallow and intermittent; the test suite runs on every change, every time, and does not get tired on the fourth similar file. Anything an agent can get subtly wrong therefore needs a mechanical check, and coverage of rules code is a gate rather than a metric (03-architecture.md AR-990). Five disciplines follow, and each of them is a requirement somewhere:
- Small modules with explicit contracts. The core exposes exactly three mutation entry points and nothing else can change state (AR-030); the workspace dependency graph is enumerated and enforced mechanically, so an upward or lateral import is a build error rather than a convention (AR-020). An agent working inside a module cannot reach past its edges by accident.
- Types as compiler-enforced documentation.
strictplusnoUncheckedIndexedAccess,exactOptionalPropertyTypesandnoFallthroughCasesInSwitch; noanyand no@ts-ignorein the core; nominal branded identifiers so passing aCityIdwhere aUnitIdbelongs is a compile error; orders, events and rejections as exhaustively-checked discriminated unions, so adding a member without handling it everywhere fails the build (AR-055, AR-245). The most likely defect in AI-authored rules code is a new case handled in the four places the agent noticed and silently defaulted in the fifth — exhaustiveness converts that from a behaviour change into a build failure. - Requirement-to-test traceability. Every core-constraining requirement maps to a named test carrying its identifier, and a CI job publishes the index and fails on any uncovered requirement (AR-935, OV-090). A reviewer — or an agent — goes from a requirement to its evidence in one step.
- Plain text only. Every artifact this project defines is diffable: the specifications, the content formats, the canonical projections of the save container, the AI parameter manifest. There is no binary editor output, no format that requires a tool to read, and no place a change can hide from a diff.
- Fail loudly, never default silently. A violated internal invariant throws a typed error rather than falling back to a plausible value; overflow throws, and saturation is banned outright, because a saturated result is a plausible-looking wrong number that will survive review (AR-055, AR-080). A silent fallback hides a defect until it reaches a player's save file, and under AI authorship it hides it from the only reviewer that reliably runs.
This document set was written the same way, which is why §3.3 exists. Fifteen documents drafted largely one at a time by agents will not fail by producing a bad requirement — they will fail by producing two documents that are each internally excellent and quietly disagree about what a word means or what a number is. OV-060 through OV-090 are the mechanical checks for that failure, and they are the specification-level equivalent of the code-level disciplines above: one owner per subject, citations that resolve, one meaning per term, and a test behind every claim.
9. Glossary
Every term below has exactly one meaning across the whole set (OV-080). The owning document is named for each; where this glossary and an owning document disagree, the owning document governs and this entry is a defect to be fixed.
9.1 The turn — 10-turn-model.md
| Term | Meaning |
|---|---|
| Orders phase | The first of a turn's three phases: every player issues and revises orders concurrently, at their own pace, blocked by nobody, against a board that cannot change under them. Bounded by a deadline in live play, by days in correspondence play, and by nothing at all in solo and two-player games. (TM-020, TM-330, TM-340) |
| Cascade | The second phase, which takes no input from anyone: the engine walks every unit in Initiative order and resolves each unit's order against the world as it stands at that instant. Computed in milliseconds, played back as a fog-filtered animation. No prompt, confirmation or opportunity dialog exists inside it, in any mode. (TM-020, TM-050, TM-1190) |
| Reckoning | The third phase: end-of-turn bookkeeping in a fixed nine-step order — combat aftermath, economy, repair, production completion, clocks, disruption clearing, command bookkeeping, victory evaluation, hashing. Also input-free. Together with the Cascade it is a pure function of the state at the close of Orders. (TM-020, TM-040, TM-1930) |
| Initiative | The integer score that sorts the activation order, recomputed at the start of every Cascade and frozen for its duration. A sort key, never a resource: it is not spent, stored, accumulated or transferred. A player sees their own units' Initiative exactly during Orders, and never an opponent's. (TM-890, TM-900, TM-920, TM-370, TM-380) |
| Stance | A unit's per-turn tactical setting, one of exactly four — March, Advance (default), Hold, Entrench — trading Initiative, movement, the right to initiate combat, reaction capacity and defensive treatment against one another. Never a Posture. (TM-630, TM-640, TM-650) |
| creation sequence | The number every entity draws at birth from one monotonically increasing global counter shared by all entity kinds and all players. Unique for the life of the game, immutable, never reissued after destruction. It confers no advantage of its own and exists to be the terminal tiebreak that makes every ordering in the set total. (TM-810, TM-820, TM-880; 01-game-rules.md GR-050) |
| activation | A unit's single turn to act within a Cascade. Exactly one per unit per turn: the unit spends its movement allowance, resolves its order, and yields, with every reaction it provokes fully resolved before the next activation begins. The frozen, sorted list is the activation roster and a unit's place in it is its activation index. (TM-1140, TM-1170, TM-1190, TM-1200) |
9.2 Command — 13-command.md
| Term | Meaning |
|---|---|
| order stack | The three levels every unit carries — [0] Posture, [1] Task, [2] Reflex — of which the topmost non-empty level executes. Popping a level never disturbs the levels beneath it, so an interruption suspends a plan rather than destroying it. The depth is fixed at three. (CM-200, CM-210, CM-220, CM-230) |
| Posture | A unit's standing job, the bottom of its order stack: exactly six — Hold, Garrison, Screen, Survey, March, Attach — plus a deliberate Idle state. Never the per-turn tactical setting, which is Stance. (CM-330) |
| Sanction | One of exactly four dials every Posture carries — Engagement, Leash, Risk, Seize — by which a player pre-decides what a unit may do on its own. Each is individually editable, shown in plain language, revocable, and logged. The Sanctions are what make committing orders before seeing the turn a skill rather than a gamble. (CM-570, CM-580) |
| Reflex | The top of the order stack, pushed only by the engine and only for enumerated causes: Withdraw, Replenish, Regroup, ReturnToPost, TakeCover, in that priority. At most one at a time; a higher-priority Reflex replaces rather than queues. It pops when its cause clears and the suspended plan resumes untouched. (CM-290, CM-300, CM-1400) |
| Doctrine | A named, player-created bundle of a Posture template, all four Sanction values, an optional Formation role and an optional Stance override, applicable in one action to a unit, a selection, a Formation, or as a unit type's default. Editing one never retroactively retasks units already created under it. (CM-770, CM-780, CM-790, CM-800) |
| Attention Event | A deterministic emission from the simulation core saying something needs a person: a class, a cause key, the affected entities, the integer terms S, T and A, a plain-language summary, and offered resolutions. Automation never depends on them — a player who never reads one gets identical unit behaviour. (CM-1610, CM-1630) |
| Dispatch | The client's ranked, deduplicated, bounded presentation of Attention Events: at most seven items, everything below the cut collapsed into a digest, each item carrying a one-action resolution and a snooze, sticky across turns until resolved or retired. Surfacing the seven things that need a decision out of three hundred units is the more important half of the command design. (CM-1640, CM-1650, CM-1660, CM-1670) |
9.3 Combat — 11-combat.md
| Term | Meaning |
|---|---|
| Clash | The single exchange that is the only mechanism by which one unit reduces another's strength by attack, bombardment or reaction. It resolves in one step — no rounds, no repeated sampling, no procedure whose cost depends on the units involved — which is what makes the pre-commitment odds readout exact. (CB-010, CB-020) |
| strength | A unit's persistent condition on a 0–100 scale that means the same thing for every unit in the game: the proportion of it remaining. It scales the unit's power linearly and lowers its resolve. A unit reaching 0 is destroyed immediately. It is the hardest thing to learn about an enemy, available only at the top Contact fidelity. (CB-100, CB-110, CB-120, CB-130) |
| disorder | A unit's persistent accumulated shock, 0–100, subtracting point-for-point from its resolve. It is the sole representation of a unit having been shaken — there is no separate suppressed or pinned state — and it recovers on a schedule a player can plan around. (CB-100, CB-140) |
| break | A side's shock reaching its resolve. For a defender it means giving ground; an attacker's break is a Falter and the attack is thrown back. Breaks are computed from shock against resolve while losses are computed from the odds ratio, and the two results are allowed to disagree. (CB-030, CB-090) |
| retreat | What a broken side does: a move to one adjacent tile chosen by a published deterministic formula that prefers avoiding further contact, then falling back toward home, then cover, then company, with the lowest tile index as the tiebreak. Retreat is what creates a front, and it is why most Clashes end with both units alive. (CB-040, CB-1310, CB-1320) |
| rout | A break with no legal retreat destination, which destroys the broken stack. One of the only two ways a unit is destroyed in combat, the other being strength reaching 0. (CB-040, CB-090) |
| encirclement | Not a rule. It is what the conjunction of "a broken unit retreats" and "a unit that cannot retreat is destroyed" produces, with no code path anywhere naming surrounded, pocket or cut off. Pockets, breakthroughs and the whole operational layer come from those two sentences. (CB-050) |
| grade | Veterancy, and nothing else. Four levels — Green, Trained, Veteran, Elite — derived from a unit's xp and never stored separately, raising power and resolve and, more importantly to a planner, lowering volatility so an experienced formation's outcomes cluster. The word is reserved: it is not a supply level and not a Contact quality. (CB-170, CB-1600, CB-1610; OV-080) |
9.4 Economy and industry — 12-economy.md, 02-units-and-industry.md
| Term | Meaning |
|---|---|
| Works | A per-city integer income, accrued into that city's Yard each Ledger phase and spendable only on that city's own production queue. Never transferable between cities, players or pools by any mechanism. It transfers in full to a new owner on capture, which is how conquest pays immediately. (EC-190, EC-200, EC-210) |
| Manpower | A single integer national pool per player — no per-city stock, no storage, no transport, no routing — priced once at commissioning and never charged again. It does not transfer on capture: you captured their factories, not their sons. Together with Works it gives every unit a two-dimensional price, so the army a player can field is dictated by the shape of the empire they hold. (EC-220, EC-070, EC-180) |
| Integration | A per-city integer in [0, 100] measuring how far a conquered city's administration is actually yours, resolved once per Ledger phase by a signed delta whose every term is displayed. With the Cadre it is the anti-snowball engine, and it is a price on a city you chose to take rather than a penalty for how much you hold. (EC-900, EC-920, EC-930) |
| Industry | A per-city rating in [1, 4] — Depot, Workshop, Foundry, Arsenal — determining exactly two things: the multiplier on that city's Works output, and which unit definitions the city may build. It is how the design delivers escalating complexity, and it is why there is no technology tree: a frontier town and a rear Arsenal are different places. (US-810, US-820, US-830, US-840) |
| brake | Any mechanism that slows a player's growth. There are exactly six, and each must pass the four-clause Visibility Test: named with an in-world referent statable in one sentence, numbered with every integer term displayed where it applies, forecastable from committed state alone, and counterable by an action available that turn and nameable in the forecast. No brake may key off a player's total city count, unit count, income, score, Standing, Tenure or rank. (EC-010, EC-020, EC-030, EC-040) |
| Administrative Cadre | A unit — built in a production queue, occupying a tile, moving, escortable, killable — that accelerates Integration where it is stationed, with an escalating price per Cadre owned. It is the design's answer to the invisible tax: the same rate expressed as an object you paid for, chose a destination for, and can lose. Nothing about this brake is hidden; it walks. (EC-1250, EC-1260, EC-1280) |
| supply state | A unit's or city's position in the three-value supply vocabulary — Supplied, Extended, Isolated — derived from Reach and hashed as supplyState. Formerly called supply grade; renamed because grade is reserved for veterancy. Combat reads it as a four-value supply tier. (EC-160 and 12-economy.md's binding naming note; 11-combat.md CB-1620, CB-1630) |
9.5 Victory — 14-victory.md
| Term | Meaning |
|---|---|
| Track | A victory condition in the one uniform shape every condition must take: an integer progress value, an integer threshold, and a Vigil. Fourteen of them, combinable at setup. A condition that cannot be stated as two integers at every turn is not admitted to the catalogue — which is what buys one readout, one combination rule, one estimator, one linter and one AI evaluator. (VC-350, VC-360) |
| Vigil | The number of consecutive Seals — an integer in [1, 12] — a Track must remain satisfied before it wins. Failure resets the counter to zero rather than decrementing it, which creates the asymmetry the whole endgame rests on: the leader must be airtight for V turns, the challenger needs one successful attack. (VC-670, VC-680, VC-690) |
| Standing | A player's integer score right now, computed at every Seal from exactly five components and no others: cities held, Landmarks held, Seats held, Works output, and army in supply. (VC-500, VC-510, VC-560) |
| Tenure | The monotone integral of Standing over every Seal so far, accruing floor(Standing / 100) per Seal. It never decreases and is never reset, spent, transferred or reduced, and it integrates current state rather than peak state — so it means the same thing every time it is consulted: the player who has been ahead the longest. Every ambiguity in the victory system resolves to it. (VC-610, VC-620, VC-640) |
| the Seal | The single instant per turn at which victory is evaluated — the last step of the turn, after the Cascade and after every economic step has settled — executing a fixed sequence of steps in a fixed order. Victory is never evaluated during the Cascade, between two activations, or on a change of city ownership, which is what makes winning independent of activation order. (VC-200, VC-210, VC-230) |
9.6 Knowledge and the map — 01-game-rules.md
| Term | Meaning |
|---|---|
| fog | The three-state per-player per-tile knowledge model: Unexplored (terrain and contents unknown), Explored (terrain known permanently, contents remembered and possibly stale), Observed (known and current). A player's view may contain nothing about a tile or unit they have neither observed now nor observed before, in any form — including a derived index, a timing, or the absence of an expected value. (GR-1120, GR-1130, GR-1180) |
| Contact | A record in a player's Contact Register of one foreign unit, City or Installation — never a reference to the true entity, and updated only by observation or decay. One per subject, at most 4,096 per player. Contacts persist and decay after the subject dies out of sight, because intelligence about a unit that no longer exists is worth exactly as much as intelligence about one that does, until you go and check. (GR-1200, GR-1210, GR-1220, GR-1250) |
| fidelity | How much a Contact tells its owner: an integer [0, 3] — Trace, Sighting, Identification, Assessment — set by how close the observer got, capped by forts, altitude and concealment, and set rather than raised on re-observation, so a distant glimpse can lower it. Never a grade. (GR-1240, GR-1260, GR-1270, GR-1280) |
| uncertainty area | A Contact's positional extent: the set of tiles within a Chebyshev radius of a projected centre, minus tiles the player has since observed without finding it there. Position degrades from a tile to an area as a Contact ages, and a Contact is dropped when its area grows too large or is fully excluded. Never a "region". (GR-1320, GR-1330, GR-1350) |
| Region | One of M named, published, disjoint divisions of the map, fixed at generation, partitioning every tile and therefore every City, each anchored on and named for a regional capital. Public from turn 1, independent of fog. It confers nothing: no movement, supply, production, combat, vision, stacking or command effect may read Region membership. It exists so a victory Track can say "hold the north". (GR-1641, GR-1642, GR-1643) |
| Landmark | One of L named Cities carrying a Landmark marker, publicly located from turn 1 and worth extra Standing. The marker confers nothing mechanically — no production, defence, vision, stacking, supply, command or movement term anywhere may read it. A Landmark is hard to take because the generator seeds it onto good defensive ground that every player can see, not because the marker helps. (GR-1646, GR-1647, GR-1648) |
| player view | The single fog-filtered read surface the core exposes per player (PlayerView(playerId)); RefereeView is full state and is available only to the authoritative host and replay tools. Filtering is structural, not advisory: the payload is built from what the player is entitled to, so hidden state cannot be reconstructed from an omitted field, an inferable count, or a timing. Identity disclosure is projected from the Contact at its fidelity, never from the true entity with fields removed. (03-architecture.md AR-410, AR-420, AR-450, AR-460) |
9.7 The engine and the game object — 03-architecture.md, 01-game-rules.md
| Term | Meaning |
|---|---|
| Core | @everylastcity/core: the pure deterministic simulation package that computes the whole game, imported unchanged by the client, the server, the CLI and the plugin SDK. No DOM, no Node built-ins, no framework, no ambient clock, no runtime dependencies outside a pinned pure-TypeScript allowlist. Exactly three mutation entry points; everything else is read-only. (AR-010, AR-020, AR-030) |
Int |
An integer-valued number with the invariant Number.isSafeInteger(v). This is the representation the rules use, exclusively — every rules document independently requires integer-only arithmetic. It covers every rules quantity, with fractions expressed per-mille as the project-wide convention. Division never uses /; it uses named helpers that state their rounding, and ratios are compared by cross-multiplication. (AR-080(a)) |
Fixed |
The project's single fixed-point representation, signed Q16.16, floor-rounded always, overflow throwing rather than saturating. It currently has no consumers, deliberately: it exists so that a future subsystem genuinely needing sub-per-mille resolution has one sanctioned answer instead of inventing a second scale. Introducing a Fixed value into rules code requires the owning document to state why per-mille integers are insufficient. (AR-080(b)) |
| Order | A declaration of intent submitted by a client — human UI, AI plugin or server relay — accepted only during the Orders phase of the turn it names, validated and then either applied or rejected with a typed reason. Orders are the only path by which intent reaches the simulation, and initial state plus the append-only order log is the game; everything else is derived. (AR-240, AR-250, AR-260, AR-030) |
| Event | A description of a state change, emitted by the Cascade and the Reckoning and never by order submission. Events are the only way state changes are described to the outside — no consumer may infer state by other means — and they are derived, never authoritative: the log records what players committed and that the turn closed, never the Cascade's results, so a replay divergence trips immediately at the hash instead of hiding inside the recording. (AR-240, AR-250; 10-turn-model.md TM-060) |
| Team | An optional fixed grouping of players assigned at setup, whose membership never changes during play. Members are not hostile to one another for any purpose — capture, zones of control, movement blocking, reactions — but share no ownership, movement rights, stacking allowance or production, and have no shared army. Shared vision is a setup flag (teamVision, default on), not a rule. Teams are the entire co-operation model: there is no diplomacy, no negotiated peace, and no mid-game realignment. (GR-040, GR-030) |
Open questions
The direction-change reconciliation is complete. Questions 1 through 6 of the previous draft are resolved and are recorded here rather than deleted, so a reader of v0.1 can see they were closed rather than forgotten. Documents 03 through 09 have had the pass: no document in this set now cites another game as authority, carries a verify-against-the-original flag, or refers to a "Classic mode" except to prohibit it. 06-ai.md is rewritten from the four AI design proposals and carries 217 requirements. The stale citations are gone. The "grade" collision is resolved — the word is reserved for veterancy, supply grade became supply state, and the Contact term became fidelity. The "Depot" conflict is resolved, with 02-units-and-industry.md owning the Forward Depot cost and 12-economy.md owning only its Reach. And 01-game-rules.md §12.2 now defines Regions and Landmarks as public map features, which were the two unmet contracts blocking 14-victory.md's Hegemony and Keys Tracks.
What remains is not editorial: none of this has been played. Every number in the seven rules documents is a first estimate by a designer who has never seen the system in motion, which is a different and larger risk than the one this question used to describe. 09-roadmap.md §2.4 owns it.
Browser parity. Browser builds are single-threaded unless cross-origin isolation headers are served, and the largest map tiers are the stress case. The engineering budget belongs to 03-architecture.md; the product decision — cap the map tier in the browser, or ship slower turns there and say so — must be answered before the browser client is marketed as equal to the desktop one. The Cascade helps here (one bounded computation per turn rather than continuous interactive resolution) but does not settle it.
Map-layer accessibility. The DOM gives screen-reader support almost free for dialogs, tables and reports, which is much of this game — but the tile map is a WebGL canvas with no accessibility tree of its own. Pillar 2 promises full accessibility, so the map needs an explicit parallel representation. 04-ui-ux.md owns the design; this is an obligation the stack created, not one it removed.
Numeric representation — chosen; only the choice's review remains open. 03-architecture.md AR-080 fixes exactly two representations for simulation state and rules code, and the rules documents are written to be exact by construction under it: 10-turn-model.md TM-100 and 11-combat.md CB-060 require integers throughout their own subjects, and 12-economy.md EC-060 forbids itself the PRNG. What remains open is 03-architecture.md's own question of whether Q16.16 or a decimal 10⁶ scale is the better single choice, which MUST be settled before the first rules module merges, because changing it afterwards changes every hash.
Dependency surface. A TypeScript client and server pull far more third-party code than a single engine vendor did, and every package is a supply-chain and determinism risk inside a project whose whole guarantee is same-log-same-result. What is the policy — pinned lockfiles, an allowlist, a hard zero-runtime-dependency rule for
packages/core? Owner: 09-roadmap.md and 08-services-platform.md.Tauri on mobile. The client stack is settled — TypeScript throughout, Phaser for the tile map, DOM for every other surface, Tauri shells for desktop and mobile. What remains open is the mobile leg: iOS and Android ship through a system webview rather than a native engine export, which is the least proven part of the plan. Revisit if webview map performance or app-store review makes it untenable; the fallback is a thin native shell around the same core and UI, never a second client.
Launch scope. Which multiplayer modes and which map tiers constitute the MVP, and does the shipped roster launch complete or Industry-tiered by phase? Recommendation: solo plus hot seat plus the full shipped set plus correspondence play at launch; real-time online a fast follow. The Industry ladder already delivers escalating complexity within one set, so a phased roster would buy little and cost the balance work twice.