docs(docs): 📝 Update Phase 3 military communications and resources documentation with handoff notes for data schema drift

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-05-19 21:24:52 -07:00
parent 5f0d8ddc08
commit f064fd0612
3 changed files with 185 additions and 1 deletions

View file

@ -0,0 +1,74 @@
---
from: simulator-infra (p1-60 fog-of-war closeout)
to: game-data
date: 2026-05-19
blocks: `./run verify` step 1 (game data JSON schemas)
related: .project/objectives/p1-60-fog-of-war-testing-ai-fairness.md
---
# Handoff — pre-existing JSON schema drift surfaced by `./run verify`
While closing **p1-60 (fog-of-war end-to-end coverage + AI fairness)**, the collective ran the full workspace gate. The fog-of-war work itself is green (29/29 Rust vision tests, 12 projection redaction tests, 6 AI fairness tests, 32/32 GUT fog tests, criterion bench at ~90 µs on the small map). `./run verify` nevertheless aborts at **step 1/18 — game data JSON schemas**, on drift that pre-dates p1-60 and is orthogonal to vision.
This handoff captures the failure set verbatim from `./run verify` (re-run 2026-05-19 at HEAD `df052213a`) so the data owner can pick it up without re-running the gate.
## Failure set (verbatim)
### 1. `public/resources/units/great_sculptor.json` — gender enum
```
FAIL public/resources/units/great_sculptor.json: gender: 'either' is not of type 'object', 'null'
```
- File: `public/resources/units/great_sculptor.json:?` (field `gender: "either"`)
- Schema expects `object` or `null`; `"either"` was a free-string value.
- Fix surface: the gender schema in `tools/validate_data/schemas/` (or whichever validator owns the unit schema) — either accept `"either"` as a documented enum value, or normalise the data to the structured form already used by other unit files.
### 2. `public/games/age-of-dwarves/data/terrain/flora_cover_blends.json` — missing `id`, `name`
```
FAIL public/games/age-of-dwarves/data/terrain/flora_cover_blends.json[0..7]: 'id' is a required property
FAIL public/games/age-of-dwarves/data/terrain/flora_cover_blends.json[0..7]: 'name' is a required property
```
All 8 array entries lack `id` and `name`. Either:
- backfill `id`/`name` per entry (canonical, matches `substrate.json` pattern), or
- relax the schema if blends are intentionally anonymous (then explain *why* in the schema doc).
### 3. `public/games/age-of-dwarves/data/terrain/substrate.json` — missing `movement_cost`, `defense_bonus`
```
FAIL public/games/age-of-dwarves/data/terrain/substrate.json[0..8]: 'movement_cost' is a required property
FAIL public/games/age-of-dwarves/data/terrain/substrate.json[0..8]: 'defense_bonus' is a required property
```
9 substrate rows lack both fields. The schema is right to demand them — pathfinding and combat both read these at runtime. Backfill from the reference implementation or design notes; do **not** drop the schema requirement.
### 4. `public/games/age-of-dwarves/data/terrain/substrate_blends.json` — missing `id`, `name`
Same shape as #2 — 5+ entries lacking the required identifiers.
## Why this didn't block p1-60
`./run verify` runs JSON schema checks **before** Rust workspace tests. None of the failing files feed into fog-of-war:
- `compute_vision` reads grid biome labels (string IDs) and elevation, not terrain JSON.
- `mc-player-api::projection` uses tile metadata already in `GameState`, not on-disk JSON.
- The fog GUT proofs build maps in-memory with hand-set biome strings.
The blocking surface is purely the data validator. Once you patch the four files (or the corresponding schemas), the gate should advance to step 2.
## Adjacent orthogonal failure (different owner — flagging only)
`cargo test -p mc-turn` shows one pre-existing failure unrelated to p1-60:
```
test abstract_projection::five_players_overflow_truncates_to_max_players ... FAILED
```
This test was already failing at 2026-05-04 (predates the alliances/wrap_mode/VisionState work) and has no fog-of-war coupling. It belongs with whoever owns `mc-turn::abstract_projection`; we left it untouched.
## Acceptance for closing this handoff
- [ ] `./run verify` advances past step 1 (game data JSON schemas) without `FAIL` lines for the four files above.
- [ ] No regression in p1-60 fog-of-war tests (re-run after any schema change: `cd src/simulator && cargo test -p mc-vision -p mc-player-api`).
- [ ] If a schema is relaxed instead of data being backfilled, document the rationale in the schema file's header.
## Pointers
- p1-60 objective: `.project/objectives/p1-60-fog-of-war-testing-ai-fairness.md` (status: done).
- Changelog entries for the closure: `.project/CHANGELOG.md` (three entries on 2026-05-18).
- Game-data agent task map: `.claude/instructions/agents-task-map.md`.

View file

@ -315,3 +315,113 @@ is an empty array (the icon itself is the indicator).
4. Once the gating tech is researched, decorations are replaced by the resource icon.
Renderer work for decorations is scheduled for the next cycle (godot-renderer).
---
## §9 Slot-Enabler Resources
The resources in §3 are tile-level yield resources — the player builds an improvement on
the tile and the city worked-tile economy gains a yield. A second class of resources —
**slot-enablers** — gate optional carrier slots and equipment slots on units at training
time (see [`UNIT_LOGISTICS.md`](UNIT_LOGISTICS.md) §"Modular slots gated by tech × resource"
and [`TECH_TREE.md`](TECH_TREE.md) §"Slot-enable table"). A slot-enabler may *also* be a
yield resource (cattle, iron, coal already appear in §3); some, like `boars`, `crag_aerie`,
`runestone`, are slot-only.
Slot-enablers carry the three-axis visibility schema from §1 and the same
`improvement_required` field, plus one additional field `slot_targets` listing the
unit-class or improvement slots they enable. A resource that is **only** a slot-enabler
(no city yield) carries `yields_per_turn: {}` and `improvement_required: null`.
### Slot-enabler classification table
| ID | visibility | yield_gate | improvement_gate | Tier | Notes |
|---|---|---|---|---|---|
| `cattle` (existing §3, bonus) | `always` | `null` | `null` | T1 | Wild herds visible; husbandry tech enables ox-wagon slot |
| `boars` | `always` | `null` | `null` | T1 | Wild boars visible from turn 1; mount slot via `animal_training` |
| `mountain_rams` | `always` | `null` | `null` | T2 | Visible on hill/mountain biomes; mount slot via `animal_training` |
| `crag_aerie` | `tech_gated` | `falconry_command` | `falconry_command` | T3 | Improvement built on a cliff/mountain tile; produces nesting crag-ravens |
| `hold_aerie` | `tech_gated` | `falconry_command` (era 6 branch) | `falconry_command` | T6 | Era-6 successor to crag_aerie; produces hold-falcons |
| `saltpeter` (existing §3, strategic) | `tech_gated` | `alchemy` | `alchemy` | T4 | Cave precipitation; powder_charges slot via `gunpowder` |
| `sulfur` | `tech_gated` | `proto_chemistry` | `proto_chemistry` | T5 | Volcanic vents and sulfur springs; powder gate alongside saltpeter |
| `coal` (existing §3, strategic) | `tech_gated` | `metallurgy` | `metallurgy` | T3 | Subsurface seams; firearm + steam_engine + powder gate |
| `iron` (existing §3, strategic) | `tech_gated` | `bronze_working` | `bronze_working` | T2 | Surface ore once smelting begins; firearm + steam_engine gate |
| `runestone` | `tech_gated` | `runelore` | `runelore` | T4 | Hill/mountain tile; rune_panel slot via `rune_resonance` |
| `copper` (existing §3, luxury) | `always` | `bronze_working` | `bronze_working` | T2 | Surface visible; radio_set + coilgun gate |
| `mithril` | `tech_gated` | `mithril_smithing` | `mithril_smithing` | T5 | Deep vein; coilgun slot via `coilgun_theory` |
| `adamantine` | `tech_gated` | `adamantine_forging` | `adamantine_forging` | T10 | Mantle-depth; mantle_drill slot via `deep_mantle_engineering` |
| `fusion_cell` | `tech_gated` | `fusion_theory` | `fusion_theory` | T10 | Manufactured, not mined; mantle_drill power source |
### Slot-target mapping
The same `slot_targets` field appears on every slot-enabler entry, listing the unit-class
slots it gates. Values come from UNIT_LOGISTICS.md §"Modular slots gated by tech × resource".
| Resource | `slot_targets` |
|---|---|
| `cattle` | `["ox_wagon"]` |
| `boars` | `["mount"]` |
| `mountain_rams` | `["mount"]` |
| `crag_aerie` | `["officer_bird"]` |
| `hold_aerie` | `["hold_falcon"]` |
| `saltpeter` | `["powder_charges"]` |
| `sulfur` | `["powder_charges"]` |
| `coal` | `["powder_charges", "firearm", "steam_engine"]` |
| `iron` | `["firearm", "steam_engine"]` |
| `runestone` | `["rune_panel"]` |
| `copper` | `["radio_set", "coilgun"]` |
| `mithril` | `["coilgun"]` |
| `adamantine` | `["mantle_drill"]` |
| `fusion_cell` | `["mantle_drill"]` |
### Depletion and scarcity
Slot-enablers obey the same depletion rules as yield resources where the rule applies.
- **Renewable** (no depletion): `cattle`, `boars`, `mountain_rams`, `crag_aerie`,
`hold_aerie`, `runestone`. These are biological or geological-feature resources that
do not exhaust in normal play.
- **Slow-depletion** (depletes on heavy industrial extraction, era 7+): `coal`, `iron`,
`copper`, `sulfur`, `saltpeter`. Depletion is era-and-yield gated by
`industrial_extraction_depletion` in `combat_balance.json`; pre-industrial extraction
is treated as renewable for game-length simulations.
- **Apex / non-replenishable**: `mithril`, `adamantine`, `fusion_cell`. Mithril and
adamantine are tier-locked deep-vein resources; fusion_cell is manufactured at a
fusion plant (consumes power, not a tile).
Scarcity is a soft signal expressed via `scarcity_tier: T1..T10`. Lower-tier resources
appear on more world tiles (T1 = abundant: every reasonable city has at least one);
higher-tier resources are intentionally sparse (T8+ may appear on a handful of tiles
worldwide, forcing trade or conquest).
### JSON entry shape
A slot-enabler resource carries the standard three-axis block plus the slot fields:
```json
{
"id": "boars",
"category": "slot_enabler",
"visibility": "always",
"yield_gate": null,
"improvement_gate": null,
"improvement_required": null,
"indicator_decorations": [],
"yields_per_turn": {},
"scarcity_tier": 1,
"slot_targets": ["mount"],
"tier": 1,
"depletion": "renewable"
}
```
Where a resource is *both* a tile-yield resource and a slot-enabler (cattle, coal, iron,
copper, saltpeter), the entry merges both shapes — its yield block and improvement
remain from §3, and the `slot_targets` field is appended.
### Cross-references
- Slot semantics, training-time resolution: [`UNIT_LOGISTICS.md`](UNIT_LOGISTICS.md) §5, §"Modular slots gated by tech × resource"
- Tech-gate pairings: [`TECH_TREE.md`](TECH_TREE.md) §"Slot-enable table"
- Resource visibility three-axis schema: §1 above
- Migration tool that populates per-unit slot entries: `tools/migrate-units-logistics.py`

View file

@ -60,7 +60,7 @@ TurnEvent::CapitalBlackoutEnded { player: PlayerId, turn: u32, new_capital_city_
"capital_blackout": {
"decay_multiplier": 0.5,
"comm_tier_penalty": 1,
"auto_promote_after_turns": 5
"auto_promote_after_turns": 1
}
```