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>
132 lines
6.5 KiB
Markdown
132 lines
6.5 KiB
Markdown
---
|
|
title: "Sprint 19 — CI Briefing"
|
|
description: "Test runner scripts, protocol version handshake, IPC round-trip benchmark"
|
|
type: sprint
|
|
status: archived
|
|
sprint: 19
|
|
team: "ci"
|
|
---
|
|
|
|
# Sprint 19: Persist — CI Tasks
|
|
|
|
**Goal:** The player can save and resume a game session with per-game directories; the simulation tier system gains eviction and scope pinning; and the first test infrastructure ships with information boundary validation and IPC hardening.
|
|
|
|
**Branch:** `ci`
|
|
**Agents:** Hoshe (QA/CI), Oscar (networking)
|
|
|
|
## Carry-over from Sprint 18
|
|
|
|
None.
|
|
|
|
## New Tickets
|
|
|
|
| # | Title | Blocked by |
|
|
|---|-------|------------|
|
|
| #270 | Test runner bash scripts | — |
|
|
| #556 | Protocol version handshake: client | #555 (server) |
|
|
| #342 | IPC round-trip timing benchmark | #555, #556 |
|
|
| #271 | IPC serialization fixture files | #270 |
|
|
|
|
Use `db/connectors/ticket show <id>` for full details.
|
|
|
|
## Key Decisions
|
|
|
|
- `decisions/architecture.md` — D-020 (IPC architecture, MessagePack codec, SimBridge trait), D-030 (three-layer test architecture: fixture / mock-protocol / real-subprocess)
|
|
|
|
## Notes
|
|
|
|
### #270 — Test runner bash scripts
|
|
|
|
The test infrastructure has no standardized entry points for CI or agents to invoke. This ticket ships the runner layer.
|
|
|
|
What this ticket must deliver:
|
|
Six scripts at `tests/`:
|
|
1. `tests/run-rust` — runs `cargo test` in `server/`, exits 0/non-zero, JSON stdout summary
|
|
2. `tests/run-godot` — runs GUT headlessly (`godot --headless -s client/tests/run_gut.gd`), exits 0/non-zero
|
|
3. `tests/run-ipc-fixtures` — Layer 1: reads fixture files from `tests/fixtures/`, validates via Rust + GDScript, exits 0/non-zero
|
|
4. `tests/run-ipc-protocol` — Layer 2: runs mock subprocess protocol state machine tests
|
|
5. `tests/run-ipc-integration` — Layer 3: starts real server subprocess, runs full round-trip, kills it
|
|
6. `tests/run-all` — invokes all five in order, collects exit codes, reports JSON summary
|
|
|
|
Script requirements per ticket description: exit code 0/non-zero, structured JSON stdout, accepts filter arguments (`--filter test_name`), no interactive input, whitelistable for Claude Code agents (no TTY prompts).
|
|
|
|
JSON stdout format (consistent across all scripts):
|
|
```json
|
|
{"suite": "rust", "total": 42, "passed": 42, "failed": 0, "duration_ms": 1230}
|
|
```
|
|
|
|
These scripts are the entry points that `make ci-server`, `make ci-client`, and future CI pipelines call. Coordinate with Makefile targets in `docs/DEVOPS.md`.
|
|
|
|
### #556 — Protocol version handshake: client
|
|
|
|
Blocked by #555 (server must send `HandshakeMessage` first).
|
|
|
|
What this ticket must deliver:
|
|
- `client/scripts/protocol/local_bridge.gd` (or `server_process.gd`): after starting the server subprocess, read the first framed message from the IPC channel
|
|
- Validate it is a `HandshakeMessage` with `protocol_version == Protocol.PROTOCOL_VERSION` (14)
|
|
- If mismatch: log error "Protocol version mismatch: server=%d, client=%d", emit a `handshake_failed` signal, shut down the server process gracefully
|
|
- If match: emit `handshake_complete`, begin normal tick loop
|
|
- Add a timeout: if no handshake message received within 5 seconds of process start, treat as mismatch
|
|
|
|
Current state: `client/scripts/protocol/protocol.gd` already checks `version` in `decode_snapshot()` and logs a mismatch. That check is per-snapshot. The handshake is the startup-time equivalent — validate once at connection, not per tick.
|
|
|
|
Files: `client/scripts/protocol/local_bridge.gd`, `client/scripts/protocol/server_process.gd`.
|
|
|
|
### #342 — IPC round-trip timing benchmark
|
|
|
|
Sprint exit criterion. Measures the complete latency path from server serialization to client scene update.
|
|
|
|
What this ticket must deliver:
|
|
- A benchmark script `tests/run-ipc-benchmark` that:
|
|
1. Starts the server subprocess
|
|
2. Waits for handshake (#555/#556)
|
|
3. Sends N `PlayerInput` messages (N = 100 by default)
|
|
4. Measures from `rmp_serde::to_vec` (server) to scene update completion (client)
|
|
5. Reports p50/p95/p99 latencies in milliseconds
|
|
6. Flags if any percentile exceeds 5ms threshold
|
|
- Output JSON: `{"p50_ms": 1.2, "p95_ms": 2.8, "p99_ms": 4.1, "threshold_ms": 5, "passed": true}`
|
|
- The benchmark is run as part of `tests/run-ipc-integration` in Layer 3
|
|
|
|
Implementation approach: server-side timestamps in `ObserverSnapshot` (add `server_emit_tick_ms` field, stripped in production builds), client records receive timestamp via `Time.get_ticks_msec()`. Delta = client receive - server emit.
|
|
|
|
Blocked by #555 and #556 — benchmark requires a working handshake before timing can start cleanly.
|
|
|
|
### #271 — IPC serialization fixture files
|
|
|
|
Layer 1 test data: pre-generated `.msgpack` fixture files that both Rust and GDScript can read to verify cross-language serialization compatibility.
|
|
|
|
What this ticket must deliver:
|
|
- A Rust binary (or test in `server/src/`) that generates fixtures to `tests/fixtures/`:
|
|
- `snapshot_minimal.msgpack` — minimal valid `ObserverSnapshot` (version=14, tick=0, one entity)
|
|
- `snapshot_full.msgpack` — all optional fields populated (monologue, dialogue, inventory, POIs, KG dump)
|
|
- `player_input_move.msgpack` — `PlayerInput { tick: 1, action: MoveNorth }`
|
|
- `player_input_interact.msgpack` — `PlayerInput { tick: 2, action: Interact { target: 99, verb: "Talk" } }`
|
|
- `malformed.msgpack` — intentionally truncated bytes (tests error handling)
|
|
- A GDScript test `client/tests/test_ipc_fixtures.gd` that reads each `.msgpack` fixture file, decodes via `Protocol.decode_snapshot()` / `Protocol.decode_player_input()`, and asserts expected field values
|
|
- Cross-language verification: the same byte stream decoded by both Rust and GDScript must produce identical field values
|
|
|
|
The fixture generator is run once (manually or in CI pre-step) to produce the committed `.msgpack` files. The files live at `tests/fixtures/` and are committed to the repo.
|
|
|
|
Blocked by #270 — fixture tests are invoked by `tests/run-ipc-fixtures`.
|
|
|
|
## Dependency Chain
|
|
|
|
```
|
|
#555 (server: protocol handshake) → #556 (ci: protocol handshake: client)
|
|
#555 + #556 → #342 (IPC benchmark: requires working handshake)
|
|
|
|
#270 (test runner scripts) → #271 (fixture files: invoked by run-ipc-fixtures)
|
|
|
|
Parallel starts: #270, #555 (server-side) — both unblocked week 1
|
|
#556 starts after #555 is at review
|
|
#271 starts after #270 merges
|
|
#342 starts after #555 + #556 both land
|
|
```
|
|
|
|
## 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(ci): description" --description "body" --base main --head ci
|
|
```
|