Standardized YAML frontmatter on all 20 files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
602 lines
28 KiB
Markdown
602 lines
28 KiB
Markdown
---
|
|
title: "QA Strategy & Test Architecture Workshop — Round 3 Notes"
|
|
description: "Qatux's synthesis notes from round 3 prioritization and final specifications"
|
|
type: workshop
|
|
status: archived
|
|
workshop: test-architecture
|
|
agent: ""
|
|
round: 3
|
|
created: 2026-02-17
|
|
---
|
|
|
|
# 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).
|