From 0f1eda8d125214a28ed09010077e736914d89273 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 6 Mar 2026 20:44:01 +0100 Subject: [PATCH] chore(meta): restructure Sprint 25 per feasibility study MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- docs/backups/settledreach.db.backup | Bin 643072 -> 659456 bytes docs/sprints/sprint-25/copy.md | 44 ++++++++-------- docs/sprints/sprint-25/joint.md | 76 ++++++++++++--------------- docs/sprints/sprint-25/server.md | 77 +++++++++++++++------------- 4 files changed, 97 insertions(+), 100 deletions(-) diff --git a/docs/backups/settledreach.db.backup b/docs/backups/settledreach.db.backup index 00b92db41a8748754babce10ad1fd5a25b3117ba..c9ebe6d8fc8c1ed52fd72e4d975636c18dd1e31a 100644 GIT binary patch delta 4505 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 hguTJ#J7^*jm20A*_|Y{>v4tT104*!QnMm|6` for full details. @@ -22,34 +22,38 @@ Use `tooling/db/ticket show ` for full details. ## Notes +**How this sprint works for copy** + +Server starts first. Tyre (#611) defines `ZoneSpec`, `CultureProfile`, and `NpcBlueprint` as Rust structs and writes example YAML showing the expected format. That YAML is the schema contract. Copy fills real content into that schema — not the other way around. + +Wait for #611 to deliver its example YAML before writing the real files. Coordinate with Tyre at sprint start to agree on file locations (`content/global/zone-identity-spec.yaml` and `content/global/culture-krenn.yaml` are the expected paths, but Tyre's struct design is authoritative). + **#609 — Zone identity spec** -- Output: a YAML or Markdown document defining zone type taxonomy for the Settled Reach's social vocabulary. -- What each zone TYPE means (not a specific location): urban residential, rural agricultural, industrial transit, informal/fringe, commercial, etc. What does each feel like? Who lives or works there? What emotional register does it carry? What is the density, pace, noise level? -- This is a general taxonomy — NOT a spec for a specific village. The generator needs the zone types as input parameters so it can extrapolate any location. -- Downstream consumers: Tyre (generator zone template parameters for #611/#612), Araminta (zone visual grammar), Mellanie (culture-primary voice cards). -- File location: `content/global/zone-identity-spec.md` (or YAML if preferred — coordinate with Tyre on what format #611 needs to ingest). -- Krenn System context: check `decisions/content.md` D-036 amendment and D-128. Station Sova's districts (D-093) are an example of zone types in action but the spec must generalize beyond Sova. +- Output: YAML file the generator deserializes at runtime. Schema defined by Tyre's `ZoneSpec` struct from #611. +- Minimum two zone types with real content: **rural** and **industrial**. These are the two the sprint proof runs. Remaining types can be stubs with plausible values. +- The taxonomy must make the generator produce visibly different output per zone type — if rural and industrial look the same, it has failed. +- Content scope: what varies between zone types (density, pace, social site mix, NPC role distribution). Not prose worldbuilding — structured parameters that the Rust generator can read. +- Do not invent the schema. Read #611's example YAML first. **#610 — Krenn culture profile** -- Output: one full culture profile for Krenn System / Station Sova, usable as generator input constraints and AI content templating prompt seeds. -- Must encode: naming conventions and phonemes, speech patterns and registers (formal/informal), economic values and class markers, attitudes toward work/leisure/authority, sensory palette (what Krenn space looks/smells/sounds like), social norms and taboos, dress and appearance codes. -- D-128: culture is implicit in starting location. This profile IS the cultural context for every generated NPC in the Krenn System — not just flavor text. -- D-121: voice is culture-driven, job modifies. The culture profile must be specific enough that Tyre can use it to constrain generated NPC voice parameters. -- D-123: this profile feeds the AI content templating pipeline. Culture vectors = prompt constraints. The profile must be concrete enough to function as a constraint, not just atmospheric description. -- File location: `content/global/culture-krenn.md` (or YAML — coordinate with Tyre). -- Existing Sova atmosphere notes live in `decisions/content.md` D-036 (amended post-workshop). Pull from those but go deeper on cultural mechanics. +- Output: YAML file the generator deserializes at runtime. Schema defined by Tyre's `CultureProfile` struct from #611. +- Must provide enough cultural signal that generated NPCs feel Krenn, not generic-space-village. +- Sprint scope: **name lists** (not phoneme generation rules — Tyre's generator picks from lists), speech markers, economic values, social norms. +- Phoneme-based name generation is explicitly out of scope for this sprint. A curated list of Krenn-sounding names is sufficient. +- Existing Krenn atmosphere and naming examples in `decisions/content.md` D-036 (amended post-workshop) are a starting point. Go deeper on concrete values (specific speech markers, actual name examples) not broader on atmospheric description. +- Do not invent the schema. Read #611's example YAML first. ## Dependency Chain ``` -#609 (zone identity spec) ──┐ - ├──> server #611 (NpcBlueprint struct) -#610 (Krenn culture profile)┘ └──> #612 (template gen) ──> #613 (NPC pipeline) +server #611 (schema contract) ──> #609 (zone spec YAML) ──┐ + ├──> server #612 (generator) + ──> #610 (culture YAML) ───────┘ ``` -Copy deliverables (#609, #610) are prerequisites for all server work in this sprint. Both can be written in parallel — they are independent of each other. +Server defines the shape. Copy fills it. Both #609 and #610 can be written in parallel once #611 delivers its example YAML. ## PR Workflow diff --git a/docs/sprints/sprint-25/joint.md b/docs/sprints/sprint-25/joint.md index 5f5aec037..fd1659911 100644 --- a/docs/sprints/sprint-25/joint.md +++ b/docs/sprints/sprint-25/joint.md @@ -1,87 +1,74 @@ # Sprint 25: Emerge — Joint -**Goal:** Prove the generator can extrapolate from minimal input — a rural Krenn village from zone type and culture profile alone, no per-location spec. +**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. ## Pre-Sprint | Item | Owner | Blocks | |------|-------|--------| -| Coordinate output format for zone identity spec (#609) | Miri + Tyre | #611 | -| Coordinate output format for culture profile (#610) | Miri + Tyre | #611, #612 | -| Confirm `NpcBlueprint` file location and module wiring | Tyre | #612, #613 | +| Tyre shares example YAML schema from #611 with Miri | Tyre | #609, #610 | +| Agree on file paths for zone spec and culture YAML | Tyre + Miri | #609, #610, #612 | -The critical coordination question: does copy deliver YAML files (which the generator reads at runtime) or Markdown design documents (which Tyre reads and encodes into Rust)? Either works — decide at sprint start. YAML is preferred if Tyre can define a schema upfront; Markdown if the profile needs to be free-form first and codified later. +The critical coordination handoff: #611 defines the Rust structs and writes example YAML showing the schema. That YAML goes to Miri immediately. Copy fills the schema with real content. Server builds Phase 1 of #612 with hardcoded stubs in parallel — does not wait for copy. ## Sprint Completion Proof — Throwaway Render **This is not a test suite. It is an eyeball test.** -The proof is a single standalone invocation that feeds the generator minimal parameters and inspects the output for shape and coherence. - -**Input (the minimum the generator must accept):** -``` -zone_type: "rural" -region: "outside Sova urban zone" -system: "Krenn" -seed: 42 -``` - -Plus the Krenn culture profile (#610) and zone identity taxonomy (#609) — loaded from disk, not hardcoded. - -**NO location-specific spec.** No "the village has a tavern." No bespoke social site list. The generator extrapolates entirely from zone type + culture. - -**Command (to be written as part of #612):** +**Commands:** ```bash cargo run --bin generator-spike -- --zone rural --seed 42 +cargo run --bin generator-spike -- --zone industrial --seed 42 ``` -**Expected output (printed to stdout):** -1. `DistrictSkeleton` summary: zone type, block count, social site placements -2. Generated NPC list: one line per NPC with name, role, 2-3 traits, one observable behavior -3. A sample relationship pair (NPC A knows NPC B as X) +**Expected output per invocation:** +- Zone type and summary (block count, social site list) +- NPC roster: name, role, 2-3 traits, one observable behavior +- Relationship pairs: "A knows B as [type] ([valence])" -**The pass condition (human judgment, not automated):** -- Does the output have *shape*? A rural area should feel different from an industrial one. -- Does it feel like it *belongs in the Krenn System*? Not generic space-village. Krenn-inflected. -- Can you read the NPC relationships from the output? Not infer — *read*. +**Pass condition — compare the two outputs side by side:** +- Rural and industrial produce **different** output shape (different social site mix, NPC role distribution, density) +- Both feel **Krenn** (shared naming conventions, cultural markers) +- NPC relationships are **readable** from text — not inferred, read +- Zone taxonomy does **visible work** (rural vs industrial distinguishable) +- Culture profile does **visible work** (both feel Krenn, not generic) -**The fail condition:** -- Output could be from any game in any setting (culture profile not doing work) -- All rural areas produce identical outputs with different names (zone type not doing work) -- NPC traits are unreadable from text (legibility fails — D-129 gate not met) +**Fail condition:** +- Both outputs look the same with different labels (zone taxonomy not doing work) +- Neither feels Krenn (culture profile not doing work) +- NPC traits unreadable from text (D-129 legibility gate not met) -**Why this framing matters (from the approval discussion):** -The sprint proof deliberately uses minimal input because the generator's entire value proposition is scaling beyond hand-authored content. If it only works with detailed per-location specs, it fails the brief — because the whole point is the player walking into an unspecified area and it still having shape. The "throwaway render" framing is intentional: this is a rough proof, not a polished demo. Pass/fail is a 5-minute eyeball by the team lead. +This sprint **discovers** the right spec — it does not implement a known one. If the output is light and not fully deep, that is expected. The proof is in the pudding: run it, read it, judge it. ## Tickets by Team | Team | # | Title | |------|---|-------| +| server | #611 | NpcBlueprint struct design | | copy | #609 | Zone identity spec | | copy | #610 | Krenn culture profile | -| server | #611 | NpcBlueprint struct design | -| server | #612 | Template assembly generator | -| server | #613 | NPC generation pipeline | +| server | #612 | Template assembly generator (includes NPC pipeline + stdout output) | ## Dependency Chain ``` -copy #609 (zone spec) ──┐ - ├──> server #611 (NpcBlueprint) ──> #612 (template gen) ──> #613 (NPC pipeline) -copy #610 (culture) ───┘ +#611 (structs + schema) ──> #609 (zone spec YAML) ──┐ + ──> #610 (culture YAML) ──┴──> #612 Phase 2 (integrate real YAML) + +#611 ──────────────────────────────────────────────────> #612 Phase 1 (hardcoded stubs, build in parallel) ``` -Copy (#609, #610) runs in parallel and completes first. Server (#611 → #612 → #613) runs sequentially after copy delivers. +#611 first. #609 and #610 in parallel after schema arrives. #612 builds in two phases — Phase 1 with stubs runs alongside copy, Phase 2 integrates real YAML. ## Open Questions | ID | Question | Blocking | |----|----------|---------| -| Q-WTF-033 | AI templating: Claude API, local ollama, or manual for v0.2? | #613, #623 | +| Q-WTF-033 | AI templating: Claude API, local ollama, or manual for v0.2? | #623 (deferred) | | Q-WTF-039 | Character creation screen: portrait render or tile-scale preview? | Sprint 26+ | | Q-WTF-040 | Do creation choices trace into the generated apartment? | Sprint 26+ | -Q-WTF-033 is the most relevant to this sprint. #613 does not need to implement AI templating — that is #623 (deferred). But the pipeline should be designed so AI templating slots in later without a rewrite. Discuss at sprint start. +Q-WTF-033 does not block this sprint. Design #612 so AI templating slots in later without a rewrite — the culture profile's speech markers are already the prompt constraint structure. ## Deferred to Future Sprints @@ -95,6 +82,7 @@ Q-WTF-033 is the most relevant to this sprint. #613 does not need to implement A ## What This Sprint Does NOT Do - No client work (no rendering of generated content) -- No new content authored beyond the zone spec and culture profile +- No new content beyond the zone spec and culture profile YAML files - No AI templating implementation (design for it, don't build it) - No playable session from the generated world +- No ECS entity spawning into a running bevy world (#613 absorbed into #612 as stdout-only proof) diff --git a/docs/sprints/sprint-25/server.md b/docs/sprints/sprint-25/server.md index 719a9f835..eed11b629 100644 --- a/docs/sprints/sprint-25/server.md +++ b/docs/sprints/sprint-25/server.md @@ -1,6 +1,6 @@ # Sprint 25: Emerge — Server Tasks -**Goal:** Prove the generator can extrapolate from minimal input — a rural Krenn village from zone type and culture profile alone, no per-location spec. +**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) @@ -9,64 +9,69 @@ | # | Title | Blocked by | |---|-------|------------| -| #611 | NpcBlueprint struct design | #609 (copy), #610 (copy) | -| #612 | Template assembly generator | #609, #610, #611 | -| #613 | NPC generation pipeline | #611, #612 | +| #611 | NpcBlueprint struct design | — (starts immediately) | +| #612 | Template assembly generator | #611, #609 (copy), #610 (copy) | Use `tooling/db/ticket show ` for full details. ## Key Decisions -- `decisions/scope.md` — D-114 (generator-first proof-of-life), D-119 (generator spike critical path), D-115 (skills + bookmark only for creation) +- `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, `DistrictType`, `ZoningType`, `SocialSitePlacement`, `TriangleAssignment`, `BlockSkeleton`. This is the Phase 1 *data model* — struct definitions for what a generated district looks like. The *generator logic* that produces these structs from input parameters does not yet exist. -- **`server/src/npc/generate.rs`** — existing NPC generator taking a `RoleDefinition` (the 10-axis model: Want, Secret, Relationships, Tolerance, DailyRoutine, InformationInventory, Contentment, PersonalityTraits, TellSystem, SkillSet). Generates a fully-populated NPC entity via `SimRng` (deterministic, seeded). Sprint 25 work extends this pipeline, it does not replace it. -- **`server/src/npc/`** — full NPC component set: `awareness.rs`, `background.rs`, `disclosure.rs`, `generate.rs`, `interaction.rs`, `mod.rs`, `mood.rs`, `relationships.rs`, `routine.rs`, `tell_state.rs`, `tolerance.rs`, `trait_modifiers.rs`, `vision.rs`. -- **`server/src/content/`** — content loading: `entanglement.rs`, `hot_reload.rs`, `instantiation.rs`, `line_pool.rs`, `loader.rs`, `spawn.rs`, `template.rs`, `types.rs`. +- **`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** -- Blocked by #609 and #610 (copy team delivers these first). Read those documents before writing the struct. -- This struct is the *interface* between the generator and all downstream systems: rendering, voice, AI templating, simulation. -- Must encode: trait set (from D-129), observable behavior surface (what a bystander can read), relationships (Sims + Rimworld style per D-129), role/occupation, cultural markers (feeds D-121 voice pipeline and D-123 AI templating), zone-type affiliation. -- The existing `RoleDefinition` in `npc/generate.rs` covers the 10-axis model. `NpcBlueprint` is a higher-level generator output that wraps role + cultural context + zone parameters. Think of `RoleDefinition` as "what the generator uses internally" and `NpcBlueprint` as "what the generator exports." -- Write the struct in `server/src/npc/blueprint.rs` (new file). Add a D-record reference in `decisions/architecture.md` or `decisions/content.md` when the struct is settled. -- Must be serializable (Serde) for inspection output (the sprint proof dumps the blueprint JSON to stdout). +- 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** +**#612 — Template assembly generator (absorbs #613)** -- Blocked by #609, #610, #611. -- Input: zone type parameter (from zone identity spec), culture profile (from Krenn culture profile), seed (u64). No per-location spec. The whole point is extrapolating from minimal input. -- Output: a `DistrictSkeleton` (already defined in `server/src/simulation/generator.rs`) with `SocialSitePlacement` entries populated and `NpcBlueprint`s attached to each role slot. -- NOT procedural geography — template assembly. The generator picks a zone template from the zone identity taxonomy and fills it. The template provides the shape; the culture profile and seed provide the variety. -- Write generator logic in `server/src/simulation/generator.rs` (add below existing structs) or extract to `server/src/simulation/template_assembly.rs` — architect's call. -- The generator must be callable standalone (not as a bevy system) for the sprint proof (see joint.md). -- Determinism: use `SimRng` (ChaCha20, existing in `server/src/simulation/rng.rs`). Same seed + same zone type + same culture profile → same output. - -**#613 — NPC generation pipeline** - -- Blocked by #611 and #612. -- Wire the template generator output into actual NPC entity spawning. -- Input: `DistrictSkeleton` with `NpcBlueprint`s → spawn ECS entities using the existing `npc/generate.rs` pipeline, extended to accept cultural markers from the blueprint. -- The existing `RoleDefinition` in `npc/generate.rs` needs a cultural marker field (or the blueprint drives trait selection to match the culture profile). -- Proof-of-life test (in the sprint proof): print generated NPC trait sets and observable behaviors to stdout. A human reading the output should be able to say "yes, this NPC could plausibly exist in the Krenn System." -- D-129: traits + behavior first. The proof gate is not rendering — it is legibility from text output alone. +- Blocked by #611. Build Phase 1 before #609/#610 arrive; integrate in Phase 2. +- Binary: `cargo run --bin generator-spike -- --zone --seed ` (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 ``` -copy #609 (zone spec) ──┐ - ├──> #611 (NpcBlueprint) ──> #612 (template gen) ──> #613 (NPC pipeline) -copy #610 (culture) ───┘ +#611 (structs + example YAML) ──> copy #609 (zone spec) ──┐ + ──> copy #610 (culture) ──┴──> #612 (generator, Phase 2) +#611 ──────────────────────────────────────────────────────────> #612 (generator, Phase 1 — no YAML needed) ``` -Server work begins when copy delivers #609 and #610. #611 must be complete before #612 starts. #613 follows #612. +#611 first. Phase 1 of #612 runs in parallel with copy writing #609/#610. Phase 2 of #612 waits for both. ## PR Workflow