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
+109 -21
View File
@@ -1686,38 +1686,126 @@ Verified on local-ci run 11 (`success`, f80c623).
Status: pending.
Goal: let the user switch a planet's production type to industry,
materials, research a science, or build a ship class; each change
appends a command to the order draft.
materials, research a tech field, or build a ship class; each change
appends a command to the order draft. Repeated changes for the same
planet collapse to the latest choice.
Artifacts:
Decisions taken with the project owner during implementation:
- `ui/frontend/src/lib/inspectors/planet/production.svelte` segmented
control with the four production options; a sub-picker for science
and ship class targets
- `ui/frontend/src/sync/order-types.ts` extends with
`SetProductionType` command variant
- references to `pkg/calc/` predictions (free production potential,
forecast output for current type) — wired through `ui/core/calc/`
- audit `ui/docs/calc-bridge.md` updates this phase's required calc
functions; if any are missing in `pkg/calc/`, raise as blocker
1. **Forecast is deferred and raised as a blocker.** The plan's audit
clause discovered that `pkg/calc/` only carries the two ship-side
functions (`ShipProductionCost`, `PlanetProduceShipMass`); every
other forecast formula (industry, materials, per-tech research,
production capacity) lives inside
`game/internal/model/game/planet.go` and is not exported.
`ui/core/calc/` and `ui/docs/calc-bridge.md` did not exist at all.
Phase 15 creates `ui/docs/calc-bridge.md` documenting the gap and
waives the forecast deliverable until a dedicated future phase
builds the real Go → WASM → TS bridge. The inspector continues to
show only the existing `freeIndustry` (free production potential)
number, which is computed engine-side and ships in the report
payload.
2. **Sub-pickers expose only what the game data already supports.**
"Research" sub-row shows the four implicit tech fields
(DRIVE / WEAPONS / SHIELDS / CARGO); custom `LocalScience`
entries are deferred until the science designer phase introduces
them. "Build Ship" sub-row shows `LocalShipClass` entries; the
`GameReport` projection is extended with a minimal
`ShipClassSummary { name }` so the e2e spec can seed one ship
class and exercise the SHIP branch end-to-end. Empty
`LocalShipClass` collapses to a localised "no ship classes
designed yet" placeholder.
3. **Re-clicks always emit a command.** The collapse-by-`planetNumber`
rule keeps at most one `setProductionType` per planet in the
draft. A click that lands on the segment matching `report.production`
still emits a command; the engine accepts repeat submits
idempotently. Avoids a fragile reverse-mapping from
`report.production` display strings (`"Drive"`, ship-class name,
science name) back to the FBS enum.
4. **Inspector layout split.** `ui/frontend/src/lib/inspectors/planet/
production.svelte` is the new component; the parent
`inspectors/planet.svelte` mounts it for `kind === "local"`
planets and drops the static read-only "current production" row
on that branch (the row stays for non-local planets). The mobile
sheet (`planet-sheet.svelte`) and the sidebar
(`sidebar/inspector-tab.svelte`) both forward
`localShipClass` from the rendered-report context.
Artifacts (delivered):
- `ui/frontend/src/sync/order-types.ts` — `SetProductionTypeCommand`
variant + `ProductionType` literal union + `PRODUCTION_TYPE_VALUES`
/ `isProductionType` helpers.
- `ui/frontend/src/sync/order-draft.svelte.ts` — `validateCommand`
branch (mirrors the engine's `subject=Production` rule); `add`
enforces collapse-by-`planetNumber` for the new variant only.
- `ui/frontend/src/sync/submit.ts` — encodes
`CommandPlanetProduce` via the new `productionTypeToFBS` helper.
- `ui/frontend/src/sync/order-load.ts` — decodes
`CommandPlanetProduce` via `productionTypeFromFBS` and skips
`PlanetProduction.UNKNOWN` rows.
- `ui/frontend/src/api/game-state.ts` — `applyOrderOverlay` rewrites
`planet.production` for `setProductionType` (helper
`productionDisplayFromCommand` mirrors
`Cache.PlanetProductionDisplayName`); new `ShipClassSummary` type
and `GameReport.localShipClass` projection (decoded from
`report.localShipClass`).
- `ui/frontend/src/lib/inspectors/planet/production.svelte` — new
segmented control with Research / Build-Ship sub-rows.
- `ui/frontend/src/lib/inspectors/planet.svelte` — accepts
`localShipClass` prop, mounts `<Production />` for local planets,
drops the static production row on that branch only.
- `ui/frontend/src/lib/inspectors/planet-sheet.svelte` and
`ui/frontend/src/lib/sidebar/inspector-tab.svelte` — forward
`localShipClass` from the rendered report context.
- `ui/frontend/src/routes/games/[id]/+layout.svelte` — derives
`localShipClass` and passes it to the mobile sheet.
- `ui/frontend/src/lib/sidebar/order-tab.svelte` — new label branch
for `setProductionType` using the new locale key.
- `ui/frontend/src/lib/i18n/locales/{en,ru}.ts` — production-control
copy plus the new order-tab label.
- `ui/frontend/tests/e2e/fixtures/report-fbs.ts` — extended with a
`localShipClass` fixture vector.
- `ui/frontend/tests/e2e/fixtures/order-fbs.ts` — discriminated
fixture union supporting both `planetRename` and
`setProductionType` payloads.
- `ui/docs/calc-bridge.md` (new) — calc-bridge gap analysis and the
Phase 15 waiver.
- `ui/docs/order-composer.md` — updated discriminated-union
reference + new "Collapse-by-target rule" section.
- Tests: extended `order-draft.test.ts`, `submit.test.ts`,
`order-load.test.ts`, `order-overlay.test.ts`,
`game-state.test.ts`, `inspector-planet.test.ts`; new
`inspector-planet-production.test.ts` Vitest component spec; new
`tests/e2e/planet-production.spec.ts` Playwright spec.
Dependencies: Phase 14.
Acceptance criteria:
- changing production type adds exactly one `SetProductionType`
command to the order draft;
- changing production type adds exactly one `setProductionType`
command to the order draft, with the engine wire shape
(`CommandPlanetProduce` + `subject` rule for `SCIENCE` / `SHIP`);
- repeated changes for the same planet collapse to the latest choice
(no duplicate commands per planet);
- forecast output number reflects the chosen production type and
matches `pkg/calc/` outputs.
(no duplicate `setProductionType` commands per planet); other
variants (e.g. `planetRename`) keep their append-only behaviour;
- forecast output number is intentionally **not** rendered in this
phase (waived per decision 1; tracked in `ui/docs/calc-bridge.md`).
Targeted tests:
- Vitest unit tests for the collapse-duplicates logic in order draft;
- Vitest component tests for forecast number rendering;
- Playwright e2e: switch production three times, submit, confirm
server reflects the latest choice.
- Vitest unit tests for the collapse-by-`planetNumber` logic in
`OrderDraftStore.add` and the `setProductionType` branch of
`validateCommand`;
- Vitest unit tests for the FBS encoder / decoder round-trip and the
`productionDisplayFromCommand` helper;
- Vitest component tests for the segmented control's segment
emission, sub-row reveal, empty-classes placeholder, and active-
highlight derivation;
- Playwright e2e: switch production three times across all four
segments, confirm the order tab carries exactly one row at every
step, gateway records the latest choice (`SHIP` + class name),
reload preserves the row through `user.games.order.get`.
## Phase 16. Inspector — Cargo Routes