Skip to content

Commit 3c01888

Browse files
authored
Merge pull request #405 from Chia-Network/improve-notifications
Improve session banner, unroll status, and on-chain timeout claims
2 parents 761c00e + 96964b4 commit 3c01888

46 files changed

Lines changed: 1560 additions & 844 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONNECTIVITY.md

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -487,10 +487,10 @@ The hub does not create a session. It can only advise and relay:
487487

488488
- **Peer degradation (no auto-cascade)**: When the peer becomes unreachable
489489
(delivery failures, 30-second silence, or hub disconnect) while the
490-
session is off-chain, `peerLiveness` moves to `'degraded'` (yellow dot).
491-
There is no automatic go-on-chain — the user must decide to escalate.
492-
Only explicit terminal signals (user clicks "Go On-Chain" or receives a
493-
FOAD) mark the peer as dead.
490+
session is off-chain, `peerLiveness` moves to `'degraded'` (yellow banner
491+
rail; tab stays a link). There is no automatic go-on-chain — the user must
492+
decide to escalate. Only explicit terminal signals (user clicks "Go
493+
On-Chain" or receives a FOAD) mark the peer as dead.
494494

495495
- **Cascade warning dialogs**: Confirmation dialogs currently warn before
496496
disconnecting or switching hubs when a peer/session would be affected.
@@ -500,41 +500,48 @@ The hub does not create a session. It can only advise and relay:
500500

501501
## UX: Connectivity Indicators
502502

503-
### Tab dots
503+
### Tab pipe marks
504504

505-
Each tab in the tab bar has a small colored dot to the left of its label
506-
text, indicating the connectivity health of the axis associated with that
507-
tab. The dot is always present (gray when idle/irrelevant) so the tab bar
508-
layout never shifts.
505+
Wallet, Hub, and Game tabs show an uncolored link (connected) or broken-chain
506+
(disconnected) emoji to the left of the label. History and Log have no pipe
507+
mark. The existing upper-right notification dots indicate unread activity and
508+
are unchanged.
509509

510-
Separately, the existing upper-right notification dots indicate unread
511-
activity (new game events, etc.). These are unchanged and serve a different
512-
purpose.
510+
Pipe marks answer only “is this pipe up?” Session mode lives on the game
511+
dashboard banner rail, not on the tabs.
513512

514-
### Per-tab color semantics
513+
| Tab | Link | Broken chain |
514+
|-----|------|----------------|
515+
| Wallet | Connected. | Disconnected. The **Wallet** label is also red. |
516+
| Hub | `hubLiveness === 'connected'` | Reconnecting, inactive, disconnected, or never connected |
517+
| Game | Live session and peer is not `dead` | `sessionPhase` none/resolved, or `peerLiveness === 'dead'` |
515518

516-
| Tab | Green | Yellow | Red | Gray |
517-
|-----|-------|--------|-----|------|
518-
| Wallet | Connected || Disconnected ||
519-
| Hub | Connected | Reconnecting | Inactive (no heartbeat) | Not connected (null / disconnected) |
520-
| Game | Peer connected (incl. clean shutdown) | On-chain, peer degraded, or peer unreachable during clean shutdown | Error, or peer dead outside clean shutdown | No session / resolved |
521-
| History |||| Always gray |
522-
| Log |||| Always gray |
519+
Handshake with `peerLiveness === null` counts as connected. `degraded` pings
520+
stay a link; that warning is banner-only. `sessionError` does not affect the
521+
tab mark.
523522

524-
### Game tab dot priority
523+
### Game dashboard banner rail
525524

526-
The Game tab dot checks conditions in this order:
525+
The session dashboard has a full-height left-edge color rail:
527526

528-
1. `sessionPhase === 'none' || 'resolved'`**gray** (no active session)
529-
2. `sessionError`**red** (genuine error — always wins)
530-
3. Clean shutdown in progress (`ShuttingDown` / `ShutdownTransactionPending` /
531-
`cleanShutdownStarted`):
532-
- peer degraded (or unexpectedly dead) → **yellow** (unreachable)
533-
- otherwise → **green** (cooperative close in flight; keepalives continue)
534-
4. `peerLiveness === 'dead'`**red** (terminal — go-on-chain or FOAD)
535-
5. `sessionPhase === 'on-chain'` or `peerLiveness === 'degraded'`**yellow** (resolving or stale peer)
536-
6. `peerLiveness === 'connected'`**green** (playing normally)
537-
7. Otherwise → **gray**
527+
| Tone | Color | When |
528+
|------|-------|------|
529+
| `idle` | Gray | No session / never set up |
530+
| `playing` | Green | Setup, handshake, off-chain play, cooperative shutdown |
531+
| `pings-bad` | Yellow | Same as playing, but `peerLiveness === 'degraded'` |
532+
| `on-chain` | Red | Going on-chain, unrolling, or a resolved unroll that still has games |
533+
| `ended` | Blue | Terminal dashboard still showing (clean resolve, failed, abandoned) |
534+
535+
On-chain beats yellow. Failed/stale outcomes that are actually over stay
536+
`ended`; the Channel label still names the outcome. Yellow also shows
537+
“Peer pings look stuck.”
538+
539+
### Game tab connectedness
540+
541+
`selectGameTabConnected` is true unless:
542+
543+
1. `sessionPhase === 'none' || 'resolved'`, or
544+
2. `peerLiveness === 'dead'`
538545

539546
Clean shutdown does **not** mark the peer dead on its own. Keepalives and the
540547
small allowlist of shutdown-related peer messages continue until local
@@ -543,17 +550,14 @@ shutdown completes. Successful/terminal session exit does not send
543550
does arrive during pre-active matchmaking, it is honored as an abort: cancel
544551
the attempt (including any in-flight async session start), surface
545552
cancelled/error, and do not leave an orphan handshake. When the channel
546-
reaches a terminal state the session exits and the dot goes gray.
553+
reaches a terminal state the session exits and the game tab shows a broken
554+
chain.
547555

548-
### Game tab error conditions (red dot)
556+
### Session error conditions
549557

550-
The Game tab shows a red dot when `sessionError` is true, or when
551-
`peerLiveness === 'dead'` outside a clean shutdown (go-on-chain or FOAD).
552558
`sessionError` is derived from:
553559

554560
- `Failed` channel state — the channel encountered an unrecoverable error
555-
- `ResolvedStale` channel state — the channel resolved but the outcome is
556-
suspect (e.g., opponent exploited a timeout)
557561
- `ResolvedStale` channel state — the channel resolved but the outcome is
558562
suspect (e.g., opponent exploited a timeout)
559563
- `game-error` game terminal — a generic game-level error (`GameStatus` with
@@ -564,7 +568,8 @@ The Game tab shows a red dot when `sessionError` is true, or when
564568

565569
Normal settlements such as `accept_settlement`, `settled_cleanly`,
566570
`opponent_timed_out`, `we_accepted`, and `slashed_opponent` are **not** session
567-
errors.
571+
errors. These conditions do not change the tab pipe mark; terminal outcomes
572+
use the `ended` banner rail.
568573

569574
### Settlement labels
570575

FRONTEND_ARCHITECTURE.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -415,11 +415,11 @@ resumable-session marker, and tab/reset coordination keys, inside the same-origi
415415
trust model described above.
416416

417417
The current and only legal envelope schema is `chia-gaming-session` version
418-
`12`. Because the project is
418+
`13`. Because the project is
419419
still alpha, every other version is deleted wholesale without decoding or
420-
migration. A decoded v12 record must also satisfy the complete phase-owned
420+
migration. A decoded v13 record must also satisfy the complete phase-owned
421421
envelope contract (keyed game membership, game-owned payload/type agreement,
422-
terminal data, and frozen terminal coin list); malformed v12 records are
422+
terminal data, and frozen terminal coin list); malformed v13 records are
423423
deleted rather than partially restored. The boot marker is retained after an
424424
incompatible or malformed resumable record is discarded so the failure remains
425425
visible at the Resume / Start Over boundary. The `version` field is kept as a
@@ -443,7 +443,7 @@ are grouped under those phase-owned payloads:
443443

444444
| Field | Type | Purpose |
445445
| ------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
446-
| `version` | `bigint` | Save schema version; currently `12`. |
446+
| `version` | `bigint` | Save schema version; currently `13`. |
447447
| `playerId` | `string` | Stable local hub/player identity for this browser state. |
448448
| `sessionId` | `string?` | Stable token linking the hub iframe and game-channel WebSocket. |
449449
| `alias` | `string?` | Local hub display alias preference. |
@@ -567,7 +567,7 @@ independently, and the accepted entry is removed only after the hand is fully
567567
settled. Schema version 12 also makes
568568
`gameInstances` plus `lastDisplayedGameId` the only persisted game protocol
569569
presentation and stores the canonical `GameProtocolPresentation` discriminant.
570-
Under the alpha no-migration policy, version 11 and all other incompatible
570+
Under the alpha no-migration policy, version 12 and all other incompatible
571571
records are deleted rather than translated from aggregate current-game fields.
572572

573573
#### Delivery-critical saves
@@ -919,8 +919,9 @@ delivery, ack reception, and keepalive reception.
919919

920920
Peer liveness is measured passively from relay traffic. The `PeerSession` object
921921
derives liveness indicators using a 5-second polling interval. These feed into
922-
the **tab-dot connectivity indicators** — colored dots to the left of each tab
923-
label showing connection health (green / yellow / red / gray). They are also
922+
the **tab pipe marks** — uncolored link / broken-chain emojis to the left of
923+
Wallet, Hub, and Game tab labels — and into the game dashboard **banner rail**
924+
(session mode: idle / playing / pings-bad / on-chain / ended). They are also
924925
passed to `GameSession` for in-game display. Separately, Shell has a cascade
925926
rule: if the peer is marked lost while the session is still off-chain, it calls
926927
`goOnChain()` on the WASM cradle.
@@ -948,12 +949,12 @@ Connected, keepalive timeout while WS is up → Inactive.
948949

949950
**Peer indicator** (`PeerLiveness`) has four states:
950951

951-
| State | Meaning | Dot color |
952-
| ----------- | ----------------------------------------------------------------------------------- | --------- |
953-
| `connected` | Peer traffic received within the last 30 seconds | Green |
954-
| `degraded` | Delivery failure reported by hub, or no peer traffic for 30+ seconds | Yellow |
955-
| `dead` | Local go-on-chain or session rejection (FOAD) — terminal for this peer relationship | Red |
956-
| `null` | No active peer session | Grey |
952+
| State | Meaning | Tab mark |
953+
| ----------- | ----------------------------------------------------------------------------------- | -------- |
954+
| `connected` | Peer traffic received within the last 30 seconds | Link |
955+
| `degraded` | Delivery failure reported by hub, or no peer traffic for 30+ seconds | Link (banner rail yellow) |
956+
| `dead` | Local go-on-chain or session rejection (FOAD) — terminal for this peer relationship | Broken chain |
957+
| `null` | No keepalive yet, or no active peer session | Link if a session is live (handshake); broken chain if none/resolved |
957958

958959
`dead` is sticky: incoming messages from that peer are ignored. Only a new session start resets to `null`.
959960

@@ -1588,16 +1589,16 @@ not to limit concurrency.
15881589
| `front-end/src/components/GameSession.tsx` | Game session UI: header, coin status, game area, overlays |
15891590
| `front-end/src/hooks/useGameSession.ts` | Thin React boundary: controller/runtime setup, host subscription, typed dispatch, selector projection |
15901591
| `front-end/src/lib/session/sessionMachine*.ts` | Root dispatcher plus cohesive channel, between-hand, proposal, durable-game, notification, command, effect, runtime, and persistence modules |
1591-
| `front-end/src/lib/session/persistence*.ts` | Canonical strict-v12 phase decoder plus primitive, between-hand/proposal, and phase-payload codecs; accepted records always produce a normalized `SessionModel` |
1592-
| `front-end/src/lib/session/sessionSnapshot.ts` | Canonical `SessionModel`v12 presentation snapshot encoder |
1592+
| `front-end/src/lib/session/persistence*.ts` | Canonical strict-v13 phase decoder plus primitive, between-hand/proposal, and phase-payload codecs; accepted records always produce a normalized `SessionModel` |
1593+
| `front-end/src/lib/session/sessionSnapshot.ts` | Canonical `SessionModel`v13 presentation snapshot encoder |
15931594
| `front-end/src/lib/gameRegistry.ts` | Exhaustive pure feature registration and game-owned codec/terms/compose dispatch |
15941595
| `front-end/src/lib/gameMountRegistry.tsx` | Exhaustive React live/frozen mount registration |
15951596
| `front-end/src/features/calPoker/useCalpokerHand.ts` | Calpoker hook: five-step protocol, card parsing, move submission |
15961597
| `front-end/src/hooks/SessionController.ts` | WASM bridge (`SessionController` class): message delivery, block data, event queue, `getWasmFields()` for persistence |
15971598
| `front-end/src/hooks/WasmStateInit.ts` | WASM initialization: load binary, deposit .hex files, create cradle |
15981599
| `front-end/src/hooks/blobSingleton.ts` | Singleton management: `getOrCreateSessionController` / `destroySessionController`; restore path for session persistence |
15991600
| `front-end/src/services/PeerSession.ts` | Per-session peer state: session ID, peer ID, liveness, message buffering/routing, send methods |
1600-
| `front-end/src/hooks/save.ts` | v12 cache/write and live/terminal lifecycle facade |
1601+
| `front-end/src/hooks/save.ts` | v13 cache/write and live/terminal lifecycle facade |
16011602
| `front-end/src/hooks/saveCoordination.ts` | Resume markers, active-tab lease, and cross-tab persistence fencing |
16021603
| `front-end/src/hooks/saveHardReset.ts` | Hard-reset and WalletConnect browser-storage cleanup |
16031604
| `front-end/src/hooks/savePreferences.ts` | Local preference encoding and decoding |

ON_CHAIN.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,11 @@ unroll is extracted from the on-chain conditions.
6464

6565
### Step 2: Preempt or Wait
6666

67-
The player compares the on-chain unroll state number against their own latest
68-
state to decide whether to preempt or wait for the timeout path (see
69-
[Preemption](#preemption)). Both outcomes produce the same result: the unroll
70-
coin is spent, creating game coins and reward coins.
67+
`channel_coin_spent` classifies the parsed channel-coin spend (see
68+
[Preemption](#preemption)). Timeout and preempt both spend the unroll coin,
69+
creating game coins and reward coins. A spend we never signed (unknown puzzle
70+
hash) or whose conditions do not match the signed historical record is an
71+
error at parse time — it is not turned into a timeout.
7172

7273
When the unroll coin spend is detected, a `ChannelStatus` notification with
7374
state `ResolvedUnrolled` (or `ResolvedStale` if the unroll was stale) is
@@ -347,12 +348,12 @@ a player sees the channel coin being spent to an unroll coin, they compare the
347348
on-chain sequence number against their own latest state:
348349

349350

350-
| On-chain SN vs ours | Action | Explanation |
351-
| ------------------- | ----------------------- | ----------------------------------------------------------------------------------- |
352-
| On-chain < ours, opposite parity | **Preempt** (immediate) | Spend the unroll coin immediately with our higher SN and more up-to-date conditions |
353-
| On-chain < ours, same parity | **Wait for timeout** | The parity rule forbids our latest state from preempting this coin; use its compact historical timeout record |
354-
| On-chain == ours | **Wait for timeout** | The unroll is at the state we expect; wait for it to resolve |
355-
| On-chain > ours | **Error** | We've been hacked or something went very wrong |
351+
| On-chain SN vs ours | Action | Explanation |
352+
| ------------------- | ------ | ----------- |
353+
| On-chain < ours, opposite parity, signed preemption source | **Preempt** (immediate) | Spend the unroll coin immediately with our higher SN and more up-to-date conditions |
354+
| On-chain < ours, same parity (or no signed preemption source) | **Wait for timeout** | The parity rule forbids preemption; use the compact historical timeout record we signed |
355+
| On-chain == ours | **Wait for timeout** | The unroll is at the state we expect; wait for it to resolve |
356+
| Unknown puzzle hash or conditions-hash mismatch | **Error** | Never signed, or not the unroll we signed — classified when the channel coin spend is parsed |
356357

357358

358359
Preemption is **immediate** — no timelock. This is by design: the preempting

0 commit comments

Comments
 (0)