Reference Map Rules This document is the authoritative specification of the game world's data model and rules. It is the ground truth for how maps are stored, how blocks behave, and how gameplay (pathfinding, movement, editing, generation) derives from that data. It binds humans and AI agents alike. If code, docs, or habits contradict this document, this document wins. If a rule genuinely needs to change, change it here first, then update the code — never the reverse. Implementation status is marked per section ( [built], [partial], [planned]) so readers know what exists today vs what the rules call for. 1. Purpose & scope The model must serve these goals, all from one data structure: Final Fantasy Tactics maps, 1:1 — integer-height terrain, slopes, steps, thin (.2) blocks, water, bridges. Space-Engineers-style building — a broad set of block shapes, placed/rotated/removed by an editor, with arbitrary fractional heights. Rolling hills of any height — continuous terrain surfaces, not grid-quantized. Dynamic terrain — explosions and moves can carve holes in walls and terrain. Procedural generation — deterministic from a seed: hills, cliffs, rocks, buildings; fixed-size, round, and open-world maps. An in-game map editor — place / rotate / delete blocks. [built: chunked voxel storage, codec, box + smooth renderers] — the rest of the rules describe the full model; parts marked planned are not yet implemented. 2. Guiding principles The data layer is the ground truth. Every block's exact size and shape lives in the map data. Rendering never modifies it. Render never feeds back. Smoothing, shading, props, and cosmetics are derived in the render layer and can never influence rules or map data. One structure. A map is a single voxel grid. "Terrain" and "blocks" are conceptual roles of voxels, not separate data structures. No derived storage. Anything derivable from the voxel grid (column tops, surfaces, adjacency) is computed or cached, never stored as map data. Deterministic. Everything derived from the map + seed (generation, cosmetics) reproduces identically given the same inputs. Append-only catalog. Tile types are added, never reordered or removed (maps reference them by index). Extensible shapes. A block shape is an enum value plus geometry. Adding a shape is additive and safe. The map stores facts; rules decide. Walkability, movement cost, hazard damage, and LoS are judgments the rules layer derives from the data — never stored in the map. 3. The voxel model 3.1 Storage The map is a 3D grid of 1×1×1 cells. Cells are grouped into chunks of 16³ ( VoxelMap.CHUNK_SIZE), keyed by chunk coordinate Vector3i. Fixed maps occupy a bounded set of chunks (an optional bounds AABB); open-world maps are unbounded and generate chunks on demand. [built] — src/map/voxel_map.gd. 3.2 Voxel encoding Each cell holds at most one block. A cell is either empty or a block: empty 0 block { type, shape, rot, height } Packed as 3 bytes little-endian: - byte 0 — type + 1 (1-indexed; 0 = empty, so catalog index 0 never collides with "empty") - byte 1 — shape (4 bits) | rot (4 bits) - byte 2 — height (0..255 → fraction 0..~1.0, 1/256 resolution); 0 means "use the shape default" [partial] — the 2-byte form (type/shape/rot) is built; the height byte is planned (schema 4). 3.3 Height = continuous surfaces height is the fraction of its cell that a block occupies, measured from the block's anchored edge: FULL occupies the whole cell. HALF is a block anchored at the bottom, occupying height of the cell (default 0.5). SLAB is a block anchored at the top, occupying height of the cell (default 0.2). A column's surface height is cell_y + height — a continuous float. Terrain hills therefore rise smoothly (0.8, 0.9, 1.0, 1.3, 1.7…) with no quantization. [planned]. 4. Materials & tile types A tile type (catalog entry) is the shared identity of a material. It describes what the material is — not how the world treats it. Material facts (ground truth): field meaning id stable name ( StringName) solid physically occupies its volume fluid physically non-solid; can flow later (water) shape canonical block shape (voxels may override per cell) height canonical fractional height (voxels may override) Rules-layer properties (NOT ground truth — the rules engine owns these): walkable, move_cost, hazard / hazard_damage, and the behavioral flags ( BLOCKS_LOS, IS_PLATFORM, BLOCKS_JUMP, REQUIRES_FLIGHT). These are judgments a rules layer consults by material. They may currently live on TileType for convenience, but they are rules data, never map data — the map never carries them. Contract: the catalog is append-only by index. Maps store type indices; never reorder or remove entries. [built, partial] — facts live on TileType; the rules-layer split is built ( src/rules/material_rules.gd owns passability/cost/hazard/flags); per-type height is still planned. 5. Block shapes The shape enum defines the geometry of a block within its 1×1×1 cell. Every shape must be a closed volume (no open faces) so it renders cleanly. shape volume FULL whole cell HALF bottom block of height SLAB top slab of height SLOPE full-length ramp (+x) HALF_SLOPE half-length ramp STAIR stepped block WEDGE corner wedge (closed) CORNER L-shaped inner corner Rotation in 90° steps ( rot 0–3 meaningful; geometry treats rot % 4). Directional shapes ( SLOPE, HALF_SLOPE, STAIR, WEDGE, CORNER) rotate around Y. The shape gallery map ( shape_gallery.vmap, planned) shows every shape in isolation and is the visual reference. [built: FULL/HALF/SLAB/SLOPE/WEDGE] — HALF_SLOPE/STAIR/CORNER and the closed WEDGE are planned. src/render/shape_geometry.gd. 6. Terrain Terrain is voxel columns: a run of solid voxels rising from the map base ( y=0) to a column top. The top voxel carries the fractional height. Rolling hills = columns whose tops vary continuously ( surface = y + height). Cliffs/steps = adjacent columns whose tops differ. Slopes = a surface that ramps between adjacent column tops (FFT-style), or a SLOPE block where a discrete slope tile is wanted. The base is implicitly y=0; generators fill solid voxels from the base up. [partial] — column filling exists via FULL/HALF stacks; continuous height tops are planned. 7. Blocks & placement Blocks are voxels placed by the editor, generator, or gameplay: Blocks snap to cells (integer grid). A block occupies [cell_y, cell_y + height). Blocks replace terrain. Placing a block clears any terrain voxels under its footprint; removing it restores terrain (generator- or map-provided). This is the Space-Engineers rule. A building's footprint may flatten sloped terrain under it (placement levels the columns it touches) — chosen per-building by the placement rule. Buildings always rest on integer cell bounds; they never "stack on" a fractional block in the same column. [planned] — no placement engine yet. 8. Surfaces, adjacency & movement These are the rules rules-engine and pathfinding will consume. All are derived from the voxel grid, never stored. The engine receives only facts — surface heights, block shapes, materials, fluid state, adjacency — and produces the verdicts (walkable? cost? blocked?). Verdicts are never written back into the map. 8.1 Surfaces A surface is a place a unit can stand: - Column top: surface(x, z) = y + height of the top voxel in column (x, z) (terrain). - Block top: the top face of any solid block (stand on a roof, a bridge, a wall cap). surface(x, z) returns { height: float, type }. [planned]. 8.2 Adjacency Rules query neighbors directly: voxel_at(x, y, z) plus its 6 (or 26) neighbors — a plain grid read. Anything about an adjacent block (its shape, height, type, fluid state) is available; the rules decide what it means. 8.3 Movement thresholds (current rule) Movement cost between adjacent columns compares their surface heights. Rise = surface(neighbor) - surface(current). The current thresholds: Rise ≤ 0.25 — free step onto a flat surface (or up a very low lip). 0.25 < rise ≤ 1.0 — needs a supporting slope/step (the approach side has a SLOPE/ STAIR/partial block, or the columns are separated by a ramp surface), or costs extra move. Rise > 1.0 — blocked, unless the actor has a jump/lift ability that explicitly allows it. These numbers are a deliberate, tunable rule — they live in one place (the rules module) and must not be scattered. [built: src/rules/movement_rules.gd]. 8.4 Pathfinding Pathfinding runs over walkable surfaces: - Nodes = (x, z, surface) (a column top, or a block top). - Edges = adjacent surfaces within the movement thresholds (8.3). - Blocked by: non-walkable surfaces, solid blocks between surfaces, hazards that forbid entry. - surface() results are cached per map and invalidated only where edited. Built as a budgeted 4-directional Dijkstra over the surface graph: PathEvaluator ( src/pathfinding/path_evaluator.gd) composes terrain rules (passability/cost), climb rules ( MovementRules), and unit mobility ( UnitProfile traits/statuses — fly, float, swim, slow/stop) into per-step verdicts; Pathfinder ( src/pathfinding/pathfinder.gd) returns either the lowest-cost path to a destination (player move, NPC) or the full reachability bubble (the FFT movement range), and reports every hazardous cell crossed so movement/combat can apply the rule's damage and statuses. Surfaces currently derive from block shape geometry (schema 3); the height byte (schema 4) will refine them. [built: src/pathfinding/*, src/rules/*]. 9. Fluids Water (and future fluids) is a non-solid fluid voxel type ( solid = false, fluid = true). Pools are clusters of fluid voxels filling low columns. Fluid flow simulation is future; the data contract only requires a fluid voxel with a type and height. [built: water type is fluid]. 10. Dynamic edits Any code may read or modify voxels: set_voxel(x, y, z, block) / clear_voxel(x, y, z) — the editor, abilities, and explosions all go through these. Explosions clear a voxel sphere (walls and terrain), carving craters. Surface-cache invalidation: editing a column invalidates that column's cached surface(). Edits are never "render changes" — they are data changes that the renderer reflects. [built: set/clear; planned: cache invalidation]. 11. Procedural generation contract Generators write ordinary voxels; they are subject to the same rules. Deterministic from (seed, position). Same seed → same map, everywhere, always. Terrain: per-column height from noise → fill columns from the base (rule 6). Structures: stamp voxel shapes (buildings, trees, rocks — those occupying voxel space) on top (rule 7). Map shapes: square = bounded chunk set; round = square bounds with a circular playable mask (columns outside the circle stay empty); open world = unbounded, chunks generated on demand from (seed, chunk_coord). Generators must produce valid maps (rule 12). [planned]. 12. Codec & validation Binary schema versioned in the header; loaders reject mismatches loudly. A map is invalid if: a voxel's type index exceeds the catalog, shape is unknown, rot ≥ 8, or chunk data is malformed. Loaders must reject invalid maps, never silently repair them. The voxel grid is the only serialized form. [built]. 13. Rendering discipline Two views exist; both are derived from the same data: RAW — the map data literally: every block at its exact shape/height, flat per-type colors. This is the truth view; rules are debugged against it. RENDER — cosmetics only: the exact same voxel shape geometry as the debug view, plus a seeded per-cell tint, rocks/foliage props, day/night, and the top-grid shader. Effects never change geometry — slopes stay slopes, voids stay holes; only colors and props are added. [built: debug + render views — identical shape geometry, render adds tint + props]. 14. Rules for agents Non-negotiable conventions for anyone (human or AI) working on this project: Never store derived data in the map. Surfaces, adjacency, LoS, cached geometry — computed or cached, never serialized. Never let rendering feed back into logic. No rule may depend on what the render layer does. The catalog is append-only. Add types; never reorder or remove. Movement/surface rules live in one place. Thresholds (§8.3) are a single tunable module, not scattered constants. Follow this document. When a rule changes, change MAP_RULES.md first, then the code, then mark the code section [built]. Validation rejects, never repairs. Corrupt maps fail loudly. The map is facts, not rules. Never encode walkability or behavior verdicts into map data; the rules layer owns all judgments. Simulation is deterministic. No engine RNG ( randi, randomize) in any simulation path — use DeterministicRng, seeded from the match. Sim math stays in integers and exact fractions. Rule data is versioned. RulesetVersion hashes the catalog + material rules + movement rules; a match pins it, and clients that don't match refuse to join. State changes are commits. Every mutation — including mid-combat map edits — flows through the sequence-numbered commit log. Nothing mutates simulation state out-of-band. Revision log: created as the foundation spec. Supersedes informal conventions discussed before it existed. Added the facts-vs-rules principle (§2.8, §14.7) and built the rules layer ( MaterialRules/ MovementRules/ UnitProfile) plus the pathfinding pipeline ( PathEvaluator/ Pathfinder) per §8. Added the determinism contract (§14.8-14.10), the deterministic RNG, ruleset versioning, and the commit log (multiplayer foundation); combat is specified in COMBAT.md. Project Context Project knowledge. Agent-facing; read this before any work. Not the doc for humans. Overview A Final Fantasy Tactics-like tactical RPG (grid-based, turn-based combat with height, movement, and class/job systems) built in Godot 4.x with GDScript. Codename: operation_tactics — the real name is TBD. Nothing about the codename should leak into user-facing strings. Goals Faithful FFT-style tactical combat: isometric(ish) grid, move/act/end-turn flow, terrain height, facing, and line-of-sight. Job/class system with learning abilities from action (JP), not just leveling. Branching/unique characters, story-driven campaign. One cohesive game, not a tech demo — a single unified codebase. Current State Engine files exist (project.godot, directory skeleton). Map data core (registry, schema, binary codec), the 3D heightfield renderer ( MapView/ MapTheme), the seeded cosmetic layer ( MapDetails), a demo map asset, and the custom test pipeline are in place. main.tscn boots the demo map with an FFT-style isometric camera (WASD pan / QE rotate / wheel zoom) and an astronomical day-night cycle ( src/render/day_night.gd): daylight length computed from latitude + month (Virginia in August ≈ 13.5h), a sun that only lights above the horizon, a phase-driven moon with cloud-dimming, and clear-sky starlight. Tune latitude_deg/ month/ day/ cloud_cover/ moon_phase/ cycle_seconds on the Sun node. F2 toggles a free-fly spectator camera ( src/camera/free_cam.gd — right-mouse look, WASD/QE move, wheel = speed, F3 recenters); it never touches the FFT camera's state, so toggling back is seamless. Rules layer + pathfinding pipeline built — facts-vs-rules split is done ( TileType = facts; MaterialRules/ MovementRules/ UnitProfile = judgments) with a budgeted Dijkstra pathfinder ( Pathfinder/ PathEvaluator) providing FFT movement-bubble reachability, lowest-cost paths, hazard reports, and fly/float/swim/status support. See "Rules & Pathfinding pipeline". Combat designed; multiplayer foundation built — COMBAT.md is the authoritative battle spec (deterministic CT turn order, streamlined stats, the eight elements, living-map mutations as commits). Foundation primitives exist: Elements, DeterministicRng, RulesetVersion, Commit/ CommitLog (with the commit vocabulary), and streamlined combat stats + affinities on UnitProfile. See "Combat & multiplayer foundation". Procedural arena — voxel_arena.vmap is the seeded showcase/test map: rolling noise terrain + stamped features covering all 10 materials, all 5 shapes, rotations 0-3, hazards, real void, multi-level climbing, and a pathable plaza. Generated by src/map/arena_generator.gd ( ArenaGenerator), written by tools/build_voxel_arena.tscn, validated by tests/map/voxel_arena_test.gd (determinism + asset-in-sync + feature coverage). Now the default map in main.gd. Test suite is 117 green. Repository created on Forgejo and pushed. Repo uses the SHA-256 object format — never recreate it as sha1, and never rely on push-to-create for this repo (Forgejo's push-to-create ignores object format and would make a sha1 repo). Engineering Standards Applies to every agent and human working here. Goal: a codebase humans can maintain long-term, not one that merely "works." Code quality Human-first. Idiomatic GDScript, clear names, logical file layout. Readability first, efficiency second — but never accept needless waste (hot paths: avoid per-frame allocations). Concise. Smallest correct change. No speculative generality, no backward-compat shims without a concrete need. Reuse over copy. Shared frameworks (tile registry, map codec; later combat math, pathfinding) live once and are used everywhere. Rule of three: third copy → extract. Modular & layered. Scripts stay focused (one responsibility); layers point one way — data/logic must not import rendering; cross-cutting concerns use signals. No spaghetti, no circular references. Debuggable. Keep logic in deterministic functions (pure input → output) where practical; avoid hidden global mutation. Comment policy Comment the WHY and the intent: design decisions, non-obvious data formats, invariants, cross-module contracts. A reviewer should understand your intent. Never narrate WHAT the code does; no AI-style filler (e.g. # get the player position). No emoji. Code should look hand-written. Testing (mandatory, Factorio-style) Before every commit: ./run_tests.sh runs the full suite headless and must pass; a pre-commit hook enforces it. Never commit red. Suite covers: codec round-trip (byte-exact), registry append-only guard, and every subsystem's core behavior as it lands. Benchmark mode ( --bench, planned) for perf-sensitive paths (combat sim, map load); checked for regressions, not just pass/fail. Test layout: res://tests/**/*_test.gd, custom runner (see res://tests/run_tests.gd). Tests extend the TestCase base and use its eq/is_true/... helpers. Growing-project guardrails Format/version drift → schema version in every binary format; loaders reject mismatches loudly, never silently. Duplication creep → rule of three + review. Scope creep → anything new must fit the modular layout or the plan changes (and this doc gets updated). Dead code → no "just in case" branches; keep the tree lean. Conventions drift → this doc is the arbiter; update it when rules change. Single-file bloat → split when a script exceeds ~300 lines or has more than one job. Map Data Architecture MAP_RULES.md is the authoritative world-model spec — read it before touching anything map-related. It defines the voxel model, tile types, shapes, terrain/blocks, movement thresholds, fluids, dynamic edits, generation, validation, and the agent rules. This section is the quick-reference; MAP_RULES.md wins on any conflict. Direction: voxel pivot. VoxelMap ( src/map/voxel_map.gd): a chunked 16³ grid of 1×1×1 voxels (open-world ready; columns are subsumed as vertical runs of solid voxels). Each voxel is {type, shape, rot} (2 bytes packed; TileType.Shape = FULL/HALF/SLAB/SLOPE/WEDGE…, per-voxel shape + rotation for the editor). Serialized by VoxelCodec ( voxel_codec.gd, schema 3, .vmap files). VoxelMesh ( src/render/voxel_mesh.gd) builds the combined shape mesh with face culling (interior faces shared with a full solid neighbor are dropped); VoxelView renders it. main.gd defaults to the voxel demo ( assets/maps/voxel_demo.vmap, built by tools/build_voxel_demo.tscn) and loads .vmap as voxels vs .otmap as the legacy column model (still present until the column code is retired). Cosmetic layer, hover/select and picking are column-only for now. Global design (see res://src/map/ for implementation). Maps are 2D grids of columns: each cell owns its entire z column, so interior-under-roof and bridges are expressible as stacked walkable surfaces. TileType ( tile_type.gd) — one shared entry per tile kind: material facts only ( id, block_height, shape, solid, fluid). Behavior (walkability, cost, hazards, flags) lives in the rules layer ( MaterialRules), never here. Logic only — visuals live in a separate theme table ( type_id → scene/texture). TileTypeRegistry ( tile_type_registry.gd) — the global catalog. Append-only: never reorder/delete entries, maps reference types by index, so adding types is always safe. version guards against breaking changes; loaders reject catalog_version > registry.version. MapData ( map_data.gd) — size, catalog_version, spawn points, and a column per cell: { elevation, ground (type index), layers: [{z, type, rot}] }. MapCodec ( map_codec.gd) — StreamPeerBuffer (little-endian) ↔ PackedByteArray. Binary layout is the contract (see file header): magic OTMP + schema version + catalog version + dims + spawns + per-cell: flags u8, elevation u16, ground u16, optional layer list. Map files use the .otmap extension. MapPicker ( src/map/map_picker.gd) — resolves a cursor ray to the specific block hit (cell + surface height), not just the column: DDA over the grid + ray-vs-AABB on every block. View-aware (RENDER fills/ thin layers vs RAW data heights). main.gd feeds hover/ selected cell+height into the top-grid shader, so a click on a roof highlights the roof, a click on a cliff face highlights that block. MapTheme ( src/render/map_theme.gd) — visual theme, type_id → color (later scene/texture). Kept out of TileType so map data stays renderer-agnostic; swapping the theme restyles the whole game. MapView ( src/render/map_view.gd) — the single data→rendering bridge: one combined ArrayMesh. Two view modes ( MapView.ViewMode, toggled with F1 in main.gd): RENDER = the game look (terrain fills to the map base — the bedrock rule, no terrain voids; layers drawn thin; cosmetic layer on; no wireframe) and RAW = the map data literally (ground blocks at [elevation, elevation + block_height] with voids intact, layers at full data height, flat colors + wireframe, no cosmetics) — the geometry rules/pathfinding are written against the RAW view. Renderer direction is 3D heightfield. The RENDER top grid is a fragment shader ( src/render/top_grid.gdshader) on a duplicate of the terrain mesh: + crosses at lattice corners + dashed edges, uniform and anti-aliased, tinted per hover_cell/ selected_cell uniforms. main.gd picks the cell under the cursor (hover on mouse move, blue select on left-click) and feeds the uniforms. MapDetails ( src/render/map_details.gd) — the cosmetic layer (two-layer model): a pure deterministic function of (map, seed) producing per-cell tint variance, C0-continuous surface jitter (no seams), and scattered props (rocks/bushes/tufts via MultiMesh). Purely visual — never feeds back into map data or rules. Two-layer model + seed contract — MapData.seed is part of the map (codec field, schema 2). Ground truth is the column blocks (drives rules/pathfinding later); the cosmetic layer is derived from (map, seed) and regenerated freely — same seed, same result, everywhere. Determinism rules: integer-hash noise keyed by (seed, x, y, z), fixed iteration order, no engine RNG. The seed can later drive other derived content (spawns, variants). DemoRegistry ( src/map/demo_registry.gd) — canonical demo catalog; stand-in for a future on-disk catalog file (append-only by index). The demo map asset ( res://assets/maps/demo.otmap, seed 12345) is generated by tools/build_demo_map.tscn. TestRegistry ( src/map/test_registry.gd) + test map ( assets/maps/test_map.otmap, 53×27, seed 20260815) — the scenario museum: every hazard, elevation shape, layered structure, wall/corridor, rotation, and spawn surface the model can express, laid out in 8×6 zones (see tools/build_test_map.gd — its coordinates are the contract; tests/map/test_map_test.gd spot-checks each zone). View it with godot --path . -- --map=res://assets/maps/test_map.otmap. Procedural maps — a generator is just another producer of MapData (same shape as the demo builder); it flows through the same codec and renderer. Generators must produce valid MapData (indices within the registry, in-bounds elevations/layers). In-game map editor (planned) — mutates in-memory MapData and re-packs through the same codec; palette is the registry. Editor stays an orthogonal consumer of the schema. Rules & Pathfinding pipeline MaterialRules ( src/rules/material_rules.gd) — the rules layer: how the world treats each material. One TerrainRule per type id: passable, move_cost, hazard/ hazard_damage, behavioral flags, status_on_enter. for_registry() yields the rule set for a catalog (defaults + per-id overrides — the single behavioral source for demo/test ids). All editor-editable as Resources. MovementRules ( src/rules/movement_rules.gd) — the single tunable module for MAP_RULES §8.3 thresholds: free_step (0.25), step_ceiling (1.0), climb_cost_per_step, water_cost_extra. UnitProfile ( src/rules/unit_profile.gd) — unit mobility: move_budget, traits (FLOAT/FLY/SWIM), status effects (slow/stop/immobilize modify the budget; float/swim statuses grant traits). PathEvaluator ( src/pathfinding/path_evaluator.gd) — composes map facts (column surfaces from voxel shape geometry) × rules × unit state into per-step verdicts ( step_cost, hazard_at). Pathfinder ( src/pathfinding/pathfinder.gd) — budgeted 4-directional Dijkstra over the surface graph. find_path() → lowest-cost path to a destination; reachability() → the FFT movement bubble (cost field + parents). PathResult carries the ordered path, total cost, and every hazardous cell crossed (movement/combat applies the rule's damage/status as the unit traverses it). Pathfinding operates on (x, y, surface) nodes derived from the voxel grid; inter-surface transitions (stairs, ledges) are edge costs per MAP_RULES §8.3. Surface heights come from shape geometry until the schema-4 height byte lands. Combat & multiplayer foundation COMBAT.md is the authoritative battle spec — read it before touching combat. It locks the FFT baseline (turn order, turn flow, occupancy, targeting) and defines our divergences: deterministic CT scheduling, the eight-element system with affinities, and the living map (destruction/placement/field events/collapse, all as commits). Elements ( src/rules/elements.gd) — the eight elements (fire/ice/lightning/water/wind/earth/holy/dark) + affinity helpers ( effective_multiplier, standing_multiplier). UnitProfile ( src/rules/unit_profile.gd) — now carries the streamlined stats (hp/mp/atk/def/mag/mdf/spd) and per-element affinities alongside mobility. DeterministicRng ( src/rules/deterministic_rng.gd) — SplitMix64-seeded PRNG; the ONLY randomness source allowed in simulation paths (COMBAT.md §7.3, MAP_RULES §14.8). RulesetVersion ( src/rules/ruleset_version.gd) — FNV-1a hash of catalog + material rules + movement rules; a match pins it, mismatched clients refuse to join. Commit/ CommitLog ( src/net/) — the append-only, sequence-numbered state-change stream (the shared clock of a match). The kind vocabulary (move_unit, set_block, remove_block, apply_damage, …) is COMBAT.md §10.1, reflected as Commit.KIND_* constants. Turn controller, damage math, targeting, field effects, collapse, and the snapshot codec: [planned] (specified in COMBAT.md). Conventions Godot 4.x GDScript; follow the godot4 skill (theme/font override quirks, ConfigFile gotchas, explicit type annotations — type warnings treated as errors). Comments explain intent, not narration (see Engineering Standards). snake_case for scripts/vars, PascalCase for node names. Branch main; commits concise, imperative mood, lowercase, focused on "why"; push frequently. Docs & Wiki Canonical docs live in the repo: GAME_BIBLE.md (the game bible — vision, systems, pipelines, baselines, glossary; read it first), MAP_RULES.md (authoritative world-model spec), COMBAT.md (authoritative battle spec), and this file. Mirrored to the wiki ( https://wiki.fifthdread.com/books/operation-tactics) by tools/sync_wiki.py — the wiki is a rendered view, the repo is the source of truth. Sync (idempotent; creates missing pages, updates existing): source ~/opencode/api_secrets.sh && python3 tools/sync_wiki.py. --check previews without writing. Which docs map to which pages is declared in tools/wiki_manifest.json. Shipping Shipping = commit and push to the project repo. Remote: Forgejo — ssh://git@forgejo.fifthdread.com:223/Fifthdread/operation_tactics.git. SHA-256 object format — the repo must be pre-created on the server as sha256; push-to-create will not work for this repo. If the game is ready for distribution on Arch-based Linux: also update the PKGBUILD so paru -S installs it. PKGBUILD lives in the Fifthdread/pkgbuilds repo at ~/opencode/pkgbuilds/ (one dir per package, -git variant recommended for an in-development game). Workflow: bump pkgver= ( rev-count.commit), regenerate makepkg --printsrcinfo > /.SRCINFO, commit .SRCINFO alongside, push. Full detail in the pkgbuilds project's CONTEXT.md/AGENTS.md.