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

7.7 KiB

title, description, type, status, sprint, team
title description type status sprint team
Sprint 25 — Server Briefing NpcBlueprint struct design, template assembly generator sprint archived 25 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.rsRoleDefinition-driven 10-axis NPC generator using SimRng (ChaCha20, deterministic). This pipeline survives; NpcBlueprint wraps above it and feeds into it.
  • server/src/simulation/rng.rsSimRng. 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), 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):

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