docs(ui): finalize MVP plan structure and de-archaeologize topic docs
MVP web client (Phases 1-30) is complete; reorganize planning + living docs around that. - PLAN.md kept as the staged MVP record (1-30) with a status block + pointers; removed the 31-36 stages, regression scenarios, and deferred-TODO section (moved out); fixed a stale cross-machine plan path. - ui/PLAN-finalize.md (new): active web-finalization plan in 8 stages (visual system, a11y, i18n, error UX, PWA, build hygiene, docs, owner manual-QA loop); absorbs former Phases 33 and 35. - ui/ROADMAP.md (new): post-MVP (Wails, Capacitor, realistic projection, acceptance + regression scenarios) and triaged deferred follow-ups. - ui/docs/README.md (new): grouped topic-doc index. - De-archaeologized all 20 ui/docs topic docs + ui/README.md + ui/core/README.md: stripped Phase-N build history, rewritten as current-state; deferred work now points at ROADMAP.md / PLAN-finalize.md. Docs-only; no code change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+14
-382
@@ -10,11 +10,18 @@ libraries (iOS/Android), and embedded directly in Wails (desktop). All
|
||||
network I/O lives on the TypeScript side via ConnectRPC, so the Go
|
||||
module is a pure compute boundary on every platform.
|
||||
|
||||
> **Status — MVP complete.** Phases 1–30 below (the full web client) are
|
||||
> done. Remaining work is tracked outside this file: the active web
|
||||
> finalization pass (visual system, accessibility, localisation, error
|
||||
> UX, PWA, docs) in [PLAN-finalize.md](PLAN-finalize.md), and the
|
||||
> deferred platform wrappers (Wails desktop, Capacitor mobile), the
|
||||
> realistic multi-turn projection, and the cross-platform acceptance
|
||||
> pass in [ROADMAP.md](ROADMAP.md). This file is retained as the staged
|
||||
> record of how the MVP was built.
|
||||
|
||||
The existing Fyne client in `client/` is deprecated and is not modified
|
||||
or imported by the new code. The strategy and rationale behind these
|
||||
choices live in the plan file at
|
||||
`/Users/id/.claude/plans/buzzing-questing-fountain.md`; the architectural
|
||||
overview is mirrored into `ui/README.md` as part of Phase 1.
|
||||
or imported by the new code. The architectural overview is mirrored into
|
||||
`ui/README.md`.
|
||||
|
||||
Each phase ends with a runnable artifact. The visual progression is:
|
||||
empty page → navigation skeleton → stubbed views → live data → real
|
||||
@@ -26,7 +33,9 @@ plan can be adjusted with at most one phase of rework.
|
||||
|
||||
## Summary
|
||||
|
||||
This plan breaks implementation into 36 small reviewable phases. Each
|
||||
This plan staged the MVP web client as 30 small reviewable phases (1–30,
|
||||
all complete; the finalization pass and post-MVP work live in
|
||||
[PLAN-finalize.md](PLAN-finalize.md) and [ROADMAP.md](ROADMAP.md)). Each
|
||||
phase has a single primary goal, clear deliverables, explicit
|
||||
dependencies, acceptance criteria, and focused tests. Tests live
|
||||
alongside the code added in the phase; a phase is not closed until its
|
||||
@@ -3408,380 +3417,3 @@ Note: the WASM artefact `ui/frontend/static/core.wasm` must be rebuilt
|
||||
(`make wasm`, needs TinyGo) for the new bridge functions to be present
|
||||
at runtime and in the Playwright suite; Vitest injects a fake `Core`
|
||||
and does not need the rebuild.
|
||||
|
||||
## Phase 31. Wails Desktop Wrapper
|
||||
|
||||
Status: pending. Re-evaluate Wails v2 vs v3 at phase start.
|
||||
|
||||
Goal: build a native desktop app for macOS, Windows, and Linux that
|
||||
runs the same frontend bundle and replaces the WASM core with embedded
|
||||
Go code.
|
||||
|
||||
Artifacts:
|
||||
|
||||
- topic doc `ui/docs/wails-version.md` recording the v2-vs-v3
|
||||
decision made at phase start with rationale
|
||||
- `ui/desktop/main.go` Wails entry point
|
||||
- `ui/desktop/app.go` IPC bindings exposing `ui/core` API to the
|
||||
WebView through a structured adapter
|
||||
- `ui/desktop/keychain/` per-OS secure-storage helpers (macOS Keychain
|
||||
via `Security` framework, Windows DPAPI, Linux Secret Service / file
|
||||
fallback at `~/.config/galaxy/keypair` with mode `0600`)
|
||||
- `ui/desktop/sqlite/` `modernc.org/sqlite` cache wired through Wails
|
||||
IPC
|
||||
- `ui/frontend/src/platform/core/wails.ts` `WailsCore` adapter
|
||||
- `ui/frontend/src/platform/store/wails.ts` `WailsKeyStore` and
|
||||
`WailsCache` adapters
|
||||
- `ui/desktop/build/icon.icns` macOS app icon
|
||||
- `ui/desktop/build/icon.ico` Windows app icon
|
||||
- `ui/desktop/build/icon.png` Linux app icon
|
||||
- `ui/Makefile` targets `desktop-mac`, `desktop-win`, `desktop-linux`
|
||||
- topic doc `ui/docs/desktop-secure-storage.md` documenting the
|
||||
Linux/Windows file fallback for missing keychains
|
||||
|
||||
Dependencies: Phase 6 (KeyStore and Cache interfaces); Phases 7
|
||||
through 30 in their web form (the desktop wrapper exercises the same
|
||||
TypeScript code).
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- the macOS, Windows, and Linux binaries each launch, complete login,
|
||||
and preserve the keypair across restarts on a fresh user profile;
|
||||
- a single source codebase produces all three OS bundles;
|
||||
- the same `Core` and `Storage` TypeScript interfaces are satisfied as
|
||||
on web, with no platform-specific code outside `platform/`;
|
||||
- Linux file fallback activates when Secret Service is absent and
|
||||
writes with `0600` permissions.
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- Go unit tests for each keychain helper, including file fallback;
|
||||
- desktop e2e smoke test driven by Wails headless mode running the
|
||||
Phase 7 login Playwright scenario via CDP;
|
||||
- regression test: keychain absence on a Linux container without
|
||||
libsecret falls back to file storage.
|
||||
|
||||
## Phase 32. Capacitor Mobile Wrapper
|
||||
|
||||
Status: pending.
|
||||
|
||||
Goal: build native iOS and Android apps that run the same frontend
|
||||
bundle and call into a gomobile-compiled `ui/core`.
|
||||
|
||||
Artifacts:
|
||||
|
||||
- `ui/mobile-bridge/bridge.go` gomobile-friendly façade over `ui/core`
|
||||
- `ui/Makefile` target `gomobile` producing `Galaxy.framework` and
|
||||
`galaxy.aar`
|
||||
- `ui/mobile/capacitor.config.ts` Capacitor project configuration
|
||||
- `ui/mobile/plugins/galaxy-core/` custom Capacitor plugin (Swift +
|
||||
Kotlin) wrapping the gomobile artifacts
|
||||
- `ui/frontend/src/platform/core/capacitor.ts` `CapacitorCore` adapter
|
||||
- `ui/frontend/src/platform/store/capacitor.ts` `CapacitorKeyStore`
|
||||
and `CapacitorCache` using `@capacitor-community/secure-storage-plugin`
|
||||
and `@capacitor-community/sqlite`
|
||||
- `ui/mobile/ios/App/Assets.xcassets/AppIcon.appiconset/` iOS app
|
||||
icon set
|
||||
- `ui/mobile/android/app/src/main/res/mipmap-*/` Android app icon
|
||||
set
|
||||
- iOS launch screen and Android splash screen
|
||||
- `ui/Makefile` targets `ios` and `android`
|
||||
- topic doc `ui/docs/mobile-bridge.md` describing the plugin
|
||||
API, marshalling strategy, and the manual smoke procedure for this
|
||||
phase
|
||||
|
||||
Dependencies: Phase 6; Phases 7 through 30 in their web form.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- both the iOS Simulator and an Android Emulator launch the app,
|
||||
complete login, and preserve the keypair across restarts (validated
|
||||
by manual smoke);
|
||||
- the same `Core` and `Storage` TypeScript interfaces are satisfied as
|
||||
on web and desktop;
|
||||
- gomobile build produces deterministic outputs reproducible in CI on
|
||||
a macOS runner.
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- Go unit tests for the `mobile-bridge` façade;
|
||||
- Capacitor plugin unit tests on iOS (XCTest) and Android (Espresso);
|
||||
- manual smoke procedure: login flow on iOS Simulator and Android
|
||||
Emulator, recorded in `ui/docs/mobile-bridge.md`. Full Appium
|
||||
automation lands in Phase 36 as part of the acceptance pass.
|
||||
|
||||
## Phase 33. PWA — Service Worker, Manifest, Web Icons
|
||||
|
||||
Status: pending.
|
||||
|
||||
Goal: make the web build installable and offline-tolerant on every
|
||||
browser. Native packaging icons live with their respective wrapper
|
||||
phases (31 for desktop, 32 for mobile) — this phase is web-only.
|
||||
|
||||
Artifacts:
|
||||
|
||||
- `ui/frontend/src/service-worker.ts` cache-first asset strategy with
|
||||
stale invalidation on app update
|
||||
- `ui/frontend/static/manifest.webmanifest` PWA manifest
|
||||
- `ui/frontend/static/icons/` web icon set sized per
|
||||
`manifest.webmanifest` requirements
|
||||
- topic doc `ui/docs/pwa-strategy.md` covering update flow and
|
||||
offline scope
|
||||
|
||||
Dependencies: Phase 25 (offline order queue).
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- the web app installs as a PWA on Chrome, Edge, and iOS Safari;
|
||||
- the service worker survives an app update without serving stale code
|
||||
on the next reload.
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- Lighthouse PWA audit at score ≥ 90;
|
||||
- Playwright test: install the app, take it offline, verify the cached
|
||||
login route still loads;
|
||||
- regression test: bumping the app version invalidates the prior
|
||||
service worker.
|
||||
|
||||
## Phase 34. Multi-Turn Projection — Realistic Planet Forecast
|
||||
|
||||
Status: pending. Long-term scope deferred but this phase ships real
|
||||
features.
|
||||
|
||||
Goal: ship a realistic multi-turn planet projection and surface it in
|
||||
the planet inspector and in the calculator's planet area. Reach circles
|
||||
already shipped in Phase 30 (auto-drawn from the calculator's selected
|
||||
planet); this phase no longer owns them.
|
||||
|
||||
The Phase 30 planet area is single-turn (MAT-only): it answers "ships
|
||||
this turn / turns per ship" at the current or overridden MAT. This phase
|
||||
makes it realistic and multi-turn by extracting the planet economy into
|
||||
`pkg/calc` and simulating turns: population growth (`×1.08`), material /
|
||||
capital / colonist supply, and the capital/colonist unpacking that
|
||||
mirrors `MakeTurn` steps 09/12/14/15. CAP and COL only affect future
|
||||
turns (post-production unpacking), so they become meaningful here and
|
||||
are added to the calculator's planet area as supply inputs alongside
|
||||
MAT.
|
||||
|
||||
Artifacts:
|
||||
|
||||
- `pkg/calc/` planet-economy extraction (single-sourced, engine
|
||||
delegates): `PlanetProduction`, `ProducePopulation`,
|
||||
`UnpackColonists`, `UnpackCapital`, reusing `ProduceShipsInTurn`; a
|
||||
multi-turn projector `ProjectPlanetBuild` answering "K ships in M
|
||||
turns" under guaranteed per-turn supply
|
||||
- thin bridges in `ui/core/calc/` + `Core` typings
|
||||
- planet inspector forecast section (next-turn population, industry,
|
||||
materials, production progress)
|
||||
- calculator planet area gains CAP and COL supply inputs and switches
|
||||
its readout to the multi-turn projector
|
||||
- topic doc `ui/docs/multi-turn-projection.md` (long-term vision:
|
||||
multi-turn planning mode, scenario branches)
|
||||
|
||||
Dependencies: Phases 17, 18, 30.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- projector output is byte-identical to running the engine's per-turn
|
||||
planet update over the same turns (Go parity);
|
||||
- the planet inspector shows a forecast section matching it;
|
||||
- the calculator planet area honours MAT / CAP / COL supply and shows
|
||||
"K ships in M turns" consistent with the projector.
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- Go parity tests for each extracted economy formula and the projector;
|
||||
- Vitest for the calculator planet area with supply inputs;
|
||||
- Playwright e2e: planet inspector forecast section.
|
||||
|
||||
## Phase 35. Polish — Accessibility, Localisation, Error UX
|
||||
|
||||
Status: pending.
|
||||
|
||||
Goal: prepare the client for technical beta with end-user-quality
|
||||
polish.
|
||||
|
||||
Artifacts:
|
||||
|
||||
- `ui/frontend/src/lib/i18n/` translation bundles for English and
|
||||
Russian, covering every visible string
|
||||
- `ui/frontend/src/lib/error/` central error surface with stable codes
|
||||
and retry / escalation guidance
|
||||
- accessibility audit results recorded under `ui/docs/a11y.md`
|
||||
- keyboard-only navigation paths for lobby, game view, and login
|
||||
- focus rings, ARIA labels, screen-reader-only text where needed
|
||||
- mobile bottom-sheet swipe-down dismissal and tap-outside dismissal,
|
||||
on top of the close button shipped in Phase 13
|
||||
- selected-planet visual on the map (ring or halo), wired off the
|
||||
Phase 13 `SelectionStore`
|
||||
|
||||
Dependencies: Phase 33.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- WCAG 2.2 AA compliance on lobby, login, and the in-game shell per
|
||||
axe-core scan;
|
||||
- the entire UI is reachable by keyboard only with visible focus
|
||||
rings;
|
||||
- every server-side error is mapped to a translated, actionable user
|
||||
message in both languages;
|
||||
- locale switch persists across reloads on every platform.
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- axe-core integration tests on every top-level view;
|
||||
- Vitest tests for the i18n bundle structure and missing-translation
|
||||
detection;
|
||||
- Playwright keyboard-only navigation tests.
|
||||
|
||||
## Phase 36. Acceptance Pass
|
||||
|
||||
Status: pending.
|
||||
|
||||
Goal: reconcile implementation, documentation, and regression coverage
|
||||
before declaring the client ready for technical beta.
|
||||
|
||||
Artifacts:
|
||||
|
||||
- updated `ui/README.md`, topic docs, and any drift in
|
||||
`docs/ARCHITECTURE.md` or `docs/FUNCTIONAL.md` (mirrored to
|
||||
`docs/FUNCTIONAL_ru.md`)
|
||||
- final cross-platform regression run on a release-candidate build
|
||||
- `ui/docs/release-checklist.md` for repeatable releases
|
||||
- visual regression baselines committed under
|
||||
`ui/frontend/tests/__snapshots__/`; if maintenance proves heavy,
|
||||
follow-up issue to switch to self-hosted Argos
|
||||
- Appium harness for iOS Simulator and Android Emulator covering the
|
||||
login flow, push-event flow, and at least one full turn loop;
|
||||
`.gitea/workflows/ui-release.yaml` extended with macOS-runner Appium
|
||||
job (mandatory pre-release gate)
|
||||
|
||||
Dependencies: Phases 1 through 35.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- implementation matches every documented contract and live topic
|
||||
doc;
|
||||
- the cross-cutting regression scenarios listed below pass on web,
|
||||
desktop, and mobile;
|
||||
- Appium smoke passes on both iOS and Android in CI.
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- run focused package tests for `ui/core` and every TypeScript
|
||||
module;
|
||||
- rerun cross-platform Playwright suites against release-candidate
|
||||
builds;
|
||||
- run Tier 2 visual regression baselines;
|
||||
- run Appium smoke suites on iOS and Android.
|
||||
|
||||
---
|
||||
|
||||
## Cross-Cutting Regression Scenarios
|
||||
|
||||
- A fresh device generates a keypair, completes email-code login, and
|
||||
successfully signs a follow-up authenticated request on every target
|
||||
platform.
|
||||
- A returning device resumes its session without re-login, preserves
|
||||
queued orders, and continues receiving push events without gaps.
|
||||
- Server-side session revocation tears down the active push stream and
|
||||
forces a re-login on every target platform within one second.
|
||||
- Tampering with `payload_bytes`, `payload_hash`, `request_id`,
|
||||
`message_type`, or any signature byte is rejected by the verifier
|
||||
in `ui/core` with a stable error code.
|
||||
- Requests outside the freshness window are rejected before they
|
||||
reach network, and the client surfaces a clock-skew warning when
|
||||
its local clock disagrees with the server time event by more than
|
||||
the freshness window.
|
||||
- The map renderer holds 60 fps with a 1000-primitive fixture on
|
||||
mid-range hardware on web (Chrome, Edge, Safari, Firefox), desktop
|
||||
(Wails on macOS, Windows, Linux), and mobile (latest iPhone, mid-
|
||||
range Android).
|
||||
- The single-tool sidebar preserves state across tab switches; the
|
||||
active view preserves state across view switches; designers
|
||||
preserve their in-progress state when navigating to the map and
|
||||
back through a transient overlay.
|
||||
- Order draft is preserved across page reloads, view switches, network
|
||||
drops, and history-mode entry / exit.
|
||||
- Orders queued offline are flushed in order on reconnect; a turn-
|
||||
cutoff conflict surfaces as a clearly failed-order banner without
|
||||
retrying forever.
|
||||
- History mode applies to every view; the order tab disappears in
|
||||
history mode and the prior draft is restored on return to the
|
||||
current turn.
|
||||
- The ship-class designer's calculations match `pkg/calc/` byte-for-
|
||||
byte; any drift between client mirror and server fails CI.
|
||||
- Linux desktop builds without Secret Service still complete login by
|
||||
falling back to the `0600` file under `~/.config/galaxy/`.
|
||||
- The web service worker invalidates correctly on app update and
|
||||
never serves stale code on the first load after a deploy.
|
||||
- Push-event signature verification is mandatory; any verification
|
||||
failure tears down the stream and reconnects with backoff.
|
||||
- Locale switch persists across reloads and applies to every visible
|
||||
string on every platform.
|
||||
|
||||
## TODO — deferred follow-ups from Phases 1-5
|
||||
|
||||
These items are explicit decisions to defer, not unknown work. Each
|
||||
should be picked up either as a follow-up patch or folded into the
|
||||
phase listed in the parenthesis when that phase lands.
|
||||
|
||||
- **Build `core.wasm` in CI, drop the committed artefacts** — install
|
||||
TinyGo on the Gitea Actions runner (`brew install tinygo` is not
|
||||
available on Linux runners, so use the official tarball or
|
||||
`curl … | tar -xz` step), add `make -C ui wasm` ahead of the Vitest
|
||||
step in `.gitea/workflows/ui-test.yaml`, then remove
|
||||
`ui/frontend/static/core.wasm` and `ui/frontend/static/wasm_exec.js`
|
||||
from the repo and re-tighten `ui/.gitignore`. Phase 5 committed the
|
||||
binaries only as a stop-gap so contributors did not have to install
|
||||
TinyGo. (Phase 5 cleanup, blocks before Phase 33 PWA.)
|
||||
- **Restore `js.CopyBytesToGo` when TinyGo fixes the
|
||||
`instanceof Uint8Array` check** — the per-element loop in
|
||||
`ui/wasm/main.go::copyBytesFromJS` is a workaround for TinyGo 0.41
|
||||
panicking on Uint8Arrays whose prototype chain crosses Node's
|
||||
`Buffer`. Track upstream
|
||||
(<https://github.com/tinygo-org/tinygo/issues>) and revert the
|
||||
helper once a release is pinned. (Phase 5 follow-up.)
|
||||
- **Migrate TS codegen to Connect-ES v2 BSR plugin once published** —
|
||||
`ui/buf.gen.yaml` runs `protoc-gen-es` v2 locally because
|
||||
`buf.build/connectrpc/es` is still on v1.6.1 and emits
|
||||
v1-incompatible imports. When the v2 plugin lands on the BSR, we
|
||||
can either keep the local plugin (no network dep) or move back to
|
||||
the remote, depending on whether buf.build rate limits are hit in
|
||||
CI. (Phase 5 follow-up; revisit when next regenerating.)
|
||||
- **Rename `gateway/internal/grpcapi/` → `gateway/internal/connectapi/`**
|
||||
— the package now hosts a Connect-Go listener that natively serves
|
||||
Connect, gRPC, and gRPC-Web; the `grpcapi` name is historical.
|
||||
Touches imports in `gateway/cmd/gateway/main.go` and a couple of
|
||||
cross-package refs. Pure rename, no behaviour change. (Phase 4
|
||||
cleanup; do alongside the next gateway change.)
|
||||
- **Rename `GATEWAY_AUTHENTICATED_GRPC_*` env vars to drop the `GRPC`
|
||||
infix** — they label the authenticated-edge tier, not the wire
|
||||
protocol. Affects `gateway/internal/config/`, the integration
|
||||
testenv defaults in `integration/testenv/gateway.go`, the README,
|
||||
and the runbook. Coordinated with the package rename above.
|
||||
(Phase 4 cleanup; not before the env vars are referenced by
|
||||
external operators.)
|
||||
- **Add a Docker-stack integration test for Connect end-to-end** —
|
||||
Phase 4 closed with service-level Connect tests only. Once a phase
|
||||
already brings up the full stack (Phase 7 onward, since auth flow
|
||||
needs backend), drop a `integration/connect_call_test.go` that
|
||||
exercises a unary Connect call and a server-streaming Connect call
|
||||
through `testenv.Bootstrap`. (Phase 7+, fold into the phase that
|
||||
needs it.)
|
||||
- **Battle viewer — push event `game.battle.new`** — when a battle
|
||||
involving the current player lands, emit a backend notification
|
||||
intent (idempotency `battle-new:<game_id>:<turn>:<battle_id>`,
|
||||
payload `{game_id, turn, battle_id}`) so the in-game shell
|
||||
surfaces a toast with a deep link into the Battle Viewer.
|
||||
(Phase 27 deferred; needs an engine emit-side change.)
|
||||
- **Battle viewer — richer ship-class visuals** — current MVP draws
|
||||
one small circle plus `<class>:<numLeft>` label per `(race,
|
||||
className)` pair. Future work derives shape / scale from mass,
|
||||
armament, shields, and the number of ships in the group.
|
||||
(Phase 27 deferred.)
|
||||
- **Battle viewer — animated re-distribution on elimination** —
|
||||
current implementation hard-jumps to the new spacing on the next
|
||||
frame; replace with an easing so the survivors visibly slide
|
||||
along the outer ring. (Phase 27 deferred.)
|
||||
|
||||
Reference in New Issue
Block a user