System S-057 assigned to real star GJ 35 (Van Maanen's Star, DG white dwarf at 13.9 ly). Renamed across all content, server code, docs, decisions, wiki lore, and config files. 224 files updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.6 KiB
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