Standardized YAML frontmatter on all 115 sprint briefing files across sprints 1-26 with title, description, type, status, sprint number, and team fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.5 KiB
title, description, type, status, sprint, team
| title | description | type | status | sprint | team |
|---|---|---|---|---|---|
| Sprint 9 — Server Briefing | Test client binary, Gauntlet rooms 1-3, golden file suite, subprocess tests | sprint | archived | 9 | server |
Sprint 9: Gauntlet — Server Tasks
Goal: Build the test infrastructure backbone — test client binary, Gauntlet rooms 1-3, golden file suite, and client QA harness — so every future sprint can be validated end-to-end.
Branch: server
Agents: Dudley (simulation dev), Tyre (architect), Hoshe (QA)
Carry-over from Sprint 8
| # | Title | Status | Notes |
|---|---|---|---|
| #480 | Test client binary scaffolding + CLI | backlog | Linchpin — blocks #481, #482, #483, #484. Was Sprint 8 scope but did not start. |
| #481 | Text renderer library | backlog | Blocked by #480. Needed for golden diff and WRONG button (F12). |
New Tickets
| # | Title | Blocked by |
|---|---|---|
| #482 | Layer 3 subprocess test: server_subprocess_sends_snapshot_on_connect | #480 |
| #484 | Test client golden file comparison (JSON diff) | #480, #481 |
| #485 | Golden file test suite: gauntlet_tick_10_matches_golden | #484 |
| #487 | Gauntlet first 3 rooms (Inventory Warehouse, Occlusion Corridor, Pause Chamber) | — |
| #488 | Gauntlet room constants module (server/src/test_world/constants.rs) | #487 |
| #489 | Content runtime validation: boot + tick 10 + snapshot | — |
| #490 | Room reset trigger mechanism | #487 |
Key Decisions
decisions/architecture.md— D-020 (subprocess IPC,ObserverSnapshot,PlayerInput), D-030 (testability architecture, Layer 3 integration tests), D-010 (deterministic simulation, injectable resources)decisions/scope.md— D-065 (smuggler inventory,CarriedBycomponent — Inventory Warehouse room), D-053 (movement stances — Pause Chamber room)decisions/perception.md— D-035 (symmetric shadowcasting — Occlusion Corridor room)
Notes
#480 — Test client binary scaffolding + CLI (tooling/test-client/)
What exists: server/ is a workspace crate (see server/Cargo.toml). The bridge module exports ObserverSnapshot, PlayerInput, read_framed, write_framed from server/src/bridge/. TcpBridge is in server/src/bridge/tcp.rs. The server binary (server/src/main.rs) already implements --test-mode --port 0 and emits LISTENING:{port} on stdout (Sprint 8, #459).
What to deliver: A new Cargo workspace crate at tooling/test-client/ that imports bridge types from the server crate. CLI flags: --connect <port>, --replay <file>, --text (text output), --json (JSON output), --quiet. This is the foundation for every subsequent test client ticket in this sprint — do it first.
Integration point: The server crate must expose read_framed and write_framed as pub from server/src/bridge/framing.rs (verify they are already public; the integration test at server/tests/game_loop.rs already uses them).
#481 — Text renderer library (server/src/bridge/text_renderer.rs)
What exists: ObserverSnapshot is fully defined in server/src/bridge/types.rs (v8, protocol version 8). Fields include entities (Vec), visible_tiles, tick, game_time, player_stance, player_inventory, etc.
What to deliver: format_snapshot_text(&ObserverSnapshot) -> String. Entity labels as kind:entity_id, sorted by distance from player position. Room name from room_at() helper (or "unknown" stub if room system not yet in place). Export as pub from the server crate for tooling/test-client/ consumption. This is also consumed by #495 (WRONG button F12 capture on the client).
#482 — Layer 3 subprocess test: server_subprocess_sends_snapshot_on_connect
What exists: server/tests/game_loop.rs already has a full IPC round-trip test using TcpBridge::accept_on(). The --test-mode --port 0 + LISTENING:{port} pattern is live in server/src/main.rs. The test for #459 already validated the handshake.
What to deliver: A new test at server/tests/layer3.rs. Spawns the server binary with --test-mode --port 0, reads LISTENING:{port} from stdout, connects via TCP (TcpBridge::connect()), sends one PlayerInput, reads one ObserverSnapshot, asserts version == PROTOCOL_VERSION, tick >= 0, entities.len() > 0. This is D-030 Layer 3 — the real subprocess, not a mock.
Gotcha: The server binary must be pre-built before this test runs. Use std::process::Command with the binary path from env!("CARGO_MANIFEST_DIR") or a build script. Look at how server/tests/bridge_tcp.rs handles spawning if there is a prior pattern.
#484 — Test client golden file comparison (JSON diff)
What exists: #480 (test client crate) must be done first. The ObserverSnapshot struct derives Serialize/Deserialize via serde.
What to deliver: A recursive diff_json_values() comparison function in tooling/test-client/src/golden.rs. Sorts object keys before compare (BTreeMap semantics). Pretty-prints field-level diffs on mismatch (path + expected + got). Exit code 1 on any mismatch. This is the comparison engine for #485.
#485 — Golden file test suite: gauntlet_tick_10_matches_golden
What exists: #484 must be done. The determinism regression test at server/tests/determinism.rs (Sprint 8, #466) already validates tick-level determinism. Gauntlet room #487 must be done (provides the map for the replay).
What to deliver: A test in server/tests/ (or via the test client) that runs a 10-tick deterministic replay against the Gauntlet map, serializes the resulting ObserverSnapshot to JSON, and compares against a committed golden file at tests/golden/gauntlet_tick_10.json. First run: generate and commit the golden file. Subsequent runs: fail on any deviation. This is the primary regression guard.
#487 — Gauntlet first 3 rooms (Inventory Warehouse, Occlusion Corridor, Pause Chamber)
What exists: Content loading system is live — server/src/content/ (ContentPlugin, ContentConfig, loader, spawn). The proof room setup in server/src/main.rs (setup_proof_room()) shows the ECS entity spawning pattern. WalkabilityMap is the tile collision resource. The room system needs a new server/src/test_world/ module (Gauntlet-specific, not production content).
What to deliver: Three rooms as hybrid YAML + Rust inject:
- Inventory Warehouse (30x28, 11 entities): tests D-065 inventory system,
CarriedBycomponent - Occlusion Corridor (42x22, 4 entities): tests D-035 LOS shadowcasting, wall placement critical
- Pause Chamber (16x16, 1 entity): tests D-053 pause guard;
--test-modegate check
Room layout: embed in server/src/test_world/rooms/ as YAML, with a Rust loader in server/src/test_world/mod.rs that spawns them via WalkabilityMap + ECS entities. Each room gets a unique room_id string. This module is Gauntlet-only — do not pollute server/src/content/.
Blocks: #488 (constants module), #490 (room reset), #497 (checklist YAML), #496 (room timer), #498 (next 3-4 rooms).
#488 — Gauntlet room constants module (server/src/test_world/constants.rs)
What exists: #487 must be done first. Depends on room definitions from that ticket.
What to deliver: GauntletRoom struct (id, name, spawn_x, spawn_y, width, height), GauntletEntity struct (stable_id, kind, x, y), 8 room constants (one per planned Gauntlet room — stub the unimplemented ones), ROOMS: &[GauntletRoom] array, room_at(x, y) -> Option<&GauntletRoom> lookup. ~100 lines. These stable coordinate references are used by all Gauntlet tests to avoid magic numbers.
#489 — Content runtime validation: boot + tick 10 + snapshot
What exists: ContentPlugin loads from content/ at startup. If content schema is valid YAML but has runtime issues (e.g., NPC references broken entity IDs), the server may panic on tick rather than at load time.
What to deliver: A test at server/tests/content_loading.rs (or extend the existing file) that: starts the server with ContentPlugin, ticks 10 times, reads an ObserverSnapshot, asserts no panic and version == PROTOCOL_VERSION. This is a smoke test for production content. Standalone — does not depend on Gauntlet rooms.
#490 — Room reset trigger mechanism
What exists: #487 (Gauntlet rooms) must be done. ECS has WalkabilityMap, TilePosition, KnowledgeGraph components.
What to deliver:
RoomResetTriggercomponent: marks a tile as a reset plateRoomSnapshotsresource: stores tick-0 entity state per room (positions, KG entries, inventory items)execute_room_resetsystem: on trigger activation, restores entity positions, player KG (room refs only), fog (room tiles), inventory items to tick-0 state
Resets: entity positions and KG, player KG (room refs only — not cross-room knowledge), fog (room tiles only), inventory items. Does NOT reset: game time, player stance, cross-room knowledge, the hub area.
Blocks: #495 (WRONG button F12 depends on reset plate context), #502 (client-side reset UX).
Dependency Chain
#480 (test client binary)
-> #481 (text renderer)
-> #484 (golden diff)
-> #485 (golden suite)
-> #482 (Layer 3 subprocess test)
-> #484 (golden diff)
#487 (Gauntlet rooms 1-3)
-> #488 (room constants)
-> #490 (room reset trigger)
#489 (content runtime validation) — standalone, parallel track
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): sprint 9 gauntlet — test client and gauntlet rooms" --description "body" --base main --head server