Dependency chain inverted: #611 (NpcBlueprint structs) now goes first and defines the schema contract. Copy team (#609, #610) fills YAML to match Tyre's structs rather than the other way around. #613 (NPC generation pipeline) cancelled and absorbed into #612 — the NPC pipeline is the print loop at the end of the generator binary, not a separate ticket. Ticket descriptions loosened: strip over-specified acceptance criteria, replace with intent + scope boundaries. Phoneme generation explicitly out of scope for #610 (name lists are sufficient). #612 gains a phased approach note (Phase 1: hardcoded stubs, Phase 2: real YAML) so server can build in parallel with copy. Briefings updated to reflect inverted chain, two-ticket server sprint, and exploratory framing: this sprint discovers the right spec, it does not implement a known one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.6 KiB
Sprint 25: Emerge — Server Tasks
Goal: Prove the generator can extrapolate from minimal input — a rural Krenn area AND an industrial Krenn 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 YAML. 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 YAML. Hardcode two zone profiles (rural, industrial stub) and a Krenn culture stub in Rust. Get the generation pipeline and stdout output working end-to-end.
- Phase 2 (#612, late): Swap hardcoded stubs for real YAML 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.yamlCultureProfile— deserializes from culture-krenn.yamlNpcBlueprint— generator output for a single NPC
- All three derive
Serialize,Deserialize(serde + serde_yaml). 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.yamlandcontent/global/culture-krenn.example.yamlshowing 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.
#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 YAML 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 YAML) ──> copy #609 (zone spec) ──┐
──> copy #610 (culture) ──┴──> #612 (generator, Phase 2)
#611 ──────────────────────────────────────────────────────────> #612 (generator, Phase 1 — no YAML needed)
#611 first. Phase 1 of #612 runs in parallel with copy writing #609/#610. Phase 2 of #612 waits for both.
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