Files
settled-reach/docs/sprints/sprint-25/server.md
T
jpmschweitzerandClaude Sonnet 4.6 58d2e3b695 chore(meta): add feasibility warnings to Sprint 25 briefings
server.md: four warnings from Troblum — generate_npc() requires a live
bevy World (stub routine generation in Phase 1), cultural text assembly
is a new code path not a one-liner, DayPhase alias collision in
generator.rs, schema negotiation takes rounds.

joint.md: confidence 15% note at top. Intra-zone variance test added
(rural seed 42 vs rural seed 43 — coherence within type, variance
across seeds). Pass conditions restructured into three explicit
comparisons: cross-type, intra-type, culture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 20:54:51 +01:00

7.0 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) — 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 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.yaml
    • CultureProfile — deserializes from culture-krenn.yaml
    • NpcBlueprint — generator output for a single NPC
  • All three derive Serialize, Deserialize (serde + serde_yaml).
  • 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.yaml and content/global/culture-krenn.example.yaml 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.

#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 YAML 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 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.

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 YAML draft from copy will not deserialize cleanly. 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