Faction Architecture Inventory

This inventory captures the active faction boundary for the current pre-alpha faction rollout. It records what exists today, names the temporary compatibility shims, and points later phases at focused checks. It does not define the final faction catalog shape.

Compatibility Boundary

Faction work is pre-alpha and does not preserve compatibility with old replay artifacts, persisted match-history replay payloads, compact snapshot versions, or old protocol payloads. Later phases may break those formats when they add explicit faction identity. Preserve current live gameplay behavior for the existing faction unless a later phase explicitly changes it.

Active Faction Boundary Sources

This document owns the active faction lifecycle taxonomy and the current catalog-id status table. server/crates/rules/src/faction.rs owns catalog facts, loadouts, and ability availability. server/src/lobby/faction_validation.rs owns lifecycle admission. docs/design/protocol.md owns wire vocabulary and payload shapes, and docs/design/server-sim.md owns the Game API and sim boundary wording.

plans/archive/faction/* files are historical-only evidence from the earlier faction rollout. They are not active checker inputs and must not be treated as lifecycle policy unless a future plan explicitly copies a rule back into an active design document. Guard scripts may check that the archive exists and is named as historical, but they must not read archived phase files as the source of current faction truth.

Catalog Id Statuses

Lifecycle status is explicit and separate from catalog existence:

Faction id or pathStatusCurrent lifecycle policy
kriegsiaplayableDefault faction for missing non-replay requests. Supported by normal human lobby, quickstart/debug starts, AI seats, dev starts, self-play defaults, replay/branch records, match-history replay, post-match replay, spectator metadata, and local prediction when version/build metadata is compatible.
ekatplayableHuman-selectable through normal lobby faction selection. Explicit playable validation accepts it for start/replay-capable contexts, and schema-2 replay/branch records may carry it. Public AI seat creation has no faction selector and still defaults to kriegsia; current local prediction is disabled when the local player is ekat.
phase2_empty_fixturetest-fixture-onlyCatalog and loadout exist for explicit Rust/test fixture coverage. It is rejected by normal lobby, quickstart, AI, replay, branch, dev scenario, self-play, match-history, and post-match paths unless the caller uses the TestFixture validation context or a direct lower-level sim test helper that deliberately owns the fixture.
plans/archive/faction/*historical-onlyArchived phase plans, handoffs, and lifecycle matrices are not active faction policy and are not checker lifecycle inputs.

Current Entity Identity

Runtime identity is still global EntityKind. The current roster has 21 global kinds: 10 units, 9 buildings, and 2 resource nodes. Server rules own the stable ids in server/crates/rules/src/kind.rs; protocol mirrors expose the same string ids in server/crates/protocol/src/lib.rs and client/src/protocol.js.

The current production catalog is in server/crates/rules/src/defs.rs:

Temporary compatibility shim policy: direct global kind checks are approved in the current rules catalog, protocol adapters, setup/loadout code, AI, dev scenarios, command execution, production, economy, combat, world-query helpers, and tests. New production files that introduce direct current-faction kind checks should either use the faction catalog API or be added to scripts/check-faction-assumptions.mjs with a short reason. High-risk approved files have a direct special-case count ratchet so newly added current-faction checks inside those large files are also visible during review. The ratchet is an inventory review tool, not approval to expand Kriegsia, Ekat, or fixture special cases casually.

Current Economy Shape

Steel, Oil, and Supply are the only player resources in snapshots, compact snapshots, replay analysis, match history replay artifacts, and the HUD. Start-map resources are still only Steel and Oil nodes. Current starting values are STARTING_STEEL = 75, STARTING_OIL = 0, STARTING_WORKERS = 4, with quickstart resources set to 99_999 Steel and 99_999 Oil.

Compact snapshots encode resources as fixed scalar slots: tick, Steel, Oil, Supply used, and Supply cap. Spectator/replay player_resources use the same Steel/Oil/Supply fields. Later generic resource work must be a separate plan; this faction plan keeps the current resource payload shape. Approved direct-resource modules are listed in docs/design/balance.md under the faction economy contract. Future direct Steel/Oil/Supply references outside those owners should either route through catalog-aware cost/loadout helpers or update that approved inventory deliberately.

Current Starting Loadout

The standard Kriegsia match start is defined by the kriegsia.standard faction loadout in server/crates/rules/src/faction.rs and assembled by server/crates/sim/src/game/setup.rs: each player gets one completed City Centre, four Workers in a ring, nearby Steel/Oil resource clusters, starting Steel and Oil, and supply from the City Centre. Unknown non-empty faction ids receive no catalog loadout, starting entities, starting Steel/Oil, or Kriegsia supply credit; lifecycle owners must validate before building a Game. Debug quickstart adds human-only extra buildings, combat units, resources, debug path overlays, and an inert enemy mortar corner for inspection.

The standard Ekat match start is defined by the ekat.standard faction loadout: one completed Zamok and one Ekat hero, with no starting Steel/Oil or Supply requirement. The phase2_empty_fixture.scout_depot loadout starts one Depot and one Scout Car for explicit fixture tests only; catalog existence does not make that id product-playable.

Replay starts reconstruct from recorded per-player PlayerStartingLoadout records. Replay validators reject missing loadouts, records for unknown players, faction mismatches, empty loadout ids, and loadout ids that do not exist in the player’s faction catalog. Global starting Steel/Oil constructors remain compatibility helpers for tests and debug starts rather than replay/lifecycle reconstruction APIs.

Current Tech Tree

Workers build City Centre, Depot, Barracks, Training Centre, R&D Complex, Factory, and Gun Works. City Centre trains Workers. Barracks trains Riflemen and Machine Gunners. Factory trains Scout Cars, Tanks, and Command Cars. Gun Works trains Mortar Teams, Anti-Tank Guns, and Artillery.

Research unlocks live in server/crates/sim/src/game/upgrade.rs and client descriptors in client/src/config.js: Methamphetamines, Anti-Tank Gun Unlock, Tank Unlock, Artillery Unlock, Command Car Unlock, and Mortar Autocast.

Current Ability Surface

Current ability ids are Charge, Smoke, Mortar Fire, Point Fire, Breakthrough, Ekat Teleport, Ekat Line Shot, and Ekat Magic Anchor. The Rust faction catalog in server/crates/rules/src/faction.rs is authoritative for ability id, carrier, target mode, range, cooldown/charge, resource cost, queueability, autocast support, and command-card affordance metadata. client/src/config.js is the checked client projection, while server/crates/sim/src/game/services/ability_orders.rs, server/crates/sim/src/game/ability.rs, and server/crates/sim/src/rules/projection.rs own execution and projection hooks. Protocol ability vocabulary and compact codes remain mirrored through server/crates/protocol/src/lib.rs and client/src/protocol.js.

Current Client Command Cards

The command-card renderer is local JS in client/src/hud_command_card.js; faction-sensitive build, train, research, and ability descriptors are driven by the checked catalog mirror in client/src/config.js. Kriegsia and Ekat command ids are namespaced by faction, unknown valid ids fail closed to an empty catalog, and fixture catalogs remain test-only. node tests/hud_command_card.mjs is the focused command-card guard, and node scripts/check-faction-catalog-parity.mjs compares client-exposed descriptor data against the Rust catalog dump.

Current AI Coupling

AI is Kriegsia-only through the public lobby seat flow. The AI decision layer assumes Workers gather Steel/Oil, City Centres anchor bases and expansions, Barracks/Factory/Gun Works drive production, Tanks influence oil demand, and Steel/Oil/Supply budgets are fixed fields. Public addAi requests do not accept a faction id and always create Kriegsia AI seats; non-Kriegsia AI support needs an explicit AI phase.

Current Prediction And WASM Coupling

Prediction/WASM assumes the current start payload, global entity ids, current command set, Steel/Oil/Supply scalar resources, and compact snapshot version. Local prediction is currently supported only for local Kriegsia players; local Ekat or fixture players disable prediction with the stable unsupported-local-faction reason until the WASM simulation and adapter support those contracts intentionally.

Lifecycle Paths

This section is the maintained source of truth for match creation, playback, replay branch, spectator, dev scenario, self-play, quickstart, AI, prediction, match-history, and post-match replay paths. Later phases must update this section whenever they touch one of those lifecycle paths.

PathFaction sourceAllowed factionsAI behaviorPrediction behaviorReplay/branch behaviorTests or checks
Normal lobby startLobbyPlayer.factionId and PlayerInit.faction_id, defaulted by lobby::faction_validationkriegsia and ekat; fixture and unknown ids rejectNo AI assignment by setFaction; AI seats are separateEnabled only for local Kriegsia when build/version metadata is compatibleSchema 2 records player faction id plus per-player loadout recordtests/faction_integration.mjs, tests/prediction_controller.mjs, server/src/lobby/faction_validation.rs tests
Quickstart/debug startHuman lobby selection plus quickstart validation/defaultingkriegsia and ekat; fixture and unknown ids rejectAI seats still default KriegsiaEnabled only for local KriegsiaSchema 2 records faction id and per-player debug/standard loadout recordsserver/crates/sim/src/game/setup/tests.rs, server/src/lobby/faction_validation.rs tests
AI add/remove/startAI PlayerInit.faction_id, created by public addAiPublic AI seats default to kriegsia; no public Ekat selectorKriegsia-only through public lobby controlsNot applicableSchema 2 records AI faction and per-player loadout if match startstests/ai_integration.mjs, tests/server_integration.mjs
Fixture/dev faction startExplicit Rust test/dev harness onlyphase2_empty_fixture only in TestFixture validation or direct lower-level testsRejected unless a later phase explicitly adds fixture AIDisabled when local fixture player is unsupportedFixture ids stay in explicit test artifacts onlyserver/crates/sim/src/game/setup/tests.rs, tests/prediction_controller.mjs, scripts/check-faction-assumptions.mjs
Replay playbackReplayArtifactV1.players[].faction_id and playerLoadouts[] in artifact schema 2Recorded playable ids kriegsia or ekat; missing, unknown, and fixture ids rejectFrom artifact onlyDisabled for replay viewersLoad from artifact schema, never lobby stateserver/crates/sim/src/game/replay.rs tests, server/src/lobby/room_task.rs replay tests
Replay branch staging/launchBranch seed seats copy recorded factionId from replay playersRecorded playable ids kriegsia or ekat; unsupported seat faction ids reject before live launchFrom recorded branch seed onlyDisabled unless supported by branch schema/WASMReconstruct from branch seed and cloned keyframeserver/src/lobby/room_task.rs tests, tests/protocol_parity.mjs
Dev scenariosScenario definition plus validation/defaultingCurrent bundled scenarios default to Kriegsia; explicit playable ids may be accepted by an owning scenarioNot applicable unless scenario declares AIEnabled only for local KriegsiaNot replayed unless scenario recording existsserver/crates/sim/src/game/setup/dev_scenarios/tests.rs, docs/context/testing.md
Self-playSelf-play PlayerInit.faction_id, validated by lobby::faction_validationCurrent bundled self-play defaults to Kriegsia; explicit Ekat needs a self-play script that owns itKriegsia-only in current live AI scriptsNot applicableArtifact schema 2 records faction ids and per-player loadoutsserver/crates/ai/src/selfplay tests
Match history replayStored schema-2 match artifactRecorded playable ids kriegsia or ekat; missing, unknown, and fixture ids rejectFrom artifact onlyDisabled for replay viewersLoad from persisted schema; schema 1 is incompatible for faction/loadout reconstructionserver/src/main.rs replay compatibility tests, docs/design/match-history.md
Spectator/no-fog viewLive match start payload or replay schemaMatch factions from start/replay metadataNot applicableDisabledPreserve recorded faction metadatatests/server_integration.mjs
Post-match replayCaptured schema-2 match artifactRecorded playable ids kriegsia or ekat; missing, unknown, and fixture ids rejectFrom artifact onlyDisabled for replay viewersLoad from captured schema with Steel/Oil/Supply resource payloadstests/server_integration.mjs

Current Guardrail Checks

Guardrail Map For Future Faction Work

When faction behavior changes, update the owning source and its guard in the same change: