docs(workshops): complete QA test architecture workshop
3-round workshop with 7 agents (Tyre, Dudley, Stig, Hoshe, Justine, Gestalt, Ozzie) plus Qatux documenting. Produced: - 59-item prioritized test backlog (60 tickets under epic #455) - Gauntlet test world spec: 7 rooms + hub, 48 entities - Test client binary spec (tooling/test-client/) - Determinism fixes (3 patches, ~22 lines) - Server --test-mode + --port 0 design - Content cross-reference validation (9 checks) - make pre-pr pipeline (6-step) - 38 client tests prioritized - Anti-tedium features (reset plate, hub teleport, WRONG button) - Human tester walkthrough - CI pipeline design (deferred but documented) Sprint 8 scope: ~17.75 team-days across 26 tickets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,590 @@
|
||||
# QA Strategy & Test Architecture Workshop — Round 3 Notes
|
||||
|
||||
**Workshop:** QA Strategy & Test Architecture
|
||||
**Round:** 3 (Prioritization & Final Specs)
|
||||
**Date:** 2026-02-17
|
||||
**Documenter:** Qatux
|
||||
**Participants:** Tyre, Dudley, Stig, Hoshe, Justine, Gestalt, Ozzie
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Round 3 produced build-ready specifications across all tracks. Every agent delivered implementation-level detail: Dudley's determinism patches are copy-pasteable, Justine's Makefile targets are ready for `make`, Stig's 38 client tests have function names and assertion logic, Hoshe's 59-item prioritized backlog has effort estimates and dependency chains, Tyre's Sprint 8 plan has a critical path and dependency graph, Gestalt's Gauntlet map has 48 entities with coordinate positions, and Ozzie's human tester walkthrough covers start-to-finish with terminal mockups.
|
||||
|
||||
**Key outcomes:**
|
||||
- **Sprint 8 scope:** 10 implementation items, ~7-8 team-days. Infrastructure only — no Gauntlet rooms, no anti-tedium features. Critical path: `--test-mode` (0.5d) -> test client binary (2-3d) -> Layer 3 test (0.5d).
|
||||
- **Gauntlet map:** 7 rooms + Central Hub, 48 entities, 2 cross-cuts, hub-and-spoke topology. Designed for Sprint 8, room content built Sprint 9+.
|
||||
- **All Round 2 open questions resolved.** 11 questions answered: SetTickRate while paused = bug (reject), entity index recycling = safe (generation counter), canonical room ordering = required, blocked_entities = feasible Sprint 9, test client at `tooling/test-client/` (lead override), 0 cross-cuts Sprint 8 (infrastructure only), content-ron in pre-pr = yes, fixed spawn order in setup function, kill+relaunch for full restart.
|
||||
- **One lead override during Round 3:** Test client binary moved from `server/src/bin/test_client.rs` to `tooling/test-client/` as a separate workspace crate. Dudley verified bridge types are already pub-exported — no server changes needed.
|
||||
|
||||
**Scope tension resolved:** Tyre scopes Sprint 8 as infrastructure-only. Dudley designs a 5-room Gauntlet loader MVP. Gestalt designs the full 7+Hub map. These are compatible: Tyre's Sprint 8 ships the pipes (flags, binary, test harness), Gestalt's map is the design document, Dudley's loader is the Sprint 9 implementation target. The map design happens now; room building happens Sprint 9.
|
||||
|
||||
---
|
||||
|
||||
## 1. Sprint 8 Implementation Plan (Tyre)
|
||||
|
||||
### Dependency Graph
|
||||
|
||||
```
|
||||
S8-1: Determinism fixes ────────────────────────────┐
|
||||
├─> S8-5: make pre-pr
|
||||
S8-2: Content cross-ref validation ─────────────────┤
|
||||
│
|
||||
S8-3: --test-mode + --port 0 ──> S8-4: Test client ──┤
|
||||
binary MVP │
|
||||
│ │
|
||||
└─> S8-6: Layer 3 test wiring
|
||||
│
|
||||
S8-7: Pause guard tests ────────────────────────────┘ (parallel, no deps)
|
||||
S8-8: Determinism regression tests ── (after S8-1)
|
||||
S8-9: EntityRegistry lifecycle tests ── (parallel, no deps)
|
||||
S8-10: Fixture staleness check ── (after S8-5)
|
||||
```
|
||||
|
||||
### Items
|
||||
|
||||
| # | Item | Owner | Effort | Depends On |
|
||||
|---|------|-------|--------|------------|
|
||||
| S8-1 | Determinism fixes (A, B, D) | Dudley | 0.5d | Nothing |
|
||||
| S8-2 | Content cross-reference validation (9 checks) | Justine | 1d | Nothing |
|
||||
| S8-3 | `--test-mode` + `--port 0` server flags | Dudley | 0.5d | Nothing |
|
||||
| S8-4 | Test client binary MVP | Dudley | 2-3d | S8-3 |
|
||||
| S8-5 | `make pre-pr` chain | Justine | 0.5d | S8-1, S8-2 |
|
||||
| S8-6 | Layer 3 test wiring | Dudley | 0.5d | S8-3, S8-4 |
|
||||
| S8-7 | Pause guard tests (6 tests) | Dudley | 0.5d | Nothing |
|
||||
| S8-8 | Determinism regression tests | Dudley | 0.5d | S8-1 |
|
||||
| S8-9 | EntityRegistry lifecycle tests (3 tests) | Dudley | 0.25d | Nothing |
|
||||
| S8-10 | Fixture staleness check | Justine | 0.25d | S8-5 |
|
||||
|
||||
**Critical path:** S8-3 (0.5d) -> S8-4 (2-3d) -> S8-6 (0.5d) = 3-4 days.
|
||||
**Total:** Server (Dudley) ~5-6d, Tooling (Justine) ~1.75d = ~7-8 team-days.
|
||||
|
||||
### What Explicitly Does NOT Ship Sprint 8
|
||||
|
||||
- Gauntlet room content (rooms 1-14)
|
||||
- Anti-tedium features (room reset, hub teleport, WRONG button)
|
||||
- Crossterm live terminal display
|
||||
- CI automation (Gitea Actions)
|
||||
- Performance baselines
|
||||
- Encoding asymmetry cross-language tests
|
||||
- Client tests (Stig's 38)
|
||||
- Cross-room transition scenarios
|
||||
|
||||
---
|
||||
|
||||
## 2. Determinism Fixes — Final Patch Specs (Dudley)
|
||||
|
||||
Three fixes shipping Sprint 8. Fix C was already done (self-corrected in Round 2). Total: ~22 lines production code, 4 regression tests.
|
||||
|
||||
### Fix A: BTreeSet for visible_positions + sort visible_tiles
|
||||
|
||||
**Files:** `server/src/perception/query.rs`, `server/src/perception/observer/mod.rs`
|
||||
|
||||
- `visible_positions: HashSet<(i32, i32)>` -> `BTreeSet<(i32, i32)>` in `VisibilityGeometry`
|
||||
- `visible_ids: HashSet<u64>` -> `BTreeSet<u64>` in `filter_visible_entities`
|
||||
- Add `visible_tiles.sort_by_key(|t| (t.x, t.y))` after collection
|
||||
- Update `collect_remembered_entities` signature to accept `&BTreeSet`
|
||||
- `sector_lookup: HashMap` stays as-is (point-lookup only, confirmed safe per OQ-4)
|
||||
|
||||
**Regression tests:** `snapshot_visible_tiles_are_sorted`, `sprint_anomaly_picks_lowest_stable_id`
|
||||
|
||||
### Fix B: Sort visible entities by entity_id
|
||||
|
||||
**File:** `server/src/perception/observer/mod.rs`
|
||||
|
||||
Insert `entities.sort_by_key(|e| e.entity_id);` after `collect_remembered_entities`.
|
||||
|
||||
**Regression test:** `snapshot_entities_sorted_by_id`
|
||||
|
||||
### Fix D: Sort movers in validate_movement
|
||||
|
||||
**File:** `server/src/simulation/movement.rs`
|
||||
|
||||
Collect movers into Vec, sort by `entity.to_bits()`, then process. Deterministic collision resolution — entity with lower `to_bits()` wins contested tiles.
|
||||
|
||||
**Regression test:** `validate_movement_deterministic_collision_winner`
|
||||
|
||||
---
|
||||
|
||||
## 3. Server `--test-mode` Final Spec (Dudley)
|
||||
|
||||
Complete `main.rs` replacement provided. Key design:
|
||||
|
||||
| Aspect | Behavior |
|
||||
|--------|----------|
|
||||
| Stdout | `LISTENING:{port}` after TCP bind (test client parses this) |
|
||||
| Stderr | All tracing via `tracing_subscriber::fmt::layer().with_writer(std::io::stderr)` |
|
||||
| Content | Gauntlet content pack (when ready), fallback to proof room |
|
||||
| Seed | `--seed 42` default in test-mode |
|
||||
| Shutdown | Exits after first client disconnect |
|
||||
| Port | `--port 0` for OS-assigned port (uses existing `accept_on(listener)`) |
|
||||
|
||||
`setup_proof_room()` extracted as reusable function for both modes.
|
||||
|
||||
---
|
||||
|
||||
## 4. Test Client Binary Final Spec (Tyre + Dudley)
|
||||
|
||||
### Lead Override: Crate Location
|
||||
|
||||
**During Round 3, the lead overruled the Round 2 location.** Test client moves from `server/src/bin/test_client.rs` to `tooling/test-client/` as a separate workspace crate.
|
||||
|
||||
**Note:** Tyre's Round 3 spec was written before this override and still references `server/src/bin/`. Dudley's addendum confirms the override and verifies all bridge types are already pub-exported — no server changes needed.
|
||||
|
||||
### CLI (Sprint 8 MVP)
|
||||
|
||||
```
|
||||
settled-reach-test-client [OPTIONS]
|
||||
|
||||
Connection:
|
||||
--connect <host:port> (default: 127.0.0.1:9876)
|
||||
|
||||
Input:
|
||||
--replay <file.jsonl> JSONL input file (one JSON array per tick)
|
||||
|
||||
Output (mutually exclusive):
|
||||
--text Structured text to stdout (default)
|
||||
--json JSON to stdout (for golden files)
|
||||
--quiet No output (CI assertions only)
|
||||
|
||||
Assertions:
|
||||
--golden <file.json> Compare final snapshot, exit 1 on diff
|
||||
--ticks <N> Disconnect after N ticks
|
||||
```
|
||||
|
||||
### Text Output Format
|
||||
|
||||
```
|
||||
=== Tick 42 | Player (15,10) facing East | Stance: Walk | TickRate: Full ===
|
||||
Game time: Day 0, 04:12 (Morning)
|
||||
Room: Occlusion Corridor
|
||||
Entities (5):
|
||||
npc:100 (18,10) Forward rel:Neutral vis:Visible d=3
|
||||
...
|
||||
Tiles: 31 visible
|
||||
Interactions (2):
|
||||
npc:100 [Talk(1), ExamineNpc(2)] distance=3
|
||||
Inventory: 2/9 [item:300(slot-0), item:301(slot-3)]
|
||||
Monologue: "Something about this manifest doesn't add up."
|
||||
===
|
||||
```
|
||||
|
||||
Entity labels: `kind:entity_id`. Sorted by distance (nearest first), ties broken by entity_id. Sections with no data omitted.
|
||||
|
||||
### Text Renderer
|
||||
|
||||
`server/src/bridge/text_renderer.rs` — library function `format_snapshot_text()` callable by the test client crate and integration tests. Pub-exported from server crate. Full implementation (~100 lines) provided by Tyre.
|
||||
|
||||
### Golden File Comparison
|
||||
|
||||
JSON with sorted keys. Recursive `diff_json_values()` produces field-by-field diff on mismatch. Exit code 1 on diff.
|
||||
|
||||
### Replay Format
|
||||
|
||||
JSONL — one JSON array of `PlayerInput` per line. Empty array `[]` = idle tick. Uses `rmp_serde::to_vec` (NOT `to_vec_named`) to match GDScript encoding.
|
||||
|
||||
### Effort Estimate
|
||||
|
||||
~2-3 days: binary scaffolding (0.5d), text renderer (0.5-1d), replay loading (0.5d), golden file comparison (0.5d), Layer 3 wiring (0.5d).
|
||||
|
||||
---
|
||||
|
||||
## 5. Gauntlet Map Specification (Gestalt)
|
||||
|
||||
### MVP Room List: 7 rooms + Central Hub
|
||||
|
||||
| # | Room | Size | Observer | Facing | Entities |
|
||||
|---|------|------|----------|--------|----------|
|
||||
| 0 | Central Hub | 24x24 | (50,58) | -- | 4 signs |
|
||||
| 1 | Fog Theater | 44x32 | (56,18) | South | 4 (3 NPCs, 1 object) |
|
||||
| 2 | Occlusion Corridor | 42x22 | (84,58) | East | 4 NPCs |
|
||||
| 3 | Inventory Warehouse | 30x28 | (17,54) | East | 11 (10 objects, 1 NPC) |
|
||||
| 4 | Interaction Gallery | 24x20 | (14,92) | East | 5 (2 NPCs, 3 objects) |
|
||||
| 5 | Pause Chamber | 16x16 | (50,86) | North | 1 NPC |
|
||||
| 6 | Dialogue Room | 28x20 | (50,114) | North | 4 NPCs |
|
||||
| 7 | Crowd Plaza | 32x32 | (96,94) | West | 15 NPCs |
|
||||
|
||||
**Total: 48 entities.** Map bounds: 0-116 x 0-124 sim tiles.
|
||||
|
||||
### Topology
|
||||
|
||||
Hub-and-spoke with 2 cross-cuts:
|
||||
- **cross-cut-W:** Inventory Warehouse <-> Interaction Gallery (T3: Full Inventory Interact)
|
||||
- **cross-cut-E:** Occlusion Corridor <-> Crowd Plaza (T1: Sprint Exit)
|
||||
|
||||
7 corridors connecting rooms to hub and each other. All 6 tiles wide, 8-14 tiles long.
|
||||
|
||||
### StableId Assignment Order
|
||||
|
||||
Entities receive StableIds in canonical spawn order:
|
||||
1. Hub entities (signs): StableId 1-4
|
||||
2. Fog Theater: StableId 5-8
|
||||
3. Occlusion Corridor: StableId 9-12
|
||||
4. Inventory Warehouse: StableId 13-23
|
||||
5. Interaction Gallery: StableId 24-28
|
||||
6. Pause Chamber: StableId 29
|
||||
7. Dialogue Room: StableId 30-33
|
||||
8. Crowd Plaza: StableId 34-48
|
||||
|
||||
**Additive-only rule:** Existing rooms and entities are NEVER reordered. New rooms and entities append. This preserves golden file stability.
|
||||
|
||||
### Cross-Room Transitions (3 MVP)
|
||||
|
||||
| # | Transition | Path | Systems Tested |
|
||||
|---|-----------|------|---------------|
|
||||
| T1 | Sprint Exit | Crowd Plaza -> cross-cut-E -> Occlusion | Sprint suppression + LOS recalculation |
|
||||
| T3 | Full Inventory Interact | Inventory -> cross-cut-W -> Interaction Gallery | Inventory limit + verb computation |
|
||||
| T6 | Pause Anywhere | Pause -> Hub -> any room | Pause persistence across teleport/room change |
|
||||
|
||||
### Scope Note: Gestalt vs Tyre vs Dudley
|
||||
|
||||
Gestalt designs 7+Hub rooms for the full Gauntlet. Tyre scopes Sprint 8 as infrastructure-only (no rooms). Dudley designs a 5-room loader MVP as the Sprint 9 implementation target. These are consistent: the map design is complete, room building follows infrastructure.
|
||||
|
||||
---
|
||||
|
||||
## 6. Client Test Suite — Final 38 Tests (Stig)
|
||||
|
||||
Deduplicated from 41 candidates (32 original + 6 Tyre additions + 3 Stig R2 additions).
|
||||
|
||||
| Priority | Count | Sprint | Categories |
|
||||
|----------|-------|--------|-----------|
|
||||
| P0 | 2 | Sprint 8 | Monologue overwrite (Bug #5), camera static during pause (Bug #2) |
|
||||
| P1 | 7 | Sprint 8-9 | Fog shader (4), entity lifecycle (2), pending recognition blob (1) |
|
||||
| P2 | 24 | Sprint 9 | Camera (5), entity alpha/color (5), UI (12), lerp (1), teleport (1) |
|
||||
| P3 | 5 | Sprint 10+ | Z-layer (4), lerp target (1) |
|
||||
|
||||
Each test includes: function name, category, exact assertions, and setup requirements.
|
||||
|
||||
### Fog Constants
|
||||
|
||||
Added to `client/scripts/autoloads/fog_state.gd`:
|
||||
- `VIS_HIDDEN = 0`, `VIS_PERIPHERAL = 180`, `VIS_FORWARD = 255`
|
||||
- `EXP_UNEXPLORED = 0`, `EXP_EXPLORED = 128`, `EXP_VISIBLE = 255`
|
||||
|
||||
5 magic number replacements in `fog_state.gd`. No shader changes needed.
|
||||
|
||||
---
|
||||
|
||||
## 7. Prioritized Test Backlog — 59 Items (Hoshe)
|
||||
|
||||
### Summary by Sprint
|
||||
|
||||
| Sprint | P0 items | P1 items | Combined effort |
|
||||
|--------|----------|----------|----------------|
|
||||
| Sprint 8 | 10 (~7d) | 15 (~10.75d) | ~17.75d |
|
||||
| Sprint 9 | 10 (~7.5d) | 12 (~12.5d) | ~20d |
|
||||
| Sprint 10+ | -- | 12 (~18.75d) | ~18.75d |
|
||||
|
||||
### Sprint 8 P0 (10 items, ~7d)
|
||||
|
||||
1. Determinism Fix A (0.5d)
|
||||
2. Determinism Fix B (0.25d)
|
||||
3. Determinism Fix D (0.25d)
|
||||
4. Server `--test-mode` + `--port 0` (1d)
|
||||
5. `make pre-pr` target (0.5d)
|
||||
6-8. Pause guard tests: movement_discarded, unpause_accepted, roundtrip (0.75d total)
|
||||
9. Content cross-reference validation (1.5d)
|
||||
10. Fixture staleness check (0.25d)
|
||||
|
||||
### Bug Catalogue Coverage
|
||||
|
||||
Every Sprint 6-7 bug has Sprint 8 P0/P1 coverage:
|
||||
- Bug #1 (snapshot delivery): #4 (`--test-mode`), #26 (Layer 3)
|
||||
- Bug #2 (camera startup): #21 (client P0: camera static during pause)
|
||||
- Bug #3 (movement while paused): #6, #7, #8, #13 (pause guard suite)
|
||||
- Bug #4 (MessagePack -128): #15, #16, #17, #18 (boundary value matrix)
|
||||
- Bug #5 (monologue overwrite): #21 (client P0: monologue not lost)
|
||||
- Bug #6 (snapshot spam): #1, #2 (determinism), #29 (golden files)
|
||||
|
||||
---
|
||||
|
||||
## 8. Tooling Specifications (Justine)
|
||||
|
||||
### `make pre-pr` — Final
|
||||
|
||||
6-step chain: lint -> build -> test -> validate-content -> check-fact-ids -> fixtures-check.
|
||||
~90s incremental, ~8min clean cache. Fixture staleness is BLOCKER (not WARNING).
|
||||
|
||||
Branch-specific variants: `pre-pr-server`, `pre-pr-client`, `pre-pr-content`.
|
||||
|
||||
Full Makefile provided with failure output examples.
|
||||
|
||||
### `make perf-baseline`
|
||||
|
||||
- Baseline file: `tests/perf/baseline.json` (committed)
|
||||
- Median of 5 runs, thresholds: <15% PASS, 15-30% WARN, >30% FAIL
|
||||
- Machine tag prevents cross-machine comparison
|
||||
- `tooling/perf-compare` and `tooling/perf-update` scripts provided (full bash code)
|
||||
- **Blocked on Gauntlet** — shadowcast bench is the only available benchmark until rooms ship
|
||||
|
||||
### Golden File Workflow
|
||||
|
||||
- Generator: `server/tests/gauntlet_golden_gen.rs` (generates JSON at ticks 0, 10, 100)
|
||||
- Comparator: `server/tests/gauntlet_golden.rs` (field-by-field diff on mismatch)
|
||||
- JSON format: sorted keys, pretty-printed, deterministic
|
||||
- `make golden-diff` / `make golden-update` targets provided
|
||||
- Full Rust code for both (~180 lines combined)
|
||||
|
||||
### CI Pipeline Design (Deferred)
|
||||
|
||||
3-tier pipeline documented and ready for when lead greenlights:
|
||||
- **Commit tier** (<2min): lint + validate-content + check-fact-ids
|
||||
- **PR tier** (<15min): build + test + fixtures staleness (BLOCKER)
|
||||
- **Nightly tier** (<30min): Layer 3 + golden files + perf benchmarks + content scaling
|
||||
|
||||
Complete `.gitea/workflows/ci.yaml` provided (~100 lines). Self-hosted runner required.
|
||||
|
||||
---
|
||||
|
||||
## 9. Content Validation — 9 Checks (Hoshe + Justine merged)
|
||||
|
||||
Extends `tooling/validate-content` (Python) with a second pass after schema validation.
|
||||
|
||||
| # | Check | Severity | Source |
|
||||
|---|-------|----------|--------|
|
||||
| 1 | `canonical_id_uniqueness` | ERROR | Hoshe |
|
||||
| 2 | `relationship_target_resolution` | ERROR | Both |
|
||||
| 3 | `location_slug_resolution` | ERROR | Both |
|
||||
| 4 | `dialogue_location_resolution` | ERROR | Both |
|
||||
| 5 | `fact_id_resolution` | ERROR | Both (absorbs `check-fact-ids`) |
|
||||
| 6 | `triangle_membership_resolution` | ERROR | Both |
|
||||
| 7 | `npc_count_accuracy` | WARNING | Hoshe |
|
||||
| 8 | `dialogue_line_id_uniqueness` | ERROR | Hoshe |
|
||||
| 9 | `bidirectional_relationship_consistency` | WARNING | Justine |
|
||||
|
||||
`ContentIndex` Python class skeleton provided. Phased rollout: Phase 1 (NPC/triangle/district) Sprint 8, Phase 2 (dialogue/fact_ids) Sprint 8, Phase 3 (warnings) Sprint 9.
|
||||
|
||||
---
|
||||
|
||||
## 10. Anti-Tedium Specifications (Ozzie + Gestalt + Stig + Dudley)
|
||||
|
||||
### Sprint 8 Priorities (Ozzie)
|
||||
|
||||
| Priority | Feature | Effort | Justification |
|
||||
|----------|---------|--------|--------------|
|
||||
| P1 | Room Reset Triggers | 1-1.5d | Can't re-test without it |
|
||||
| P2 | WRONG Button MVP | 1d | Can't report bugs without it |
|
||||
| P3 (Sprint 9) | Hub Teleport | 0.5d | Convenience, not necessity |
|
||||
| P4 (Sprint 9) | Timer + Checklist | 1.5-2d | Engagement, not necessity |
|
||||
|
||||
**Note:** Tyre's Sprint 8 plan does NOT include anti-tedium features (infrastructure-only scope). Anti-tedium ships Sprint 9 per Tyre's roadmap, Sprint 8 per Ozzie/Gestalt's priority.
|
||||
|
||||
### Room Reset — Final Spec
|
||||
|
||||
- Trigger: Step on ResetPlate + Interact (NOT automatic)
|
||||
- Server: `RoomResetTrigger` component, `RoomSnapshots` resource, 10-tick debounce
|
||||
- Resets: entity positions, KG, fog (room tiles), room-sourced inventory, dialogue state
|
||||
- Does NOT reset: other rooms, player position, session timer, SimRng state
|
||||
- Test-mode only: entities only added with `--test-mode`
|
||||
|
||||
### WRONG Button MVP — Final Spec
|
||||
|
||||
- Hotkey: F12
|
||||
- Captures: current ObserverSnapshot (JSON), text output, tick/room/seed, tester description
|
||||
- Output: `tests/bug-reports/gauntlet-t{tick}-{timestamp}/` (4 files: report.md, snapshot.json, text_output.txt, description.txt)
|
||||
- Zero server changes needed for MVP
|
||||
- Full version (Sprint 9+): 60-tick ring buffer, input history, replay seed
|
||||
|
||||
### Hub Teleport
|
||||
|
||||
- Home key -> `PlayerAction::TeleportToHub`
|
||||
- Instant camera snap + 0.3s fade-to-black
|
||||
- Gauntlet-only (server rejects in non-Gauntlet maps)
|
||||
|
||||
### Client-Side UI (Stig)
|
||||
|
||||
- `bug_report.gd` autoload: ~80 lines GDScript, F12 handler, ring buffer, modal prompt
|
||||
- `InsertOverlay/FlashRect`: shared by reset flash and teleport fade
|
||||
- `GauntletProgress` overlay: room name, run counter, timer, progress bar (Sprint 9)
|
||||
- Fog constants migration: 6 named constants, 5 line replacements
|
||||
|
||||
---
|
||||
|
||||
## 11. Human Tester Workflow (Ozzie)
|
||||
|
||||
### 6-Phase Walkthrough
|
||||
|
||||
1. **Navigate to a Room** — walk from Hub, room name updates, timer starts, checklist loads
|
||||
2. **Execute the Checklist** — printed checklist or terminal display, auto-tracking for `type: auto` items
|
||||
3. **Encounter a Bug** — F12 -> pause -> one-line prompt -> 4 files saved -> resume
|
||||
4. **Reset and Re-Test** — step on reset plate -> room reverts to tick-0 state -> retry
|
||||
5. **Move to Another Room** — Home -> Hub -> walk to next room
|
||||
6. **End Session** — Ctrl+C -> session summary (rooms tested, coverage %, times, bug reports)
|
||||
|
||||
### Quick-Test Developer Workflow
|
||||
|
||||
Target: **65 seconds** from fix to verification.
|
||||
Build (10s) -> start server+client (3s) -> navigate (5s) -> test checklist items (45s) -> exit (2s).
|
||||
|
||||
### Entity Visibility Symbols
|
||||
|
||||
| Symbol | State | Meaning |
|
||||
|--------|-------|---------|
|
||||
| `●` | VISIBLE | In clear vision cone |
|
||||
| `◐` | REMEMBERED | In fog, previously seen |
|
||||
| `◌` | FOGGED | Detected but not recognized |
|
||||
| `✕` | BLOCKED | LOS blocked by wall (debug, requires `blocked_entities`) |
|
||||
| `⚡` | RECOGNIZING | Mid-cognitive-delay |
|
||||
|
||||
### Test Client Display
|
||||
|
||||
10 sections: Header, Player, Entities, Fog, Sound (Sprint 9+), Cognition (Sprint 9+), Interactions, Monologue/Dialogue, Inventory, Status. MVP ships sections 1-4, 7-10.
|
||||
|
||||
---
|
||||
|
||||
## 12. Checklist YAML Schema (Stig + Ozzie merged)
|
||||
|
||||
### Per-Room Format
|
||||
|
||||
```yaml
|
||||
# content/gauntlet/rooms/{room_id}/checklist.yaml
|
||||
room: occlusion_corridor
|
||||
checks:
|
||||
- id: occ_01_hidden_not_visible
|
||||
description: "NPC behind wall is NOT visible"
|
||||
type: auto # auto | manual
|
||||
step: "Stand at (45,3), face East"
|
||||
condition:
|
||||
player_near: [45, 3]
|
||||
entity: hidden-1
|
||||
expected: blocked
|
||||
if_wrong: |
|
||||
LOS leaking through wall. Check shadowcast.rs.
|
||||
```
|
||||
|
||||
### Condition Grammar (7 types)
|
||||
|
||||
`player_near`, `player_facing`, `entity` + `expected` (blocked/visible/remembered/recognizing), `expected_sector`, `perception_mode`, `fog_visible_count_min`/`max`, `inventory_count`, `dialogue_active`, `monologue_contains`.
|
||||
|
||||
### Cross-Room Checklist
|
||||
|
||||
`content/gauntlet/cross_room_checks.yaml` at Gauntlet root. Transition scenarios with multi-room paths and structured conditions.
|
||||
|
||||
### Auto-Tracking Split
|
||||
|
||||
| Feature | Godot Client | Test Client |
|
||||
|---------|-------------|-------------|
|
||||
| Room name + timer | Yes | Yes |
|
||||
| Progress bar (X/Y) | Yes (total from YAML) | Yes |
|
||||
| Auto-evaluate conditions | **No** | **Yes** (Rust, type-safe) |
|
||||
| Per-item display | **No** | **Yes** |
|
||||
|
||||
---
|
||||
|
||||
## 13. Boundary Value Tests — 41 Values (Hoshe)
|
||||
|
||||
### Encoding Asymmetry (Resolved)
|
||||
|
||||
GDScript uses `int_16` for values 256-32767; Rust uses `uint_16`. Both are spec-valid. `rmp_serde` accepts `int_16`-encoded positive values for `u64` fields (Dudley traced through rmp-serde 1.3.1 in Round 2).
|
||||
|
||||
### 4-Direction Tests
|
||||
|
||||
| Direction | What | Location | When |
|
||||
|-----------|------|----------|------|
|
||||
| Rust -> GDScript (fixture) | Snapshots with overlap-zone ticks | `test_msgpack_boundaries.gd` | Every PR |
|
||||
| GDScript -> Rust (fixture) | Inputs with overlap-zone ticks | `serialization.rs` | Every PR |
|
||||
| Rust -> GDScript (raw bytes) | Hand-crafted uint_16/uint_32 bytes | `test_msgpack_boundaries.gd` | Every commit |
|
||||
| GDScript -> Rust (raw bytes) | Hand-crafted int_16/int_32 bytes | `serialization.rs` | Every commit |
|
||||
|
||||
Full test code provided for all 4 directions.
|
||||
|
||||
---
|
||||
|
||||
## 14. Layer 3 Test — Final Spec (Hoshe)
|
||||
|
||||
`server/tests/layer3.rs`, `#[test] #[ignore]`, <10 seconds.
|
||||
|
||||
- Build server binary
|
||||
- Launch as subprocess with `--test-mode --port 0`
|
||||
- Parse `LISTENING:{port}` from stdout (5-second timeout)
|
||||
- Connect via TCP, send `PlayerInput` via `rmp_serde::to_vec` (not `to_vec_named`)
|
||||
- Receive `ObserverSnapshot`, assert version, tick, entity count, player entity kind
|
||||
- `ServerGuard` drop pattern for cleanup (kill on drop)
|
||||
- Tolerates initial tick=0 snapshot before input processed (Dudley R2 adjustment)
|
||||
|
||||
Full Rust code (~80 lines) provided.
|
||||
|
||||
---
|
||||
|
||||
## 15. Resolved Questions
|
||||
|
||||
### New Answers from Round 3
|
||||
|
||||
| ID | Question | Answer | By |
|
||||
|----|----------|--------|-----|
|
||||
| Q1 (R2-OQ-09) | Canonical room ordering for StableId? | Yes — append-only rule. YAML room order = spawn order = StableId order. | Dudley, Gestalt |
|
||||
| Q2 (R2-OQ-10) | Room reset vs full restart? | Both. Room reset for iteration, kill+relaunch for determinism. | Gestalt |
|
||||
| Q3 | Cross-cuts in Sprint 8? | 0 cross-cuts Sprint 8 (infrastructure only). 2 in map design. Build first 2 Sprint 10. | Tyre, Gestalt |
|
||||
| Q4 (R2-OQ-05) | `blocked_entities` feasibility? | Feasible, ~300 tile lookups/tick. Sprint 9 scope, gated behind `--test-mode`. | Dudley |
|
||||
| Q5 (R2-OQ-07) | Test client binary location? | `tooling/test-client/` (lead override). Bridge types already pub-exported. | Tyre, Dudley |
|
||||
| Q6 (R2-OQ-08) | Cross-room checklist location? | `content/gauntlet/cross_room_checks.yaml` at Gauntlet root. | Gestalt |
|
||||
| Q7 (R2-OQ-01) | SetTickRate while paused? | Bug. Reject. Pause exits only via explicit Unpause. 4-line fix in `input.rs`. | Dudley |
|
||||
| Q8 (R2-OQ-02) | Entity index recycling safe? | Yes (bevy generation counter). Discipline: call `unregister()` on despawn. | Dudley |
|
||||
| R2-OQ-03 | `content-ron` in pre-pr? | Yes, in full `pre-pr` only. Not in `pre-pr-server`. | Tyre |
|
||||
| R2-OQ-06 | Checklist overlay in Godot? | Lightweight overlay (room name + timer). Full tracking test-client-only. | Stig |
|
||||
| R2-OQ-11 (cross-cuts) | 4 cross-cuts too many? | Keep all 4 in design. Build 2 for Sprint 8 map. Don't consolidate Sound Lab. | Tyre, Gestalt |
|
||||
|
||||
### Questions Deferred to Implementation
|
||||
|
||||
| ID | Question | Assigned To | When |
|
||||
|----|----------|-------------|------|
|
||||
| R2-OQ-04 | Fixture staleness separate target? | Justine | Resolved: No. 10-15s cost negligible. |
|
||||
| OQ-11 | Client tests headless stability | Stig | Before making client tests a CI merge gate |
|
||||
|
||||
---
|
||||
|
||||
## 16. Sprint 9+ Roadmap (Tyre)
|
||||
|
||||
### Tier 1: Sprint 9 (~10-12 team-days)
|
||||
|
||||
R-01: Gauntlet rooms 1-4 (3-4d), R-02: Room reset trigger (1.5d), R-03: Hub teleport (0.5d), R-04: Client tests P0-P1 (2-3d), R-05: Determinism golden files (1d), R-06: Fog byte constants (0.25d), R-07: Encoding asymmetry tests (1.5d).
|
||||
|
||||
### Tier 2: Sprint 10 (~15-17 team-days)
|
||||
|
||||
R-08: WRONG button MVP (2d), R-09: Gauntlet rooms 5-8 (3-4d), R-10: Room timer + PBs (1d), R-11: Checklist auto-tracking (2d), R-12: Performance baselines (1.5d), R-13: Client tests P2 (3d), R-14: Enhanced test client terminal (2d).
|
||||
|
||||
### Tier 3: Sprint 11+ (build when needed)
|
||||
|
||||
Gauntlet rooms 9-14, cross-room transitions, CI automation, content scaling stress, `blocked_entities`, client test headless, client tests P3, additional pause guard tests, WRONG button full capture, bidirectional relationship warnings.
|
||||
|
||||
### Risk Register (5 risks)
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|-----------|--------|------------|
|
||||
| Test client takes >3 days | Medium | Delays Layer 3 | MVP scope intentionally minimal |
|
||||
| Proof room too simple for golden files | Low | Golden files don't catch real bugs until rooms exist | Proves toolchain; value comes Sprint 9 |
|
||||
| Content validation finds many existing errors | Medium | Sprint 8 effort bloat | Run as WARNING first sprint |
|
||||
| Client test headless blocks CI | Low | Can't automate client tests | Manual `make pre-pr-client` interim |
|
||||
| Fixture BLOCKER frustrates devs | Low | Devs skip pre-pr | Education; false positives are worse |
|
||||
|
||||
---
|
||||
|
||||
## 17. Points of Agreement (consensus)
|
||||
|
||||
1. **Sprint 8 is infrastructure.** All agents accept Tyre's framing: build the pipes, fill them Sprint 9+.
|
||||
2. **Determinism fixes are complete.** Copy-pasteable code, 4 regression tests, ~22 lines total.
|
||||
3. **`make pre-pr` is the developer discipline tool.** Replaces CI until CI is greenlighted. BLOCKER on fixture staleness.
|
||||
4. **Gauntlet map design is complete.** 7+Hub rooms, 48 entities, 2 cross-cuts. Additive-only constraint.
|
||||
5. **Test client binary architecture is settled.** `tooling/test-client/` crate (lead override), text renderer in server library, golden file JSON comparison.
|
||||
6. **38 client tests are prioritized and build-ready.** Function names, assertions, setup — ready for Stig to implement.
|
||||
7. **Anti-tedium is fully specified.** All 4 features have implementation details. Priority ranked.
|
||||
8. **All open questions resolved.** Zero questions carry forward to implementation.
|
||||
|
||||
## 18. Points of Tension (resolved or minor)
|
||||
|
||||
1. **Fixture staleness: BLOCKER.** Tyre's argument accepted by all. Justine updated spec.
|
||||
2. **Sprint 8 anti-tedium:** Ozzie/Gestalt prioritize room reset + WRONG button for Sprint 8. Tyre defers both to Sprint 9 (infrastructure-only scope). The implementation plan (Tyre) is authoritative for Sprint 8 scope; anti-tedium ships Sprint 9.
|
||||
3. **Gauntlet room count for Sprint 8:** Dudley (5 rooms), Gestalt (7+Hub). Tyre (0 rooms — infrastructure only). Resolution: map design has 7+Hub, room building is Sprint 9.
|
||||
4. **Test client location:** Tyre R3 says `server/src/bin/`. Lead overrides to `tooling/test-client/`. Dudley confirms no server changes needed. Lead override is authoritative.
|
||||
5. **F3 debug overlay:** Deferred indefinitely per Stig. WRONG button captures same data. Ozzie accepts.
|
||||
|
||||
---
|
||||
|
||||
## 19. Gaps Remaining
|
||||
|
||||
1. **Gauntlet content authoring.** Room YAML, entity placement, checklist definitions need writing. No specific agent assigned for content creation.
|
||||
2. **Client test headless stability (OQ-11).** Still unverified. Must be tested before making client tests a CI merge gate.
|
||||
3. **Sound events not in ObserverSnapshot.** Required for full test client display (sections 5-6). Not yet designed server-side.
|
||||
4. **Cognitive delay visual not implemented.** Tyre's tests #33-34 (blob rendering, recognition transition) depend on D-060 client implementation.
|
||||
5. **Gauntlet map vs Dudley's loader room count.** Gestalt designs 8 rooms (7+Hub). Dudley's loader MVP has 5 rooms. Reconciliation needed during Sprint 9 implementation (which rooms build first).
|
||||
Reference in New Issue
Block a user