ui/phase-14: rename planet end-to-end + order read-back

Wires the first end-to-end command through the full pipeline:
inspector rename action → local order draft → user.games.order
submit → optimistic overlay on map / inspector → server hydration
on cache miss via the new user.games.order.get message type.

Backend: GET /api/v1/user/games/{id}/orders forwards to engine
GET /api/v1/order. Gateway parses the engine PUT response into the
extended UserGamesOrderResponse FBS envelope and adds
executeUserGamesOrderGet for the read-back path. Frontend ports
ValidateTypeName to TS, lands the inline rename editor + Submit
button, and exposes a renderedReport context so consumers see the
overlay-applied snapshot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-09 11:50:09 +02:00
parent 381e41b325
commit f80c623a74
86 changed files with 7505 additions and 138 deletions
+139 -24
View File
@@ -1522,27 +1522,138 @@ Targeted tests:
inspector content, and on `chromium-mobile-iphone-13` asserts the
bottom-sheet appears and the close button clears it.
## Phase 14. First End-to-End Command — Rename Planet
## ~~Phase 14. First End-to-End Command — Rename Planet~~
Status: pending.
Status: done.
Goal: prove the entire pipeline (inspector → composer → submit →
server → state refresh) by wiring up exactly one action: renaming a
planet.
Artifacts:
Decisions taken with the project owner during implementation:
- `ui/frontend/src/lib/inspectors/planet.svelte` adds a `Rename` action
that opens a small inline editor and adds a `RenamePlanet` command
to the order draft on confirm
- `ui/frontend/src/sync/submit.ts` `submitOrder()` function that POSTs
the entire draft via `GalaxyClient.execute('user.games.order', ...)`
and applies per-command results
- `ui/frontend/src/lib/sidebar/order-tab.svelte` adds a `Submit order`
button calling `submitOrder()` and renders accepted / rejected
status per command after submit
- on successful submit, refresh game state so the rename appears on the
map and in the inspector
1. **Optimistic overlay over `user.games.order`.** The plan's
acceptance criterion ("name change within one second") is
inconsistent with the engine's order endpoint, which only
validates and stores; rename takes effect at turn cutoff.
Phase 14 keeps `user.games.order` for the wire path and adds a
pure projection `applyOrderOverlay(report, commands, statuses)`
in `api/game-state.ts`. Inspector, mobile sheet, and map
renderer read a derived `renderedReport` (context key
`RENDERED_REPORT_CONTEXT_KEY`) that swaps planet names in for
every applied or in-flight rename. Raw `gameState.report`
stays available for debugging / history mode.
2. **Read-back endpoint `user.games.order.get`.** Without a
server snapshot of stored orders the optimistic overlay would
not survive a cache wipe. Phase 14 adds the new authenticated
message type with a backend route
`GET /api/v1/user/games/{game_id}/orders?turn=N` (pass-through
to the engine's existing `GET /api/v1/order`). The frontend
calls it from `OrderDraftStore.hydrateFromServer` only when
the local cache row is *absent* — an explicitly empty cache
row honours the user's empty draft. The `turn` query is
required (the frontend always knows the current turn from the
lobby record).
3. **Per-command results from real engine response.** The engine
now answers `PUT /api/v1/order` with `202 Accepted` and a
populated `UserGamesOrder` body (per-command `cmdApplied`,
`cmdErrorCode`, plus an engine-assigned `updatedAt`). The
gateway parses that JSON into the extended FBS
`UserGamesOrderResponse` envelope and the frontend reads the
per-command outcome through `submitOrder`. A defensive
batch-level fallback covers an empty `commands` array.
4. **Applied commands stay in the draft.** Per the gameplay
model, the order is the player's intent surface — submitted
commands stay until the user removes them or until turn
cutoff (Phase 24 wires the auto-clear). Statuses are
runtime-only; on reload the draft re-validates as `valid` and
the overlay re-applies.
5. **Validator parity through a TS port.** `ValidateTypeName`
from `pkg/util/string.go` is mirrored in
`ui/frontend/src/lib/util/entity-name.ts`. The inspector's
inline editor disables the confirm button until the input
passes; the draft store re-runs the validator on every `add`
and exposes per-row `valid` / `invalid` to the order tab.
6. **`updatedAt` plumbing without enforcement.** Phase 14 sends
`0` on every submit (no client-side stale-order detection
yet); the engine still writes a real timestamp, the gateway
surfaces it in the FBS response, and the draft stashes it.
Future phases can wire conditional updates without a wire
change.
Artifacts (delivered):
- `pkg/schema/fbs/order.fbs` — extended `UserGamesOrderResponse`
(`game_id`, `updated_at`, `commands`); new
`UserGamesOrderGet` / `UserGamesOrderGetResponse` tables.
- `pkg/model/order/order.go` — `MessageTypeUserGamesOrderGet` and
`UserGamesOrderGet` typed payload.
- `pkg/transcoder/order.go` — `JSONToUserGamesOrder`,
`UserGamesOrderResponseToPayload`,
`UserGamesOrderGetToPayload`,
`PayloadToUserGamesOrderGet`,
`PayloadToUserGamesOrderResponse`,
`UserGamesOrderGetResponseToPayload`,
`PayloadToUserGamesOrderGetResponse`. Replaces the old
`EmptyUserGamesOrderResponsePayload` helper.
- `backend/internal/server/handlers_user_games.go` — new
`GetOrders` handler. `engineclient.GetOrder` forwards to the
engine's `GET /api/v1/order` with the player rebound.
`backend/openapi.yaml` documents the new GET operation;
`contract_test.go` extended with a `queryParamStubs` map for
required query parameters.
- `gateway/internal/backendclient/games_commands.go` — updated
`executeUserGamesOrder` (parses real engine JSON via
`JSONToUserGamesOrder`); new `executeUserGamesOrderGet` and
`projectUserGamesOrderGetResponse`.
`gateway/internal/backendclient/routes.go` registers the new
message type.
- `ui/Makefile` — `order.fbs` joins `FBS_INPUTS`; regenerated TS
bindings under `ui/frontend/src/proto/galaxy/fbs/order/`.
- `ui/frontend/src/sync/order-types.ts` — `PlanetRenameCommand`
variant added to the discriminated union.
- `ui/frontend/src/sync/submit.ts` — `submitOrder` posts the FBS
request and parses per-command verdicts.
- `ui/frontend/src/sync/order-load.ts` — `fetchOrder` issues
`user.games.order.get`.
- `ui/frontend/src/sync/order-draft.svelte.ts` — extended with
per-command `statuses`, `validate` / `markSubmitting` /
`applyResults` / `markRejected` / `revertSubmittingToValid` /
`hydrateFromServer`, and the `needsServerHydration` flag.
- `ui/frontend/src/lib/util/entity-name.ts` — TS port of
`ValidateTypeName`.
- `ui/frontend/src/api/game-state.ts` — pure
`applyOrderOverlay(report, commands, statuses)` projection
plus the `currentTurn` rune on `GameStateStore`.
- `ui/frontend/src/lib/rendered-report.svelte.ts` — derives the
overlay-applied report and exposes it through
`RENDERED_REPORT_CONTEXT_KEY`.
- `ui/frontend/src/lib/galaxy-client-context.svelte.ts` —
`GalaxyClientHolder` so command-driven UI can resolve the
per-game `GalaxyClient` via context.
- `ui/frontend/src/lib/inspectors/planet.svelte` — Rename action
+ inline editor with `validateEntityName`-driven feedback.
- `ui/frontend/src/lib/sidebar/order-tab.svelte` — per-row
status, Submit button with disabled-state matrix, refresh on
success, surfaces batch errors inline.
- `ui/frontend/src/lib/sidebar/inspector-tab.svelte` and
`ui/frontend/src/lib/active-view/map.svelte` — switched to
`renderedReport`.
- `ui/frontend/src/routes/games/[id]/+layout.svelte` — wires the
rendered report and galaxy-client contexts; runs
`orderDraft.hydrateFromServer(...)` after the boot
`Promise.all` resolves when `needsServerHydration`.
- `ui/frontend/src/lib/i18n/locales/{en,ru}.ts` — keys for
rename action / editor / order statuses / submit copy.
- Tests: `entity-name.test.ts`, `submit.test.ts`,
`order-load.test.ts`, `order-overlay.test.ts`,
`order-tab.test.ts`, extended `order-draft.test.ts` and
`inspector-planet.test.ts`. New Playwright spec
`tests/e2e/rename-planet.spec.ts`.
- Documentation: `docs/ARCHITECTURE.md` §9, `docs/FUNCTIONAL.md`
§6.2 (and `docs/FUNCTIONAL_ru.md` mirror), `ui/docs/order-composer.md`
with the new "Submit pipeline", "Optimistic overlay", and
"Server hydration on cache miss" sections.
Dependencies: Phases 12, 13.
@@ -1550,19 +1661,23 @@ Acceptance criteria:
- the user can select a planet, click `Rename`, type a new name, see
the command appear in the order tab, click `Submit`, and observe the
planet's name change everywhere within one second;
- attempting an empty or invalid name is blocked locally (button
disabled with tooltip);
- a server-side rejection (race condition) is surfaced as `rejected`
status in the order tab.
planet's name change everywhere within one second (overlay applies
immediately on the inspector / mobile sheet / map; server-side state
catches up at turn cutoff);
- attempting an empty or invalid name is blocked locally (Submit
button disabled, inline error message under the input);
- a server-side rejection is surfaced as `rejected` status on every
in-flight row, with the gateway's error message inline.
Targeted tests:
- Vitest unit tests for `submitOrder` with mocked `GalaxyClient`;
- Vitest component test for the inline rename editor including
validation;
- Playwright e2e: rename a seeded planet, reload, confirm the new name
persists.
- Vitest unit tests for `submitOrder`, `fetchOrder`,
`applyOrderOverlay`, `validateEntityName`, and the extended
`OrderDraftStore`.
- Vitest component tests for the inline rename editor and the
Submit button states.
- Playwright e2e: rename a seeded planet, reload, confirm the new
name persists; rejected path keeps the old name.
## Phase 15. Inspector — Planet Production Controls