Four-round workshop (Gestalt, Tyre, Paula, Burnelli-Sheldon, Miri) mapping the full generation pipeline from planetary heightmap to walkable tile. 25 D-records produced. Ticket dependency chain for Tier 0-4 implementation identified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
339 lines
18 KiB
Markdown
339 lines
18 KiB
Markdown
---
|
||
title: "Tyre — Round 1: Technical Architecture Inventory"
|
||
description: "Layer-by-layer audit of the generation pipeline: state, data format boundaries, dependency order, testing strategy"
|
||
type: workshop
|
||
status: active
|
||
workshop: generation-cascade
|
||
agent: tyre
|
||
round: 1
|
||
created: 2026-04-30
|
||
---
|
||
|
||
# Generation Cascade — Round 1: Technical Architecture Inventory
|
||
|
||
**Tyre — Technical Architect**
|
||
|
||
---
|
||
|
||
## Summary Verdict
|
||
|
||
The pipeline has a hard cliff at Layer 5. Everything above it (galactic through city placement) is working production code. Everything below it is either stub types or missing entirely. The two-phase district generator architecture is well-designed on paper; zero generation code exists.
|
||
|
||
The thinnest vertical slice from heightmap to walkable tile — a flat single-district world with no social structure — costs roughly 6–7 dev-days starting from today's codebase.
|
||
|
||
---
|
||
|
||
## Pipeline Inventory
|
||
|
||
### Layer 1 — Galactic Scale
|
||
**State: DONE**
|
||
|
||
| Property | Detail |
|
||
|----------|--------|
|
||
| Input | Wiki TOML files, body `index.md` frontmatter |
|
||
| Output | `systems.db` — `star_systems`, `bodies`, economics tables |
|
||
| Code | `tooling/economy-db/import_economics.py` + Rust `generate_brands` binary |
|
||
| Format | SQLite (file-based; read-only canonical snapshot) |
|
||
|
||
No gaps. This is the upstream root for all other layers.
|
||
|
||
---
|
||
|
||
### Layer 2 — Planetary Heightmap Simulation
|
||
**State: DONE**
|
||
|
||
| Property | Detail |
|
||
|----------|--------|
|
||
| Input | Body definition dict (planet_class, parameters from `index.md`) |
|
||
| Output | Terrain dict: NumPy arrays for elevation, temperature, moisture, surface_water, river_grid |
|
||
| Code | `tooling/planet-gen/planet_simulation.py` |
|
||
| Format | In-memory NumPy (not persisted separately; consumed immediately by atlas generator) |
|
||
|
||
No gaps. Called by `generate_atlas.py` as `simulate(body_def)`.
|
||
|
||
---
|
||
|
||
### Layer 3 — Atlas City Placement and Infrastructure
|
||
**State: DONE**
|
||
|
||
| Property | Detail |
|
||
|----------|--------|
|
||
| Input | Terrain dict (Layer 2) + body definition from `systems.db` |
|
||
| Output | `markers.json` per body (cities, roads, rail, POIs) + atlas tables in `systems.db` |
|
||
| Code | `tooling/planet-gen/generate_atlas.py` |
|
||
| Format | JSON files (pixel space: 512×256 grid) + SQLite |
|
||
|
||
Output schema: `cities[{id, name, kind, center: [row, col], population}]`, roads/railroads as A\* paths. Gate terminal POI placed at largest population centre.
|
||
|
||
No gaps in the atlas layer itself. The gap is *downstream*: nothing in the Rust runtime consumes this output.
|
||
|
||
**Critical scale fact:** Atlas coordinates are (row, col) in a 512×256 pixel grid. A district is 512×512 sim tiles = 256×256 visual tiles = 256m. The ratio between atlas pixels and sim tiles is undefined and unimplemented. This is the first missing translation.
|
||
|
||
---
|
||
|
||
### Layer 4 — City Naming
|
||
**State: DONE**
|
||
|
||
| Property | Detail |
|
||
|----------|--------|
|
||
| Input | `markers.json` (empty name fields) |
|
||
| Output | `markers.json` (named cities and features, Gemma-driven) |
|
||
| Code | `tooling/planet-gen/gemma_naming.py` |
|
||
| Format | JSON (same file, in-place update) |
|
||
|
||
No gaps.
|
||
|
||
---
|
||
|
||
### Layer 5 — City-to-District Decomposition
|
||
**State: MISSING — no design, no code, no types**
|
||
|
||
This is the first gap in the entire pipeline. A city marker in the atlas is a single pixel coordinate. A walkable district is 512×512 sim tiles. There is no code or design that answers:
|
||
|
||
- How many districts does a city of population N get?
|
||
- What is the coordinate system mapping from atlas pixel-space to world tile-space?
|
||
- How are district boundaries drawn between cities?
|
||
- How are inter-city "wilderness" districts handled?
|
||
- What district IDs are assigned, and how are they derived deterministically from the world seed?
|
||
|
||
The `DistrictContext` type on `DistrictSkeleton` is `pub type DistrictContext = String` — it exists to hold world position and neighboring district references, but nothing populates it.
|
||
|
||
**Data format boundary:** Python JSON (markers.json, 512×256 pixel coordinates) → Rust runtime (`DistrictId`, world tile positions). No bridge code exists.
|
||
|
||
**Minimum viable implementation:** A function that takes a body's atlas markers and produces a `Vec<(DistrictId, world_tile_origin)>` — one district per city, deterministically positioned. This is the unlock for everything below.
|
||
|
||
---
|
||
|
||
### Layer 6 — DistrictSkeleton Generation (Phase 1)
|
||
**State: STUB — types compile, zero generation code**
|
||
|
||
The designed five-stage pipeline from the workshop-outcomes.md:
|
||
|
||
| Stage | What | Code state |
|
||
|-------|------|------------|
|
||
| Stage 1: Classification | WorldTier, ComplexityTier, SettingType | Types exist; no generation logic |
|
||
| Stage 2: Block grid | 4×4 BlockSkeleton, DistrictLayoutMode | Types exist; no generation logic |
|
||
| Stage 3: Reservation | MultiBlockReservation, skyscrapers, terminals | Types exist; no generation logic |
|
||
| Stage 4: Social site + NPC | SocialSitePlacement, triangles, DerivedDistrictAnalysis | Types exist; no generation logic |
|
||
| Stage 5: Guarantee audit | GuaranteeAuditResult | Type is `String` stub |
|
||
|
||
Every sub-type that requires actual content or logic is a `pub type Foo = String` alias:
|
||
- `DistrictContext`, `SocietyProfileRef`, `ZoneDefinition`, `DistrictBoundaries`
|
||
- `GuaranteeAuditResult`, `CorridorSpine`, `ChunkLayout`, `CorridorSpine`
|
||
- `Era`, `EraModification`, `LandmarkSlot`, `AccessPoint`
|
||
|
||
**WorldTier naming mismatch (bug to fix before any generation work):**
|
||
`generator.rs` defines: `Peripheral | Connected | Core`
|
||
Workshop outcomes (L-3, lead decision): `Epicenter | Regional | Backwater | Passage | Waypoint`
|
||
These are incompatible. The workshop canonical values must replace the current enum before the generation spike begins.
|
||
|
||
**Missing DistrictSkeleton fields:**
|
||
The struct in `generator.rs` is missing three fields specified in the workshop outcomes design:
|
||
- `vertical_structure: VerticalStructure` (from D-READY-11)
|
||
- `breach_only_zones: Vec<ZoneId>` (from guarantee audit, Tier 2)
|
||
- `derived_analysis: DerivedDistrictAnalysis` (Phase 1 computed, from Miri/Gestalt)
|
||
|
||
**Minimum viable implementation:** Stage 1 + Stage 2 only. Classification from WorldTier + flat 4×4 block grid with uniform zoning. No reservations, no social sites, no audit. Produces a compilable DistrictSkeleton that can be handed to Phase 2.
|
||
|
||
Workshop estimate for Phase 1 basic: ~3 dev-days.
|
||
|
||
---
|
||
|
||
### Layer 7 — ChunkData Generation (Phase 2)
|
||
**State: MISSING — no code, no type resolution**
|
||
|
||
Phase 2 is the per-chunk fill step that runs when a chunk is first loaded. It reads the `BlockSkeleton` for the block containing the chunk and produces tile data (walls, floors, furniture placements).
|
||
|
||
`GeneratorChunkData` is `pub type GeneratorChunkData = Vec<bool>` — a flat boolean walkability array. The actual tile type system (`TileId`) is `pub type TileId = String`. No tile vocabulary exists.
|
||
|
||
The heritage grammar overlay system (D-READY-9, `HeritageGrammarOverlay`) does not exist as Rust code or TOML data. It was designed in the workshop but nothing has been authored.
|
||
|
||
**Data format boundary:** `BlockSkeleton` (Rust struct) → 64×64 tile grid (`GeneratorChunkData`). The generation logic for this conversion is entirely absent.
|
||
|
||
**Minimum viable implementation:** Produce a flat walkable floor tile grid for each chunk, with wall tiles on chunk boundaries. No rooms, no furniture. Just a walkable district footprint. Heritage grammar can be deferred.
|
||
|
||
Workshop estimate for Phase 2 basic: ~4 dev-days. With heritage grammar: substantially more.
|
||
|
||
---
|
||
|
||
### Layer 8 — Chunk Streaming + Generator Integration
|
||
**State: PARTIAL — streaming works, generator hookup is zero**
|
||
|
||
`chunk_streaming.rs` correctly manages load/unload based on player position. It compiles, passes tests, and handles z-level isolation properly. The architecture is sound.
|
||
|
||
The gap: `WalkabilityMap::load_chunk()` creates `ChunkData::new_walkable()` — a fully-walkable blank placeholder. It has no knowledge of districts, no connection to any generator, and no way to distinguish "in a building" from "in open space."
|
||
|
||
For Phase 5 (generated world), `load_chunk()` needs to:
|
||
1. Determine which district the chunk coordinate falls within
|
||
2. Look up the `DistrictSkeleton` for that district
|
||
3. Identify the `BlockSkeleton` for the block containing this chunk
|
||
4. Run Phase 2 fill and return real tile data
|
||
|
||
None of steps 1–4 exist. The streaming system is a ready container waiting for content.
|
||
|
||
**Minimum viable integration:** A `DistrictMap` resource mapping `ChunkCoord → DistrictId`, and a Phase 2 dispatch in `load_chunk()`. ~1 dev-day once Phase 1 and 2 exist.
|
||
|
||
---
|
||
|
||
### Layer 9 — Social Site Templates and Triangle System
|
||
**State: DONE (runtime system, not wired to generator output)**
|
||
|
||
`triangle.rs` is fully implemented production code:
|
||
- `FullTemplateDef`, `RoleSchema`, `SpaceSpec`, `TriangleDef` — content schema with validation
|
||
- `generate_intra_template_triangles` and `generate_cross_template_triangles` — working
|
||
- `tick_triangle_escalation` and `apply_resolve_triangle` — working ECS systems
|
||
- Tests pass, serialization correct, FNV-1a determinism enforced (D-010)
|
||
|
||
The gap: `DistrictSkeleton.social_sites` contains `Vec<SocialSitePlacement>` with `template_tag: String` references. Nothing reads those tags and spawns `TemplateOwnership` components. The link from generator output to runtime triangle population is missing.
|
||
|
||
---
|
||
|
||
### Layer 10 — NPC Generation Spike
|
||
**State: PARTIAL — proof-of-concept, not production pipeline**
|
||
|
||
`server/src/bin/generator_spike.rs` (Sprint 25) generates NPCs from `ZoneSpec + CultureProfile` inputs. It produces named NPCs with traits, wants, behaviors, relationships, and want-tells. It works and demonstrates the 10-axis model is viable.
|
||
|
||
However: it is a standalone binary that bypasses ECS entirely. It operates on `ZoneSpec` (not `DistrictSkeleton`). It is not wired to the spatial generation pipeline. Its output is printed to stdout, not spawned into the ECS world.
|
||
|
||
The gap between this spike and production NPC generation: `ZoneSpec` must be derived from `BlockSkeleton.zoning` + `DistrictSkeleton.society_profile`, then NPCs must be spawned with `TemplateOwnership` components into the bevy ECS world.
|
||
|
||
---
|
||
|
||
## Data Format Boundaries
|
||
|
||
Four boundaries, three of them currently bridgeless:
|
||
|
||
```
|
||
Python atlas output → [BRIDGELESS] → Rust runtime
|
||
markers.json → → DistrictMap resource
|
||
(512×256 pixel coords) → → (sim tile coords)
|
||
|
||
Atlas city marker → [BRIDGELESS] → District grid
|
||
(row, col) pixel → → Vec<(DistrictId, ChunkCoord)>
|
||
|
||
DistrictSkeleton (Rust) → [BRIDGELESS] → ChunkData (Rust)
|
||
Phase 1 output → → Phase 2 fill
|
||
BlockSkeleton → → 64×64 tile grid
|
||
|
||
chunk_streaming.rs → [PARTIAL] → generator
|
||
load_chunk() → → ChunkData::new_walkable() (wrong)
|
||
```
|
||
|
||
The only working boundary is Python atlas → `systems.db` (the atlas tables), which is consumed by the Godot atlas map UI but not by the game simulation server.
|
||
|
||
---
|
||
|
||
## Strict Dependency Order
|
||
|
||
```
|
||
[DONE] Layer 1-4: Atlas pipeline complete
|
||
↓
|
||
[MISSING] Layer 5: City-to-district decomposition ← FIRST UNLOCK
|
||
↓
|
||
[STUB] Layer 6: DistrictSkeleton Phase 1 generation
|
||
↓ (WorldTier enum fix is a prerequisite)
|
||
[MISSING] Layer 7: ChunkData Phase 2 fill
|
||
↓
|
||
[PARTIAL] Layer 8: chunk_streaming → generator wiring
|
||
↓
|
||
↓ character/apartment work gates here
|
||
[BACKLOG] #681 Apartment generator
|
||
[BACKLOG] #682 Apartment rendering
|
||
```
|
||
|
||
**Parallelization opportunities within the blocked stack:**
|
||
- Once Layer 5 design is locked, Phase 1 (Layer 6) and content authoring (heritage grammar TOML, zone-type RON files, culture profiles) can proceed in parallel — they share no code dependency
|
||
- Different districts can be generated in parallel once Layer 6 code exists (each district is seed-isolated)
|
||
- Triangle template YAML authoring has no code dependency on Layer 5-8 and can proceed now
|
||
|
||
**NOT blocked by generation pipeline:**
|
||
- #619 Full character customization — Phase 4 work on a hand-authored 2-floor test map
|
||
- #694 Character creation screen — Phase 4, same rationale
|
||
- #616 Verb vocabulary design — content design work, cascade phase 4
|
||
|
||
---
|
||
|
||
## Ticket Analysis
|
||
|
||
### Tickets with incorrect cascade position (should be formally blocked)
|
||
|
||
| Ticket | Title | Current status | Issue |
|
||
|--------|-------|----------------|-------|
|
||
| #681 | Apartment generator (server) | backlog | Needs walkable generated world (Layers 5-8 complete). Should be explicitly blocked. |
|
||
| #682 | Apartment rendering (client) | backlog | Blocked by #681, which is blocked by the generation pipeline. |
|
||
| #615 | Tycoon small business starting state | backlog | Feeds #681. Should be blocked behind pipeline completion. |
|
||
|
||
### Tickets that can proceed (Phase 4 per cascade definition)
|
||
|
||
| Ticket | Title | Reasoning |
|
||
|--------|-------|-----------|
|
||
| #694 | Character creation screen | Phase 4: player control scheme on 2-floor test map. Does not require generated world. |
|
||
| #619 | Full character customization | Phase 4: same. Aesthetic-only, works on hand-authored map. |
|
||
| #616 | Verb vocabulary design | Content design. No code dependency on generation pipeline. |
|
||
|
||
### New tickets needed
|
||
|
||
These represent the actual missing implementation work:
|
||
|
||
1. **"Fix WorldTier enum values in generator.rs"** — Replace `Peripheral/Connected/Core` with `Epicenter/Regional/Backwater/Passage/Waypoint` per lead decision L-3. Blocked by nothing. ~0.5 dev-days.
|
||
|
||
2. **"City-to-district decomposition — spec and implementation"** — Define atlas pixel → world tile coordinate mapping, district count per city, district boundary algorithm. Produces `Vec<(DistrictId, world_tile_origin)>` from atlas markers. Blocks all Phase 1/2 work. ~2 dev-days.
|
||
|
||
3. **"DistrictSkeleton Phase 1 generator (Stages 1-2)"** — Classification + block grid. Minimal viable Phase 1 that produces a compilable skeleton. Blocked by ticket 1 and 2. ~3 dev-days.
|
||
|
||
4. **"Add missing DistrictSkeleton fields"** — `vertical_structure`, `breach_only_zones`, `derived_analysis` from workshop spec. Can be stub types initially, but must be present for schema completeness. ~0.5 dev-days.
|
||
|
||
5. **"ChunkData Phase 2 minimal fill"** — Flat walkable floor with wall boundaries. No rooms, no furniture. Blocked by ticket 3. ~2 dev-days (minimal slice).
|
||
|
||
6. **"Wire generator to chunk_streaming"** — `DistrictMap` resource + dispatch in `load_chunk()`. Blocked by tickets 2, 3, 5. ~1 dev-day.
|
||
|
||
---
|
||
|
||
## Testing Strategy Per Layer
|
||
|
||
**Design principle:** Each layer produces deterministic output from a seed. Same seed = same output. This is the primary invariant to test at every layer.
|
||
|
||
| Layer | Test approach | Independent? |
|
||
|-------|--------------|--------------|
|
||
| Atlas (done) | `make regen-db` + `make check-systems-db-stamp` + visual Godot inspection | Yes |
|
||
| City-to-district decomp | Unit test: given N markers, produces M districts with valid positions and no overlaps. Determinism: same seed → same output. | Yes |
|
||
| DistrictSkeleton | Unit test: same seed → identical skeleton (byte-level). Coverage test: 100 seeds → no two identical skeletons. Guarantee audit: all Tier 1 sites present for inhabited districts. | Yes |
|
||
| ChunkData fill | Gauntlet snapshot test (new Gauntlet room for generated district). Visual inspection in `--test-mode SR_LIVE=1`. Walkability consistency: every Phase 2 chunk must have ≥1 walkable tile. | Yes |
|
||
| Chunk streaming integration | Existing `chunk_streaming` tests verify load/unload logic. New: integration test verifying loaded chunks are non-blank after generator wiring. | Partially (streaming logic tested; fill content not yet) |
|
||
|
||
The Gauntlet test world should get a dedicated "generated district" room for Phase 2 validation once Phase 1+2 exist. Per CLAUDE.md rules, this must be a new room, not modification of existing rooms.
|
||
|
||
---
|
||
|
||
## Implementation Priority Order
|
||
|
||
Ordered by what unblocks the most:
|
||
|
||
1. **Fix WorldTier enum** — 0.5 days, unblocks all generator work (nothing runs correctly until this is right)
|
||
2. **City-to-district decomp** — 2 days, the first real gap; unblocks all Phase 1/2 code
|
||
3. **DistrictSkeleton Phase 1 (Stages 1-2 only)** — 3 days, produces first generated skeletons
|
||
4. **Missing DistrictSkeleton fields** — 0.5 days, schema completeness
|
||
5. **ChunkData Phase 2 minimal** — 2 days, produces first walkable generated tiles
|
||
6. **Chunk streaming integration** — 1 day, first end-to-end walkable generated world
|
||
7. **Full Phase 2 with rooms/furniture** — 4+ days (after vertical slice validated)
|
||
8. **Vertical structure / z-levels** — 7 days, Phase 4/5+ (per workshop estimate)
|
||
9. **MobileChunk** — 9.5 days, deferred until static districts work
|
||
|
||
**Total for thinnest vertical slice (flat walkable district):** ~9 dev-days
|
||
|
||
**Formal gate:** Character and apartment work (#681, #682, #615) should be blocked in the ticket system behind the vertical slice milestone (items 1-6 above complete).
|
||
|
||
---
|
||
|
||
## Appendix: Key Architectural Constraints to Preserve
|
||
|
||
These are non-negotiable from the workshop and D-records:
|
||
|
||
- **D-010 determinism:** All generation must consume randomness exclusively through `SimRng`. No `std::hash::DefaultHasher`. Same seed = same world, always.
|
||
- **D-010 BTreeMap:** All `HashMap` usage in generator output types is prohibited. Use `BTreeMap` throughout.
|
||
- **D-108 MobileChunk:** Vessel interiors use the same Phase 2 primitives as static districts. No separate generation path.
|
||
- **D-110 signed z-levels:** `base_z: i8` for positions, `z_levels: u8` for counts. Already implemented correctly in the types.
|
||
- **L-7 XOR prohibition:** In-playthrough events use `DamageOverlay` exclusively. No XOR reseeding.
|
||
- **Two-phase immutability:** Phase 1 output is frozen. All runtime changes go through the overlay/delta layer.
|
||
- **chunk_streaming cadence:** Default 10 ticks (1 game-minute). Phase 2 fill must complete within a single cadence window or be deferred to background.
|