ui/synthetic-report: PLAN parity rule + testing doc

Locks in the synthetic-report parity rule as a global "Assumptions
and Defaults" entry in ui/PLAN.md: every phase that extends the
server->UI report contract must also extend the legacy parser in
the same PR (or document in tools/local-dev/legacy-report/README.md
why the new field cannot be derived from legacy text). The Go side
already enforces shape compatibility via the pkg/model/report
import; this rule extends that mechanical guard to "did we remember
to wire the new field through".

ui/docs/testing.md grows a "Synthetic reports for visual testing"
section with the full conversion -> load -> compose loop and the
two operational gotchas (no network on synthetic ids, page reload
clears the in-memory map).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-10 11:08:13 +02:00
parent 8f320010c6
commit f5ac9fac59
2 changed files with 69 additions and 0 deletions
+50
View File
@@ -109,6 +109,56 @@ mode off, troubleshooting common boot issues).
The local-dev stack is independent from the local-ci stack below;
they bind different ports and can run side by side.
## Synthetic reports for visual testing (DEV)
For visual checks of the map, inspectors and order-overlay against
rich game states, the lobby exposes a DEV-only "Load synthetic
report" affordance (`import.meta.env.DEV`). The flow is:
1. Convert a legacy text report (`tools/local-dev/reports/{dg,gplus}/`)
to JSON with the Go CLI:
```sh
go run ./tools/local-dev/legacy-report/cmd/legacy-report-to-json \
--in tools/local-dev/reports/dg/KNNTS039.REP \
--out /tmp/dg39.json
```
See [`../../tools/local-dev/legacy-report/README.md`](../../tools/local-dev/legacy-report/README.md)
for what the parser populates today and how to extend it when a
new UI phase decodes a new `Report` field.
2. Run the UI dev server (`pnpm -C ui/frontend dev`), open the lobby,
and use the "Load JSON…" file picker in the **Synthetic test
reports (DEV)** section. The page navigates to
`/games/synthetic-<uuid>/map` with the report wired into the
in-game shell.
In synthetic mode:
- The map view, inspectors and races view render against the loaded
report exactly as they would for a real game.
- Composing orders works locally (overlay applies through
`applyOrderOverlay` as usual), but **nothing is sent to the
gateway** — `OrderDraftStore.scheduleSync` short-circuits because
the synthetic id is not a UUID and the layout deliberately does
not bind a `GalaxyClient` for this game.
- The order draft is persisted into the platform `Cache` under the
same `order-drafts` namespace as real games, keyed by the
synthetic id, so navigating back into the same synthetic session
restores the draft. The cache is cleared with
`__galaxyDebug.clearOrderDraft(gameId)` (DEV debug surface).
- A page reload loses the in-memory report registry; opening the
same synthetic id afterwards redirects to /lobby. Re-load the JSON
to reseed.
The synthetic-report parity rule (see [`../PLAN.md`](../PLAN.md) §
Assumptions and Defaults) requires every UI phase that extends
`decodeReport` to also extend the legacy parser in lockstep, or to
record in the parser's `README.md` that the new field cannot be
derived from legacy text. This keeps the synthetic-mode coverage in
step with the contract as the UI grows.
## Local CI verification
`tools/local-ci/` ships a self-contained Gitea + Actions runner via