Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 44 additions & 39 deletions CONNECTIVITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,10 +472,10 @@ The hub does not create a session. It can only advise and relay:

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

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

## UX: Connectivity Indicators

### Tab dots
### Tab pipe marks

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

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

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

| Tab | Green | Yellow | Red | Gray |
|-----|-------|--------|-----|------|
| Wallet | Connected | — | Disconnected | — |
| Hub | Connected | Reconnecting | Inactive (no heartbeat) | Not connected (null / disconnected) |
| 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 |
| History | — | — | — | Always gray |
| Log | — | — | — | Always gray |
Handshake with `peerLiveness === null` counts as connected. `degraded` pings
stay a link; that warning is banner-only. `sessionError` does not affect the
tab mark.

### Game tab dot priority
### Game dashboard banner rail

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

1. `sessionPhase === 'none' || 'resolved'` → **gray** (no active session)
2. `sessionError` → **red** (genuine error — always wins)
3. Clean shutdown in progress (`ShuttingDown` / `ShutdownTransactionPending` /
`cleanShutdownStarted`):
- peer degraded (or unexpectedly dead) → **yellow** (unreachable)
- otherwise → **green** (cooperative close in flight; keepalives continue)
4. `peerLiveness === 'dead'` → **red** (terminal — go-on-chain or FOAD)
5. `sessionPhase === 'on-chain'` or `peerLiveness === 'degraded'` → **yellow** (resolving or stale peer)
6. `peerLiveness === 'connected'` → **green** (playing normally)
7. Otherwise → **gray**
| Tone | Color | When |
|------|-------|------|
| `idle` | Gray | No session / never set up |
| `playing` | Green | Setup, handshake, off-chain play, cooperative shutdown |
| `pings-bad` | Yellow | Same as playing, but `peerLiveness === 'degraded'` |
| `on-chain` | Red | Going on-chain, unrolling, or a resolved unroll that still has games |
| `ended` | Blue | Terminal dashboard still showing (clean resolve, failed, abandoned) |

On-chain beats yellow. Failed/stale outcomes that are actually over stay
`ended`; the Channel label still names the outcome. Yellow also shows
“Peer pings look stuck.”

### Game tab connectedness

`selectGameTabConnected` is true unless:

1. `sessionPhase === 'none' || 'resolved'`, or
2. `peerLiveness === 'dead'`

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

### Game tab error conditions (red dot)
### Session error conditions

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

- `Failed` channel state — the channel encountered an unrecoverable error
- `ResolvedStale` channel state — the channel resolved but the outcome is
suspect (e.g., opponent exploited a timeout)
- `ResolvedStale` channel state — the channel resolved but the outcome is
suspect (e.g., opponent exploited a timeout)
- `game-error` game terminal — a generic game-level error (`GameStatus` with
Expand All @@ -549,7 +553,8 @@ The Game tab shows a red dot when `sessionError` is true, or when

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

### Settlement labels

Expand Down
33 changes: 17 additions & 16 deletions FRONTEND_ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,11 @@ resumable-session marker, and tab/reset coordination keys, inside the same-origi
trust model described above.

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

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

#### Delivery-critical saves
Expand Down Expand Up @@ -902,8 +902,9 @@ delivery, ack reception, and keepalive reception.

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

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

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

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

Expand Down Expand Up @@ -1571,16 +1572,16 @@ not to limit concurrency.
| `front-end/src/components/GameSession.tsx` | Game session UI: header, coin status, game area, overlays |
| `front-end/src/hooks/useGameSession.ts` | Thin React boundary: controller/runtime setup, host subscription, typed dispatch, selector projection |
| `front-end/src/lib/session/sessionMachine*.ts` | Root dispatcher plus cohesive channel, between-hand, proposal, durable-game, notification, command, effect, runtime, and persistence modules |
| `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` |
| `front-end/src/lib/session/sessionSnapshot.ts` | Canonical `SessionModel` → v12 presentation snapshot encoder |
| `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` |
| `front-end/src/lib/session/sessionSnapshot.ts` | Canonical `SessionModel` → v13 presentation snapshot encoder |
| `front-end/src/lib/gameRegistry.ts` | Exhaustive pure feature registration and game-owned codec/terms/compose dispatch |
| `front-end/src/lib/gameMountRegistry.tsx` | Exhaustive React live/frozen mount registration |
| `front-end/src/features/calPoker/useCalpokerHand.ts` | Calpoker hook: five-step protocol, card parsing, move submission |
| `front-end/src/hooks/SessionController.ts` | WASM bridge (`SessionController` class): message delivery, block data, event queue, `getWasmFields()` for persistence |
| `front-end/src/hooks/WasmStateInit.ts` | WASM initialization: load binary, deposit .hex files, create cradle |
| `front-end/src/hooks/blobSingleton.ts` | Singleton management: `getOrCreateSessionController` / `destroySessionController`; restore path for session persistence |
| `front-end/src/services/PeerSession.ts` | Per-session peer state: session ID, peer ID, liveness, message buffering/routing, send methods |
| `front-end/src/hooks/save.ts` | v12 cache/write and live/terminal lifecycle facade |
| `front-end/src/hooks/save.ts` | v13 cache/write and live/terminal lifecycle facade |
| `front-end/src/hooks/saveCoordination.ts` | Resume markers, active-tab lease, and cross-tab persistence fencing |
| `front-end/src/hooks/saveHardReset.ts` | Hard-reset and WalletConnect browser-storage cleanup |
| `front-end/src/hooks/savePreferences.ts` | Local preference encoding and decoding |
Expand Down
21 changes: 11 additions & 10 deletions ON_CHAIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ unroll is extracted from the on-chain conditions.

### Step 2: Preempt or Wait

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

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


| On-chain SN vs ours | Action | Explanation |
| ------------------- | ----------------------- | ----------------------------------------------------------------------------------- |
| On-chain < ours, opposite parity | **Preempt** (immediate) | Spend the unroll coin immediately with our higher SN and more up-to-date conditions |
| 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 |
| On-chain == ours | **Wait for timeout** | The unroll is at the state we expect; wait for it to resolve |
| On-chain > ours | **Error** | We've been hacked or something went very wrong |
| On-chain SN vs ours | Action | Explanation |
| ------------------- | ------ | ----------- |
| 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 |
| 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 |
| On-chain == ours | **Wait for timeout** | The unroll is at the state we expect; wait for it to resolve |
| Unknown puzzle hash or conditions-hash mismatch | **Error** | Never signed, or not the unroll we signed — classified when the channel coin spend is parsed |


Preemption is **immediate** — no timelock. This is by design: the preempting
Expand Down
Loading
Loading