Files
settled-reach/docs/sprints/sprint-25/server.md
T
jpmschweitzer 23d9ff0a58 Merge remote-tracking branch 'origin/main' into planning
# Conflicts:
#	CHANGELOG.md
#	content/_meta/README.md
#	content/_meta/npc-authoring-style-guide.md
#	wiki/_templates/cultural-group.md
#	wiki/_templates/institution.md
#	wiki/_templates/star-system.md
#	wiki/characters/devra.md
#	wiki/characters/drin.md
#	wiki/characters/harek.md
#	wiki/characters/lera-sessik.md
#	wiki/characters/maret-korr.md
#	wiki/characters/naia-tamm.md
#	wiki/characters/nils-davan.md
#	wiki/characters/pell.md
#	wiki/characters/renn.md
#	wiki/characters/resha.md
#	wiki/characters/sabel.md
#	wiki/characters/sera-venn.md
#	wiki/characters/torek-lintar.md
#	wiki/characters/voss.md
#	wiki/star-systems/krenn/index.md
2026-03-14 00:24:53 +01:00

105 lines
7.7 KiB
Markdown

---
title: "Sprint 25 — Server Briefing"
description: "NpcBlueprint struct design, template assembly generator"
type: sprint
status: archived
sprint: 25
team: "server"
---
# Sprint 25: Emerge — Server Tasks
**Goal:** Prove the generator can extrapolate from minimal input — a rural Van Maanen's Star area AND an industrial Van Maanen's Star zone from zone type and culture profile alone, no per-location spec. Two zone types, one culture, side-by-side comparison.
**Branch:** `server`
**Agents:** Dudley (simulation dev), Tyre (architect)
## New Tickets
| # | Title | Blocked by |
|---|-------|------------|
| #611 | NpcBlueprint struct design | — (starts immediately) |
| #612 | Template assembly generator | #611, #609 (copy), #610 (copy) |
Use `tooling/db/ticket show <id>` for full details.
## Key Decisions
- `decisions/scope.md` — D-114 (generator-first proof-of-life), D-119 (generator spike critical path)
- `decisions/content.md` — D-122 (all NPCs generated), D-128 (culture implicit in location), D-129 (NPC personality: traits + behavior first), D-121 (voice culture-driven), D-123 (AI content templating via culture vectors)
- `decisions/architecture.md` — D-012 (chunk-based map, borderless generation future)
## What Exists
- **`server/src/simulation/generator.rs`** (576 lines) — `DistrictSkeleton` data model and related enums. Struct definitions only — no production generation logic yet.
- **`server/src/npc/generate.rs`** — `RoleDefinition`-driven 10-axis NPC generator using `SimRng` (ChaCha20, deterministic). This pipeline survives; `NpcBlueprint` wraps above it and feeds into it.
- **`server/src/simulation/rng.rs`** — `SimRng`. Use this for all randomness in the generator binary.
- **`server/src/npc/`** — full NPC component set including `mood.rs`, `relationships.rs`, `routine.rs`, `trait_modifiers.rs`.
## Notes
**Phased approach**
This sprint discovers the right spec — it does not implement a known one. Three phases:
- **Phase 0 (#611):** Define the Rust structs (`ZoneSpec`, `CultureProfile`, `NpcBlueprint`) and write example RON files. Build the RON validator CLI. Share with copy team immediately — this unblocks #609 and #610.
- **Phase 1 (#612, early):** Build the generator binary with hardcoded test data. Do not wait for copy to finish their RON files. Hardcode two zone profiles (rural, industrial stub) and a Van Maanen's Star culture stub in Rust. Get the generation pipeline and stdout output working end-to-end.
- **Phase 2 (#612, late):** Swap hardcoded stubs for real RON loading from disk. Wire in copy's actual #609 and #610 files. Run the two-zone proof.
This phasing means the copy team's blocking relationship is on the final integration, not the generator build. Server can move through Phase 0 and Phase 1 in parallel with copy writing #609/#610.
**#611 — NpcBlueprint struct design**
- Starts immediately. No blockers.
- Define three structs in `server/src/npc/blueprint.rs` (new file):
- `ZoneSpec` — deserializes from zone-identity-spec.ron
- `CultureProfile` — deserializes from culture-van-maanens-star.ron
- `NpcBlueprint` — generator output for a single NPC
- All three derive `Serialize`, `Deserialize` (serde + `ron`). **Use RON format, not YAML/JSON.** RON is Rust-native, struct-aware, supports enums and comments. The Rust structs ARE the schema — no separate schema file to maintain.
- `NpcBlueprint` fields: name (String), role (occupation), traits (Vec of trait enum), observable_behaviors (Vec<String>), cultural_markers (speech register, filler words from culture profile), relationships (Vec of (npc_id, relationship_type, valence)).
- Use a spike-specific `SpikeOutput` struct for the binary's top-level output — do NOT couple to `DistrictSkeleton` for the proof. Keep the spike isolated.
- Key deliverable: write `content/global/zone-identity-spec.example.ron` and `content/global/culture-van-maanens-star.example.ron` showing the schema copy must fill. Share these with Miri before copy starts writing real content.
- Add a note in the file header pointing to the tickets (#609, #610) that fill these schemas with real content.
- **Build a RON validator CLI** (`tooling/validate-content <file.ron>`) that deserializes into the actual Rust structs and prints errors. This is the copy team's lint tool — they run it to check their files without needing to compile the server. ~20 lines of Rust, ship it as part of #611.
**#612 — Template assembly generator (absorbs #613)**
- Blocked by #611. Build Phase 1 before #609/#610 arrive; integrate in Phase 2.
- Binary: `cargo run --bin generator-spike -- --zone <type> --seed <n>` (new binary in `server/src/bin/`).
- Phase 1: hardcoded `ZoneSpec` and `CultureProfile` stubs in Rust. Focus on the generation logic and output formatting.
- Phase 2: load zone spec and culture RON from disk at runtime. Zone taxonomy is the file — adding a new zone type requires zero Rust changes.
- Determinism: `SimRng` seeded from the `--seed` flag. Same inputs = same output.
- NPC generation: use the existing `npc/generate.rs` pipeline. `NpcBlueprint` maps to `RoleDefinition` via a conversion method. The blueprint's cultural markers bias trait selection.
- Stdout output per invocation: zone type header, NPC list (name, role, traits, one observable behavior), relationship pairs ("A knows B as colleague (positive)").
- Sprint proof runs twice: `--zone rural --seed 42` and `--zone industrial --seed 42`. The comparison is the test.
## Dependency Chain
```
#611 (structs + example RON + validator) ──> copy #609 (zone spec) ──┐
──> copy #610 (culture) ──┴──> #612 (generator, Phase 2)
#611 ──────────────────────────────────────────────────────────────────> #612 (generator, Phase 1 — no RON needed)
```
#611 first. Phase 1 of #612 runs in parallel with copy writing #609/#610. Phase 2 of #612 waits for both.
## Feasibility Warnings
From Troblum's pre-sprint review. Read before starting.
1. **`generate_npc()` requires a live bevy World.** The existing function in `npc/generate.rs` takes `TilePosition`, `StableId`, and a real `bevy_ecs::World`. The spike binary has none of these. Do not attempt to instantiate a full World for text output — stub or strip routine generation in Phase 1. Wire only the axes that produce inspectable output (traits, relationships, cultural markers). Full ECS wiring is deferred.
2. **Cultural text assembly is the real work of #612.** The existing generator produces enum variants and placeholder strings (`format!("{} has a {:?} secret", ...)`). There is no cultural text surface in the codebase today. Getting `CultureProfile` fields to appear in NPC output is a new code path — budget time for it, it is not a one-liner.
3. **`DayPhase` name collision.** `server/src/simulation/generator.rs` defines `DayPhase = String` as a stub type alias, shadowing the real `DayPhase` enum in `server/src/simulation/time.rs`. Use the real enum explicitly or alias the stub out of scope before the spike binary sees both. Do not let the collision silently compile to the wrong type.
4. **Schema negotiation takes rounds.** The first RON draft from copy will not deserialize cleanly — the validator will catch this early. Build in slack between Phase 1 and Phase 2 — expect at least one round of struct adjustments after seeing real content.
## PR Workflow
When ready to submit, create a PR with `tea` CLI. All flags are required to avoid TTY prompts (see CLAUDE.md "Gitea access" section):
```bash
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): generator spike — NpcBlueprint, template assembly, NPC pipeline" --description "body" --base main --head server
```