R4: push enrichment — events carry a state delta, kill the last poll
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 37s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 37s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
Enrich the in-app live stream into a delta channel so the UI renders a move from the event without a follow-up game.state, and make the matchmaking poll a stream-down fallback. - pkg/fbs: trailing fields on opponent_moved (move+game+bag_len), your_turn (move_count), match_found (state), game_over (game), notify (account/invitation/state), MoveResult (rack+bag_len); regenerate Go + TS. - backend: notify owns the FB encoding (encode.go + payload.go input structs); game/lobby/social map their domain types in. emitMove builds the move delta; game.Service.InitialState feeds match_found/game_started the recipient's initial StateView; friends/invitations notify carry their account/invitation. The move-commit response (submit_play/pass/exchange/resign) returns the actor's refilled rack + bag size. - gateway: MoveResult transcode carries rack+bag_len. - ui: pure lib/gamedelta.ts reducer advances the per-game cache keyed on move_count (idempotent + gap-safe); app.svelte seeds the cache on match_found/game_started; Game.svelte applies the delta (commit/pass/exchange/resign drop their load()); NewGame polls only while app.streamAlive is false. - docs: ARCHITECTURE §10, FUNCTIONAL(+ru), backend/gateway/ui READMEs; PRERELEASE R4 marked done + Refinements.
This commit is contained in:
+37
-1
@@ -20,7 +20,7 @@ the edge before prod. Each phase maps back to the owner's raw pre-release TODO l
|
||||
| R1 | Schema & naming reset | 1 + 10 | **done** |
|
||||
| R2 | Stress harness + contour observability + early run | 9a | **done** |
|
||||
| R3 | Edge hardening | 2 + 8 + 3 | **done** |
|
||||
| R4 | Push enrichment + kill the last poll | 4 + 5 | todo |
|
||||
| R4 | Push enrichment + kill the last poll | 4 + 5 | **done** |
|
||||
| R5 | Bundle slimming | 6 | todo |
|
||||
| R6 | Refactor + docs reconciliation + de-staging | 7 | todo |
|
||||
| R7 | Final stress run + tuning | 9b | todo |
|
||||
@@ -281,3 +281,39 @@ Then Stage 18.
|
||||
queue) and the flag badge / clear action on the user list / card.
|
||||
- The jet regen also restored the previously missing `game_drafts`/`game_hidden` generated models
|
||||
(their tables were added after the last jetgen run; no behaviour change).
|
||||
|
||||
- **R4** (interview + implementation):
|
||||
- **Locked decisions:** **delta-first**, not full snapshots — an event carries only the new move and
|
||||
the UI applies it to its per-game cache, keyed on `move_count` (idempotent + gap-safe: a gap or the
|
||||
actor's own move falls back to a `game.state` + `game.history` refetch). `match_found` /
|
||||
`game_started` carry the recipient's **initial `StateView`** (instant lobby→game); the fallback
|
||||
refetch stays the existing two calls (no merged endpoint); the matchmaking poll runs **only while
|
||||
the stream is down** (2.5 s); **all** UI-state-changing events carry their payload (incl. lobby `notify`).
|
||||
- **Enriched events** (`pkg/fbs` trailing fields — backward-compatible, no FB regen of *values*, only
|
||||
the schema): `opponent_moved` (+`move`/`game`/`bag_len`), `your_turn` (+`move_count`), `match_found`
|
||||
(+`state`), `game_over` (+`game`), `notify` (+`account`/`invitation`/`state`). The pre-R4
|
||||
`opponent_moved` scalars (`seat`/`action`/`score`/`total`) stay for wire back-compat, now redundant
|
||||
with `move`/`game` — slated for the R6 de-stage.
|
||||
- **Encoding placement:** the `notify` package keeps ownership of the FlatBuffers encoding (a new
|
||||
`encode.go` mirrors the gateway transcode but reads wire-agnostic `notify.*` input structs +
|
||||
`engine.MoveRecord`); the game/lobby/social services map their domain types to those structs, so the
|
||||
wire schema stays out of the domain. **Flagged for R6:** this partly duplicates the gateway encoders
|
||||
(different source types) — a candidate consolidation.
|
||||
- **Actor self-fetch killed too** (beyond literal "push"): the `submit_play`/`pass`/`exchange`/`resign`
|
||||
**response** (`MoveResult`) now returns the actor's refilled rack + bag size, so the mover renders the
|
||||
next turn from the response — `Game.svelte`'s `commit`/`pass`/`exchange`/`resign` drop their `await load()`.
|
||||
- **`match_found` enrichment** needs a per-seat initial state: `lobby.GameCreator` gained `InitialState`,
|
||||
and `game.Service.InitialState` builds the `notify.PlayerState` (rack re-encoded to wire indices, the
|
||||
variant alphabet embedded for a first-seen variant).
|
||||
- **UI:** a pure `lib/gamedelta.ts` reducer (`applyMoveDelta` / `applyGameOver` / `seedInitialState`,
|
||||
unit-tested) advances the cache; `app.svelte` seeds it on `match_found` / `game_started`; `Game.svelte`
|
||||
applies the delta (falling back to `load()` while composing, on a gap, or on its own move's new rack);
|
||||
`NewGame.svelte` polls only when `app.streamAlive` is false and guards its teardown so a push-delivered
|
||||
match is not cancelled.
|
||||
- **notify (friends/invitations) scope:** the backend carries the full account / invitation payload on the
|
||||
wire (per "all events → push"); the UI seeds the game cache from `game_started` but keeps its lightweight
|
||||
**authoritative** badge refresh (`refreshNotifications`, on the rare `notify` event + on foreground) rather
|
||||
than adding client-side friend/invitation caches — the per-move hot path is fully de-fetched, which was the
|
||||
goal. Deeper lobby-cache consumption is an easy follow-up.
|
||||
- **No schema change** (no migration); the contour needs no DB wipe. Tests: `notify` FB round-trips +
|
||||
`emitMove` delta + the `gamedelta` reducer; the e2e mock now emits the enriched delta.
|
||||
|
||||
Reference in New Issue
Block a user