docs(briefings): add Sprint 11 Combine briefings

Server (5 tickets), client (2 tickets), and joint coordination
briefings for the Gauntlet completion sprint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 09:54:08 +01:00
co-authored by Claude Opus 4.6
parent e2d2211ff6
commit 6f9a53cb1f
3 changed files with 320 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
# Sprint 11: Combine — Client Tasks
**Goal:** Complete the Gauntlet test matrix with final interaction-combination rooms and cross-room scenarios, deliver the WRONG-button bug-capture system, resolve open implementation questions OQ-07 and OQ-18, and add contraband detection — hardening the system as a fully covered, testable unit.
**Branch:** `client`
**Agents:** Stig (UI/client dev), Tyre (arch review), Hoshe (QA)
---
## New Tickets
| # | Title | Blocked by |
|---|-------|------------|
| #507 | WRONG button full captures (input history ring buffer, snapshot history, replay seed) | #495 (done) |
| #522 | Resolve OQ-07: no-insert interaction behavior (diegetic test) | — |
Use `db/connectors/ticket show <id>` for full details.
---
## Key Decisions
- `decisions/architecture.md` — D-020 (Godot/Rust IPC, ObserverSnapshot as the only data crossing the boundary), D-030 (testability architecture — JSON output format for bug reports)
- `decisions/perception.md` — D-056 (cursor states — insert-styled geometric, diegetic test: labels render on z-layer 6, disappear if insert is off), D-057 (entity interaction — vertical list, diegetic test referenced), D-048 (neural insert overlay — visual design)
---
## Open Questions to Resolve Early
- **OQ-07 (#522): No-insert interaction behavior** — Resolve early (day 1-2) because the answer determines whether #507's WRONG button output includes cursor-state data in the capture, and whether the entity interaction system (#432) needs a code path for insert-off mode. Three candidate resolutions: (a) cursor reverts to default shape only, no verb labels; (b) cursor does not change at all; (c) non-insert fallback renders prompts via alternative channel. Resolve, document as a decision amendment to D-056 or D-057, then implement.
---
## Notes
**#507 — WRONG button full captures (input history ring buffer, snapshot history, replay seed)**
- Existing: The WRONG button MVP (#495, done Sprint 9) is in place as a `bug_report.gd` autoload. F12 pauses, captures a single `ObserverSnapshot` (JSON), runs the text renderer, saves tick/room/seed and a tester description to `tests/bug-reports/gauntlet-t{tick}-{timestamp}/`. The MVP is a single-tick point-in-time capture.
- Deliver: Upgrade the bug capture system to a rolling 60-tick history:
- **Input history ring buffer:** Maintain a 60-entry circular buffer of `PlayerInput` arrays in `bug_report.gd`. Every tick, push the current tick's input(s) onto the buffer (oldest entry evicted when full). On F12 capture, flush the last 60 ticks of inputs to `inputs.jsonl` (one JSON array per line — matches the replay format from `tooling/test-client/src/replay.rs`).
- **Snapshot history:** Maintain a parallel 60-entry circular buffer of `ObserverSnapshot` JSON strings. Flush to `snapshots.jsonl` on capture (one snapshot per line). Allows replay of the exact 60-tick window leading to the bug.
- **Replay seed:** Include the server's current RNG seed in the capture output (requires the server to send the seed in the `ObserverSnapshot` or via a metadata message). Output as `seed.txt` in the bug report directory.
- **Room metadata:** Already present in MVP — confirm it includes the room name and map identifier.
- Output directory format is unchanged: `tests/bug-reports/gauntlet-t{tick}-{timestamp}/`.
- Integration: The `inputs.jsonl` output must be valid input for `tooling/test-client --replay <file>` (Sprint 10 #483). Test this by replaying a captured session against a fresh server — it should reproduce the same snapshot sequence.
- The ring buffer must add negligible per-tick cost — pre-allocate the 60-slot arrays at startup. Do not allocate on every tick.
- Non-obvious: The server RNG seed may need a new field in `ObserverSnapshot` or a dedicated handshake message. Coordinate with the server team if a protocol change is needed. If the seed is already present in an existing field, document which field.
**#522 — Resolve OQ-07: no-insert interaction behavior (diegetic test)**
- Existing: D-056 cursor states and D-057 entity interaction both reference the diegetic test: interaction labels render on z-layer 6 (insert overlay) and disappear if the insert is off. `client/scripts/rendering/cursor_renderer.gd` handles cursor state transitions. The entity interaction vertical list is in `client/scripts/rendering/` (likely `entity_renderer.gd` or a dedicated interaction UI file, per #432).
- Deliver: Resolve OQ-07 with one of three options, implement, and document:
- **(a) Cursor reverts to default shape only, no verb labels** — cursor state machine still fires (shape changes: default → entity hover bracket or X-shape on object hover), but the tooltip/verb label layer is suppressed because z-layer 6 (insert overlay) has no data to render. This is the most diegetically consistent option: the character still physically orients to the target, but receives no information from their insert.
- **(b) Cursor does not change at all** — insert-off mode means the cursor_renderer.gd stays in Default state regardless of what the cursor is over. Full suppression.
- **(c) Non-insert fallback** — prompts appear via a non-insert visual channel (e.g., world-space text at z-layer 2 or 3). Least diegetically consistent; least likely to be the right answer given D-056's "diegetic test" framing.
- After resolution: amend the relevant decision (D-056 or D-057) with the OQ-07 resolution note, and update `client/scripts/rendering/cursor_renderer.gd` and the interaction list renderer to enforce the chosen behavior when `insert_active == false`.
- This is primarily a design resolution + small implementation task. Effort: 0.5d for decision, up to 0.5d for code.
---
## Dependency Chain
```
#507 (WRONG button full captures) — standalone (#495 done), parallel
#522 (OQ-07 resolution) — standalone, resolve day 1-2
```
Both tickets are independent. No cross-team blockers. Client can run both tracks in parallel from day 1.
**Watch:** If #507 requires a new field on `ObserverSnapshot` for the RNG seed, that is a server protocol change. Raise with the server team immediately — do not wait until the ticket is otherwise complete.
---
## 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):
```bash
tea pr create --repo jpmschweitzer/settled-reach --login schweitz \
--title "feat(client): sprint 11 combine — client" \
--description "body" --base main --head client
```
+137
View File
@@ -0,0 +1,137 @@
# Sprint 11: Combine — Joint / Integration
**Goal:** Complete the Gauntlet test matrix with final interaction-combination rooms and cross-room scenarios, deliver the WRONG-button bug-capture system, resolve open implementation questions OQ-07 and OQ-18, and add contraband detection — hardening the system as a fully covered, testable unit.
**Agents:** Dudley, Tyre, Hoshe (server); Stig, Tyre, Hoshe (client)
---
## Pre-Sprint Decisions
| Item | Status | Needed by |
|------|--------|-----------|
| OQ-18 resolution (#524) | Resolve day 1-2. Output: new D-NNN decision in `decisions/`. May require update to dialogue filter in #305. | Downstream content work (unblocked immediately on resolution) |
| OQ-07 resolution (#522) | Resolve day 1-2. Output: D-056 or D-057 amendment. | #507 (WRONG button — cursor state data in captures) |
| RNG seed in ObserverSnapshot | Client team must confirm whether seed is already present in the snapshot wire format. If not, server team must add it. Raise on day 1. | #507 (replay seed capture) |
No other decisions are required before Sprint 11 begins. All other tickets operate within confirmed decisions.
---
## Cross-Team Dependencies
```
Server #504 (Sprint Gauntlet, Eavesdrop Alcove, Confrontation Stage)
→ Server #506 (cross-room transitions T1-T8)
Client #507 (WRONG button full captures)
→ potential Server protocol change (RNG seed field on ObserverSnapshot)
raise day 1 if needed; server team to respond same day
Server #524 (OQ-18 resolution)
→ no client code change, but unblocks copy team content work
Client #522 (OQ-07 resolution)
→ cursor_renderer.gd + interaction list renderer update (client-only)
```
**Critical path:** `Server #504 → Server #506`. Server team should start #504 immediately. All other tickets are independent parallel tracks.
**Potential protocol dependency:** If #507 requires adding a seed field to `ObserverSnapshot`, this is a server change that the client depends on. Identify this on day 1 and route it through the server team as a minimal, additive struct field with `#[serde(default)]` so the client handles older snapshots gracefully during development.
---
## Sprint Completion Proof
Sprint 11 is complete when all of the following are observable:
**Gauntlet expansion (server #504)**
- Server boots with Gauntlet content and all 10 rooms load without panic (7 from Sprint 10 + 3 new: Sprint Gauntlet, Eavesdrop Alcove, Confrontation Stage)
- Room constants for all 3 new rooms in `server/src/test_world/constants.rs`
- Sprint Gauntlet: player sprints past an NPC → interaction buffer empty during sprint, anomaly monologue fires retroactively post-sprint
- Eavesdrop Alcove: player in Careful stance at eavesdrop position → World SFX boost observable in log, conversation murmur event emitted
- Confrontation Stage: player confronts NPC → peripheral NPC movement during confrontation does NOT trigger anomaly monologue; delayed "did I miss something?" monologue fires post-confrontation
**Cross-room transitions (server #506)**
- `make test-server` includes 8 cross-room transition tests (T1-T8)
- All 8 pass: knowledge state, inventory, fog carry-over, and stance all survive room transitions correctly
- T2 (Inventory Interact): item `CarriedBy` survives transition; item absent from tile queries in new room; present in player inventory in snapshot
**Content scaling (server #513)**
- `make test-server` includes the 100-tick max-NPC stress test
- Test passes: every tick within D-026 100ms budget at maximum Active tier entity count (80 NPCs)
- Behavioral regression check passes: baseline NPC subset matches `tests/perf/baseline.json` at tick 100
**Contraband detection (server #425)**
- NPC with `ScanAuthority` within interaction range of player carrying a `Contraband`-flagged item → NPC KG updated with `HasContraband` fact at `KnowsDetails` confidence
- Scan event present in ObserverSnapshot for that tick
- No scan event emitted when player carries no contraband items
- BTreeMap used for KG storage (D-010 determinism, D-041)
**OQ-18 resolved (server #524)**
- New D-NNN decision committed to `decisions/` (content.md or architecture.md as appropriate)
- Decision states clearly whether dialogue access tiers and D-041 confidence tiers are the same system or layered
- If #305 dialogue filter changed: `make test-server` still passes
**WRONG button full captures (client #507)**
- F12 during Gauntlet play → bug report directory contains: `inputs.jsonl` (60 ticks of PlayerInput history), `snapshots.jsonl` (60 ticks of ObserverSnapshot history), `seed.txt` (server RNG seed), plus existing MVP files (text render, description)
- `tooling/test-client --replay inputs.jsonl` against a fresh server with matching seed reproduces the same snapshot sequence
- Ring buffer pre-allocated at startup — no per-tick allocation in hot path
**OQ-07 resolved (client #522)**
- D-056 or D-057 amended with OQ-07 resolution note
- `client/scripts/rendering/cursor_renderer.gd` enforces the chosen insert-off behavior
- Interaction verb labels absent (or reduced per chosen option) when insert is off in Gauntlet test mode
---
## Test Plan Alignment (D-030)
| D-030 Phase | Coverage | Sprint 11 Status |
|-------------|----------|-----------------|
| Phase 1 (infra) | Done Sprints 1-2 | — |
| Phase 2 (integration) | Done Sprints 3-5 | — |
| Phase 3 (CauseChain, divergent snapshots) | Gauntlet golden suite expanded Sprint 9-10 | Expanding with 3 new rooms (#504) |
| Cross-room transitions | T1-T8 from Gestalt (#506) | In scope this sprint |
| Replay / scripted testing | Replay loading done (#483, Sprint 10) | WRONG button ring buffer feeds replay system (#507) |
| Performance regression | Baseline committed (#499, Sprint 10) | 100-tick stress test adds to baseline (#513) |
| Pre-PR gate | `make pre-pr` stable | Unchanged |
---
## Sequence Recommendation
**Days 1-2 (all teams, parallel):**
- Server: start #504 immediately (no blockers). Sprint Gauntlet is the critical path node.
- Server: start #524 (OQ-18 resolution) on day 1. Write decision by end of day 2.
- Server: start #513 (stress test) in parallel — standalone, no dependency on #504.
- Server: start #425 (contraband detection) in parallel — standalone.
- Client: start #522 (OQ-07 resolution) on day 1. Implement and amend decision by end of day 2.
- Client: start #507 (WRONG button) on day 1 — confirm RNG seed availability first.
**Days 3-5:**
- Server: complete all 3 rooms in #504 and verify server boots without panic. Hand off to #506.
- Server: start #506 (cross-room transitions) once #504 is merged.
- Client: #507 ring buffer implementation complete; end-to-end replay test.
**Days 6-10:**
- Server: complete #506 (T1-T8 tests pass).
- All teams: verify against Sprint Completion Proof criteria.
---
## PR Workflow
Each team submits their own PR from their branch. Integration is via merge to `main`. No joint branch.
```bash
# Server
tea pr create --repo jpmschweitzer/settled-reach --login schweitz \
--title "feat(simulation): sprint 11 combine — server" \
--description "body" --base main --head server
# Client
tea pr create --repo jpmschweitzer/settled-reach --login schweitz \
--title "feat(client): sprint 11 combine — client" \
--description "body" --base main --head client
```
+103
View File
@@ -0,0 +1,103 @@
# Sprint 11: Combine — Server Tasks
**Goal:** Complete the Gauntlet test matrix with final interaction-combination rooms and cross-room scenarios, deliver the WRONG-button bug-capture system, resolve open implementation questions OQ-07 and OQ-18, and add contraband detection — hardening the system as a fully covered, testable unit.
**Branch:** `server`
**Agents:** Dudley (simulation), Tyre (arch review), Hoshe (QA)
---
## New Tickets
| # | Title | Blocked by |
|---|-------|------------|
| #504 | Gauntlet rooms: Sprint Gauntlet, Eavesdrop Alcove, Confrontation Stage | #487 (done) |
| #506 | Cross-room transition test scenarios (T1-T8 from Gestalt) | #498 (done), #504 |
| #513 | Content scaling stress test (100 ticks, max-NPC pack, tick budget) | #487 (done), #499 (done) |
| #425 | Contraband detection — NPC scan checks carried items + KG | #424 (done) |
| #524 | Resolve OQ-18: dialogue access tiers vs D-041 confidence hierarchy | — |
Use `db/connectors/ticket show <id>` for full details.
---
## Key Decisions
- `decisions/architecture.md` — D-010 (deterministic simulation), D-026 (100ms tick budget), D-041 (knowledge graph — 4-level confidence hierarchy, BTreeMap requirement, dialogue tier mapping)
- `decisions/perception.md` — D-055 (sprint suppresses interaction buffer — Sprint Gauntlet room), D-059 (fog/shader five layers — Eavesdrop Alcove), D-060 (cognitive delay — Eavesdrop Alcove), D-070 (confrontation as cognitive vulnerability — Confrontation Stage)
- `decisions/content.md` — D-028 (dialogue access layers — OQ-18 resolution), D-065 (contraband spec)
- `decisions/scope.md` — D-065 (contraband detection scope)
---
## Open Questions to Resolve Early
- **OQ-18 (#524): Dialogue access tiers vs D-041 confidence hierarchy** — Resolve in the first half of the sprint. The decision affects #305 (dialogue selection pipeline filtering logic) which may need updating. Document result as a new D-NNN decision. Consult Gestalt and Nigel on the archetype dimension before closing.
---
## Notes
**#504 — Gauntlet rooms: Sprint Gauntlet, Eavesdrop Alcove, Confrontation Stage**
- Existing: `server/src/test_world/rooms/` has 7 rooms: `hub.rs`, `fog_theater.rs`, `occlusion_corridor.rs`, `inventory_warehouse.rs`, `interaction_gallery.rs`, `pause_chamber.rs`, `dialogue_room.rs`, `crowd_plaza.rs`. Constants live in `server/src/test_world/constants.rs`. The `ROOMS` array there is additive-only — append new entries at the end or StableId ranges will shift and break all existing golden files.
- Deliver three new room modules, each following the exact pattern of existing rooms:
- **Sprint Gauntlet** (~28x20): Tests D-055 sprint suppression. Player sprints past a visible NPC; interaction buffer must be empty during sprint, anomaly monologue must fire retroactively after sprint ends. Include a sprint-trigger tile and a reset plate.
- **Eavesdrop Alcove** (~24x16): Tests eavesdrop positioning and ListeningFocus (D-071). Player in Careful stance at a corner within audible range of two NPCs in conversation. Test: World SFX boost observable, conversation murmur event emitted (D-072), interaction buffer suppressed at eavesdrop distance.
- **Confrontation Stage** (~32x24): Tests D-070 confrontation vulnerability. Player confronts an NPC target; peripheral NPC movement that occurs during confrontation should not trigger anomaly monologue (suppressed by confrontation dip). Post-confrontation delayed monologue fires for the missed event.
- Each room needs constants in `server/src/test_world/constants.rs` (GauntletRoom struct + StableId range). Append after `RESET_PLATE_STABLE_IDS`. Update `ROOMS` array and `EXPECTED_ENTITY_COUNT`.
- Blocks #506 — complete #504 before the sprint midpoint.
**#506 — Cross-room transition test scenarios (T1-T8 from Gestalt)**
- Existing: `tooling/test-client/src/replay.rs` is complete (Sprint 10 #483). `tooling/test-client/src/golden.rs` has snapshot diffing infrastructure. All 7 Sprint 10 rooms (#498) are done; all 3 new Sprint 11 rooms (#504) must be done first.
- Deliver 8 cross-room transition tests covering system combination bugs at room boundaries. Source: `gestalt-round3.md` scenarios T1-T8. Approximate scope:
- **T1 Sprint Exit:** Player sprints out of Sprint Gauntlet room into corridor, asserts interaction buffer clears on entry, restores on exit.
- **T2 Inventory Interact:** Player picks up item in Inventory Warehouse, carries it to adjacent room, verifies CarriedBy survives room transition (tile presence removed, item appears in player inventory in new room snapshot).
- **T3 Pause Anywhere:** Player pauses mid-corridor (between rooms), asserts tick stops, resumes cleanly.
- **T4-T8:** Refer to `gestalt-round3.md` for full scenario specs (knowledge state persistence, fog carry-over, eavesdrop cut on transition, confrontation interrupt on room change).
- Write tests in `server/tests/` or as a dedicated integration test file. Tests must use the replay system or `cargo nextest` ECS world setup — no ad-hoc test harness.
- Blocked by #504. Start after Sprint Gauntlet, Eavesdrop Alcove, and Confrontation Stage are wired and the server boots without panic.
**#513 — Content scaling stress test (100 ticks, max-NPC pack, tick budget)**
- Existing: `server/src/content/` has the content loader. `tooling/perf-baseline` script exists (Sprint 10 #499). The baseline NPC pack is in place. `tests/perf/baseline.json` committed.
- Deliver: A test that spawns the maximum entity count (Active tier ceiling per D-026: 80 NPCs), runs 100 ticks, and asserts that each tick completes within the 100ms budget. Also asserts no behavioral regression on the baseline NPC subset (same positions and knowledge state at tick 100 as in the baseline run). Run via `make test-server` — not a separate script.
- Integration: Compare output against `tests/perf/baseline.json`. The perf baseline tool (#499, done) already defines the output format; match it.
- Standalone, no cross-team dependency. Can run in parallel with #504.
**#425 — Contraband detection: NPC scan checks carried items + KG**
- Existing: `server/src/simulation/inventory.rs` has `CarriedBy`, `InventorySlot`, `ItemName`, and `collect_inventory_for()`. The inventory system is complete (#424 done). D-065 defines contraband as unlicensed lattice components. `server/src/knowledge/` has `graph.rs`, `registry.rs`, `types.rs`.
- Deliver: An NPC scan system that, when an NPC with scan authority is within interaction range of the player, queries the player's carried items for contraband flag. If a contraband item is in inventory, update the NPC's KnowledgeGraph (`KnowsDetails`, source `DirectObservation`) for the player entity with fact: `HasContraband`. Emit a scan event to the ObserverSnapshot for client-side rendering (no client ticket required — use existing event wire).
- Specifics: Define a `Contraband` marker component on item entities. Define `ScanAuthority` component on NPC entities with scan permissions. The scan check runs in a dedicated system scheduled after the perception phase, before the snapshot phase. Use `StableId` references throughout — no raw bevy Entity handles in the knowledge entry.
- Standalone. No cross-team dependency.
**#524 — Resolve OQ-18: dialogue access tiers vs D-041 confidence hierarchy**
- Existing: D-028 defines 4 dialogue access layers: surface/real/secret/unprompted. D-041 defines 4 KG confidence tiers: `Suspects < KnowsOf < KnowsDetails < Direct`. The dialogue selection pipeline (#305) already filters by confidence tier. OQ-18 asks whether character-archetype dialogue access tiers are the same system or a layered system on top.
- Deliver: A written decision (commit to `decisions/content.md` or `decisions/architecture.md` as appropriate as a new D-NNN) resolving one of: (a) same system — archetype access tiers map directly 1:1 to KG confidence tiers (surface=Suspects+, real=KnowsOf+, secret=KnowsDetails+), or (b) layered — character archetype adds a second gating dimension independent of KG confidence (e.g., detective can always access surface regardless of KG confidence because institutional authority, smuggler needs KnowsOf minimum for surface because outsider). If the decision changes the filtering logic in #305, update that system accordingly.
- This is a design resolution ticket, not a large implementation task. The output is a decision document + any follow-on code change to the dialogue filter. Time estimate: 0.5d for discussion + decision write-up, up to 1d if #305 needs updating.
- Resolve early — day 1 or 2 — so downstream content work can proceed with confidence.
---
## Dependency Chain
```
#504 (Sprint Gauntlet, Eavesdrop Alcove, Confrontation Stage)
→ #506 (cross-room transitions T1-T8)
#513 (stress test 100 ticks) — standalone, parallel
#425 (contraband detection) — standalone, parallel
#524 (OQ-18 resolution) — standalone, resolve day 1-2
```
**Critical path:** `#504 → #506`. Complete #504 before sprint midpoint. All other tickets are parallel tracks.
---
## 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):
```bash
tea pr create --repo jpmschweitzer/settled-reach --login schweitz \
--title "feat(simulation): sprint 11 combine — server" \
--description "body" --base main --head server
```