Files
settled-reach/docs/sprints/sprint-22/server.md
T
jpmschweitzer 23d9ff0a58 Merge remote-tracking branch 'origin/main' into planning
# 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
2026-03-14 00:24:53 +01:00

9.9 KiB

title, description, type, status, sprint, team
title description type status sprint team
Sprint 22 — Server Briefing Signed z-level fix, NPC pool generation, triangle instantiation, contamination activation sprint archived 22 server

Sprint 22: Wire — Server Tasks

Goal: Fix the fog regression, wire the authored Sova content into a production population and triangle set, and activate the contamination pacing layer that gives the vertical slice its 30-minute shape.

Branch: server Agents: Dudley (simulation dev), Tyre (arch), Hoshe (QA)

New Tickets

# Title Blocked by
#565 Fix signed z-level addressing: base_z u8 → i8
#567 Add modifications: Vec stub on chunk and MobileChunk
#176 NPC pool generation
#188 Triangle instantiation from authored content #176
#254 Contamination activation mechanic #188, Q-017
#509 Fuzzy tests for dynamic/procedural maps
#512 Zone Gate room implementation

Use tooling/db/ticket show <id> for full details on any ticket.

Key Decisions

  • decisions/architecture.md — D-010 (deterministic simulation, BTreeMap over HashMap), D-026 (simulation tiers), D-041 (knowledge graph data model)
  • decisions/content.md — D-023 (three-tier content model), D-024 (10-axis NPC model), D-025 (social site as atomic template unit), D-028 (dialogue tagged pools), D-029 (population entanglement ratio 30/50/20), D-034 (THE FRIEND pattern), D-036 (Sova Transit District setting)
  • decisions/scope.md — D-027 (vertical slice success criteria), D-087 (3 active forks, 2 passive tensions), D-089 (self-contained triangle forks, no cross-triangle cascade)

Open Questions to Resolve Early

  • Q-017: Triangle pressure threshold — What events increase pressure? What numeric threshold triggers confrontation situation? Assigned to Gestalt and Paula. Resolve before #254 starts — the contamination mechanic needs a pressure model. If Q-017 is unresolved by the time #188 lands, implement #254 with a timer-based fallback (tick count, e.g., 1800 ticks = 30 game-minutes) and leave the pressure threshold as a named constant for Q-017 to fill in.

Notes

#565 — Fix signed z-level addressing: base_z u8 → i8

  • Decision D-110 (pending formal filing): change base_z from u8 to i8 in MultiBlockReservation, FloorZone.z_level, and all structs that reference a z-level position (not a count). z_levels counts remain u8.
  • Mechanically a cascading type change — no logic changes, only type propagation through ~20 structs.
  • Grep starting points: server/src/simulation/spatial.rs, server/src/simulation/zone.rs, and any struct with a field named z_level or base_z.
  • Add a doc comment on DistrictSkeleton (if present) clarifying the distinction: base_z: i8 (lowest floor, can be negative for sub-basement) vs z_levels: u8 (count, always positive).
  • Acceptance: cargo test -p server green, clippy clean.

#567 — Add modifications: Vec stub on chunk and MobileChunk

  • Decision D-111/D-112 (pending formal filing): DLC scope stub. Entry point for future player construction system.
  • Add modifications: Vec<Modification> (initially an empty Vec) to both static chunk entities and MobileChunk entities.
  • Define Modification enum/struct in a new server/src/simulation/modification.rs with at minimum: position: TilePosition, modification_type: ModificationType (enum stub), placed_at_tick: u64.
  • Wire into SaveStateV1 (server/src/simulation/save_state.rs) so player-modified spaces round-trip through save/load. The field can be Vec<Modification> with #[serde(default)] — empty for now, but the save slot exists.
  • No construction logic ships. This is a data model stub only.
  • Acceptance: a round-trip save/load test asserts the modifications field serializes and deserializes without data loss (even when empty).

#176 — NPC pool generation

  • This is the key production startup ticket. The Gauntlet uses hand-crafted setup_gauntlet(). The production path needs an equivalent that loads from content/ and spawns the Sova NPC pool.
  • The authored content already exists: 23 NPC YAML files at content/campaigns/main/systems/van-maanens-star/stations/sova/districts/transit/npcs/. The content loader (server/src/content/loader.rs) already loads DistrictContent. The spawn module (server/src/content/spawn.rs) already has spawn_district_npcs() and resolve_cross_references().
  • The gap: there is no production startup path that calls these. The server boots into Gauntlet (setup_gauntlet) or does nothing (without the gauntlet feature). Add a production startup system that: (1) loads content/campaigns/main/campaign.yaml, (2) resolves the Sova district, (3) calls spawn_district_npcs() + resolve_cross_references(), (4) applies the 30/50/20 entanglement ratio from D-029 to mark NPCs as flat/mundane/intrigue-entangled via a new EntanglementTag component.
  • Entanglement ratio: seed the assignment via SimRng (D-010 determinism). The 17 authored Sova NPCs are pre-assigned by content (YAML field triangle_membership). The entanglement ratio applies to any procedurally generated padding NPCs.
  • Acceptance: integration test (non-gauntlet boot) asserts 17+ NPCs spawn with correct StableId assignments and at least one EntanglementTag::Intrigue entity.

#188 — Triangle instantiation from authored content

  • Depends on #176. The 5 triangle YAML files exist at content/campaigns/main/systems/van-maanens-star/stations/sova/districts/transit/triangles/ (hub-power, bar-tensions, informant-question, worried-knowledge, worried-partner).
  • The instantiation engine (server/src/content/instantiation.rs) already handles FullTemplateDefTriangleState. This ticket wires the authored Sova triangles (loaded from YAML) into that engine, rather than template-generated triangles.
  • Per D-087: 3 active forks (T1, T2, T4 = hub-power, bar-tensions, informant-question) and 2 passive tensions (T3, T5 = worried-knowledge, worried-partner). The TriangleState component must reflect this classification.
  • Per D-089: self-contained forks. No cross-triangle cascade. Each TriangleState escalates independently.
  • Cross-template triangle (D-024): bar-tensions links NPCs across the logistics-hub and bar templates. Ensure TemplateReferenceMap entries are populated for cross-template role assignments.
  • Acceptance: after production startup, query<TriangleState>() returns 5 triangle entities: 3 tagged Active (forks), 2 tagged Passive (tensions). All 5 have valid role assignments to existing NPC entities.

#254 — Contamination activation mechanic

  • Blocked by #188. Blocked by Q-017 resolution (or timer fallback — see Open Questions above).
  • The StorytellerPlugin in server/src/storyteller/mod.rs is currently a stub. This ticket adds the v0.1 contamination system.
  • Minimum viable: a Bevy system that fires after a configurable tick threshold (CONTAMINATION_DELAY_TICKS, default 1800 = 30 game-minutes at 10 tps). On firing: (1) set a ContaminationActive resource to true, (2) push pressure on all Active triangles (increment TriangleState::pressure by a configured delta), (3) emit a ContaminationEvent that the monologue system can hook for "the air feels different" NPC behavioural shifts.
  • This gives the session its shape: 30 minutes of daily life, then escalation begins. The contamination is not an event the player sees directly — it's the upstream cause of NPC deviations, tell escalation, and eventually confrontation-threshold crossings.
  • If Q-017 resolves before this ticket ships: wire the actual pressure threshold from that resolution. If not: the CONFRONTATION_THRESHOLD constant is the hook point, set to a reasonable default (e.g., 75 on a 0-100 scale).
  • Acceptance: integration test runs 1801 ticks, asserts ContaminationActive resource is set and all Active TriangleState entities have pressure > 0.

#509 — Fuzzy tests for dynamic/procedural maps

  • Part of QA epic #455. Randomized testing of procedurally generated maps against structural invariants.
  • Invariants to assert: (1) all walkable tiles are reachable from the player start position (connectivity), (2) no entity spawns outside the map bounds, (3) every door entity has at least one adjacent walkable tile on each side, (4) tile count within expected range (±20% of configured density).
  • Use SimRng with varying seeds (e.g., 50 seeds, 1ms timeout each). proptest or a manual seed loop both acceptable.
  • Acceptance: cargo test -p server -- fuzzy_map passes with at least 50 seed variations, each asserting all 4 invariants.

#512 — Zone Gate room implementation

  • Part of QA epic #455. Gauntlet room for testing zone transitions.
  • Add to server/src/test_world/rooms/ following the existing room pattern (see shift_change.rs for reference).
  • The room needs: two distinct zones (e.g., ZoneId::Terminal and ZoneId::Corridor), a walkable path crossing the zone boundary, a door entity on the boundary, and a reset plate.
  • Next available StableId range starts at 75 (see test_world/mod.rs for the allocation table). Update the table comment.
  • Acceptance: golden file test for this room passes; zone-crossing event fires when player entity crosses the boundary tile.

Dependency Chain

#565 (z-level type fix) — standalone
#567 (modifications stub) — standalone
#509 (fuzzy map tests) — standalone
#512 (zone gate room) — standalone

#176 (NPC pool generation)
  → #188 (triangle instantiation from authored content)
    → #254 (contamination activation)  [also blocked by Q-017 / timer fallback]

Parallel tracks: #565, #567, #509, #512 can all start immediately. #176 is the chain head — start it first after those are underway.

PR Workflow

When ready to submit, create a PR with tea CLI:

tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): Sprint 22 — NPC pool, triangles, contamination" --description "body" --base main --head server