# 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
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) —DistrictSkeletondata model and related enums. Struct definitions only — no production generation logic yet.server/src/npc/generate.rs—RoleDefinition-driven 10-axis NPC generator usingSimRng(ChaCha20, deterministic). This pipeline survives;NpcBlueprintwraps 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 includingmood.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.ronCultureProfile— deserializes from culture-van-maanens-star.ronNpcBlueprint— 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. NpcBlueprintfields: 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
SpikeOutputstruct for the binary's top-level output — do NOT couple toDistrictSkeletonfor the proof. Keep the spike isolated. - Key deliverable: write
content/global/zone-identity-spec.example.ronandcontent/global/culture-van-maanens-star.example.ronshowing 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 inserver/src/bin/). - Phase 1: hardcoded
ZoneSpecandCultureProfilestubs 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:
SimRngseeded from the--seedflag. Same inputs = same output. - NPC generation: use the existing
npc/generate.rspipeline.NpcBlueprintmaps toRoleDefinitionvia 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 42and--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.
-
generate_npc()requires a live bevy World. The existing function innpc/generate.rstakesTilePosition,StableId, and a realbevy_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. -
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. GettingCultureProfilefields to appear in NPC output is a new code path — budget time for it, it is not a one-liner. -
DayPhasename collision.server/src/simulation/generator.rsdefinesDayPhase = Stringas a stub type alias, shadowing the realDayPhaseenum inserver/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. -
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