ui/phase-15: planet inspector production controls + order-draft collapse

Adds the second end-to-end command (`setProductionType`) with a
collapse-by-`planetNumber` rule on the order draft, the segmented
production-controls component on the planet inspector, the FBS
encoder/decoder pair for `CommandPlanetProduce`, and the
`localShipClass` projection on `GameReport`. Forecast number is
deferred and tracked in the new `ui/docs/calc-bridge.md`.
This commit is contained in:
Ilia Denisov
2026-05-09 15:54:30 +02:00
parent c4f1409329
commit 915b4372dd
31 changed files with 2200 additions and 76 deletions
+44 -2
View File
@@ -95,7 +95,7 @@ stored value).
`OrderCommand` is a discriminated union on the `kind` field. Phase
12 shipped the skeleton with a single content-free variant; Phase
14 adds the first real one:
14 added the first real one and Phase 15 added the second:
```ts
interface PlaceholderCommand {
@@ -111,7 +111,20 @@ interface PlanetRenameCommand {
readonly name: string;
}
type OrderCommand = PlaceholderCommand | PlanetRenameCommand;
interface SetProductionTypeCommand {
readonly kind: "setProductionType";
readonly id: string;
readonly planetNumber: number;
readonly productionType:
| "MAT" | "CAP" | "DRIVE" | "WEAPONS"
| "SHIELDS" | "CARGO" | "SCIENCE" | "SHIP";
readonly subject: string;
}
type OrderCommand =
| PlaceholderCommand
| PlanetRenameCommand
| SetProductionTypeCommand;
```
The `id` field is the canonical identifier the store uses for
@@ -123,6 +136,35 @@ with the inline editor in `lib/inspectors/planet.svelte`, the
local validator (`lib/util/entity-name.ts`, parity with
`pkg/util/string.go.ValidateTypeName`), and the submit pipeline.
`setProductionType` is the wire-mirror of the engine's
`CommandPlanetProduce` (`pkg/model/order/order.go`). The local
validator runs the same `subject=Production` rule as
`game/internal/router/validator.go`: `subject` is required and
must satisfy `validateEntityName` when `productionType` is
`SCIENCE` or `SHIP`; otherwise it is the empty string. The
optimistic overlay rewrites `planet.production` using
`productionDisplayFromCommand` (`api/game-state.ts`), which
mirrors the engine's `Cache.PlanetProductionDisplayName` so the
overlay stays byte-equal with the next server report.
### Collapse-by-target rule (Phase 15)
`setProductionType` is the first variant to carry a
collapse-by-target rule. `OrderDraftStore.add` enforces it:
when the incoming command's `kind` is `"setProductionType"` it
drops every prior `setProductionType` entry with the same
`planetNumber` (and the matching keys from `statuses`) before
appending. Other variants keep their append-only behaviour —
each `planetRename` is a distinct user-visible action and
collapsing them would lose intent.
Net effect on the order tab: at most one `setProductionType`
row per planet, regardless of how many times the player clicks
through the inspector segments. Auto-sync still fires on every
mutation; the engine accepts repeat submits idempotently. A
`setProductionType` and a `planetRename` for the same planet
coexist — the rules apply within a `kind`, not across.
## Store
`OrderDraftStore` lives in