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:
+29
-28
@@ -15,8 +15,9 @@ namespace.
|
||||
This topic doc covers the web implementation only. The platform-
|
||||
agnostic `KeyStore` and `Cache` interfaces in
|
||||
`src/platform/store/index.ts` are what the rest of the client codes
|
||||
against; later phases bring `WailsStore` and `CapacitorStore` adapters
|
||||
that satisfy the same contracts.
|
||||
against; `WailsStore` and `CapacitorStore` adapters will satisfy the
|
||||
same contracts on their respective platforms (see
|
||||
[../ROADMAP.md](../ROADMAP.md)).
|
||||
|
||||
Source-of-truth for the cross-service contract is
|
||||
[`../../docs/ARCHITECTURE.md` §15 "Transport security model"](../../docs/ARCHITECTURE.md);
|
||||
@@ -38,13 +39,12 @@ recently:
|
||||
|
||||
Browsers older than the baseline above will fail at the first
|
||||
`generateKey({ name: 'Ed25519' }, ...)` call with a
|
||||
`NotSupportedError`. Phase 6 deliberately does not ship a JavaScript
|
||||
fallback (e.g. `@noble/ed25519`) — keeping the keystore on WebCrypto
|
||||
is what gives us non-extractable storage on every supported engine.
|
||||
The Phase 7 root layout runs a one-time probe on boot and switches
|
||||
to a "browser not supported" page (described in
|
||||
[`auth-flow.md`](auth-flow.md)) when the probe rejects, instead of
|
||||
attempting the keystore generate.
|
||||
`NotSupportedError`. No JavaScript fallback (e.g. `@noble/ed25519`)
|
||||
is shipped — keeping the keystore on WebCrypto is what gives
|
||||
non-extractable storage on every supported engine. The root layout
|
||||
runs a one-time probe on boot and switches to a "browser not
|
||||
supported" page (described in [`auth-flow.md`](auth-flow.md)) when
|
||||
the probe rejects, instead of attempting the keystore generate.
|
||||
|
||||
### WebKit non-determinism note
|
||||
|
||||
@@ -60,8 +60,9 @@ is missing.
|
||||
|
||||
Tests that assert "the same key signs the same message identically"
|
||||
must either pin to the Vitest path (Node WebCrypto) or be replaced
|
||||
with verify-after-sign assertions. The Phase 6 Playwright spec uses
|
||||
the verify path, which works on every engine in the baseline.
|
||||
with verify-after-sign assertions. The Playwright spec for the
|
||||
keystore uses the verify path, which works on every engine in the
|
||||
baseline.
|
||||
|
||||
## IndexedDB schema
|
||||
|
||||
@@ -112,14 +113,14 @@ wipes every namespace.
|
||||
|
||||
Namespaces in current use:
|
||||
|
||||
| Namespace | Key | Value type | Owner |
|
||||
|--------------------|--------------------------------|-----------------------------------------------|------------------------------------|
|
||||
| `session` | `device-session-id` | `string` | Phase 7+ |
|
||||
| `game-prefs` | `{gameId}/wrap-mode` | `WrapMode` | Phase 11+ (`game-state.md`) |
|
||||
| `game-prefs` | `{gameId}/last-viewed-turn` | `number` | Phase 11+ (`game-state.md`) |
|
||||
| `order-drafts` | `{gameId}/draft` | `OrderCommand[]` | Phase 12+ (`order-composer.md`) |
|
||||
| `game-history` | `{gameId}/turn/{N}` | `GameReport` | Phase 26+ (`game-state.md`) |
|
||||
| `game-map-toggles` | `{gameId}` | `{toggles: MapToggles; lastResetTurn: number}` | Phase 29+ (`game-state.md`) |
|
||||
| Namespace | Key | Value type | Owner |
|
||||
|--------------------|--------------------------------|------------------------------------------------|--------------------------|
|
||||
| `session` | `device-session-id` | `string` | `auth-flow.md` |
|
||||
| `game-prefs` | `{gameId}/wrap-mode` | `WrapMode` | `game-state.md` |
|
||||
| `game-prefs` | `{gameId}/last-viewed-turn` | `number` | `game-state.md` |
|
||||
| `order-drafts` | `{gameId}/draft` | `OrderCommand[]` | `order-composer.md` |
|
||||
| `game-history` | `{gameId}/turn/{N}` | `GameReport` | `game-state.md` |
|
||||
| `game-map-toggles` | `{gameId}` | `{toggles: MapToggles; lastResetTurn: number}` | `game-state.md` |
|
||||
|
||||
The `game-map-toggles` blob stores the gear popover's per-game
|
||||
visibility state plus a `lastResetTurn` companion field. Reading
|
||||
@@ -131,10 +132,10 @@ whenever `lastResetTurn < currentTurn`, so a fresh server turn
|
||||
always greets the player with every map category visible (see
|
||||
`game-state.md` for the new-turn-reset contract).
|
||||
|
||||
Later phases will add more per-feature namespaces (fixtures, lobby
|
||||
snapshot, etc.). The contract is namespace-strings stay scoped to
|
||||
one feature; cross-feature reads through the cache are by convention
|
||||
disallowed.
|
||||
Additional per-feature namespaces will be added as needed (fixtures,
|
||||
lobby snapshot, etc.). The contract is namespace-strings stay scoped
|
||||
to one feature; cross-feature reads through the cache are by
|
||||
convention disallowed.
|
||||
|
||||
## Keystore lifecycle
|
||||
|
||||
@@ -176,9 +177,8 @@ Thin orchestration layer over `KeyStore` + `Cache`:
|
||||
- `loadDeviceSession(keyStore, cache)` returns
|
||||
`{ keypair, deviceSessionId }`. The `keypair` field is always
|
||||
populated (loaded if present, freshly generated if not). The
|
||||
`deviceSessionId` field is `null` until Phase 7's
|
||||
`confirm-email-code` handler stores the gateway-issued id via
|
||||
`setDeviceSessionId`.
|
||||
`deviceSessionId` field is `null` until the `confirm-email-code`
|
||||
handler stores the gateway-issued id via `setDeviceSessionId`.
|
||||
- `setDeviceSessionId(cache, id)` writes the id to the `session`
|
||||
namespace.
|
||||
- `clearDeviceSession(keyStore, cache)` wipes both the keypair and
|
||||
@@ -186,7 +186,7 @@ Thin orchestration layer over `KeyStore` + `Cache`:
|
||||
push-event-driven revocation path.
|
||||
|
||||
A `null` `deviceSessionId` is the signal that the session is
|
||||
unauthenticated — Phase 7 routes such users to `/login`.
|
||||
unauthenticated — the root layout routes such users to `/login`.
|
||||
|
||||
## Test layout
|
||||
|
||||
@@ -211,7 +211,8 @@ the debug entry point never attaches `window.__galaxyDebug`.
|
||||
|
||||
## Future: native targets
|
||||
|
||||
Phase 31 (Wails) and Phase 32 (Capacitor) bring native keystores —
|
||||
Native desktop and mobile targets (planned in
|
||||
[../ROADMAP.md](../ROADMAP.md)) will bring native keystores —
|
||||
Keychain on macOS / iOS, DPAPI/Credential Locker on Windows,
|
||||
libsecret on Linux, Android Keystore on Android — behind the same
|
||||
`KeyStore` interface, plus SQLite-backed `Cache` adapters. The web
|
||||
|
||||
Reference in New Issue
Block a user