Play-route transition state model and UX (#343) #519

Merged
PlasticDigits merged 1 commit from issue-343-play-route-transition-ux into main 2026-06-25 22:23:20 +00:00
PlasticDigits commented 2026-06-25 22:01:32 +00:00 (Migrated from gitlab.com)

Summary

Implements GitLab #343 — play-route podium transition state model and UX for /:

  • Pure state machine (live, unarmed, expired-pending-roll, settling, epoch-advanced, syncing) derived from indexer GET /v1/arena/timers + podium epochs (#344 ingest lag).
  • Hero + side-chip countdowns share chainNowSec wall-vs-chain skew from useArenaHeroTimer (extracted arenaChainNow.ts) — no more split block_timestamp_sec anchors between polls.
  • Expired timers show settlement copy + aria-busy (not a silent frozen 00:00); optional permissionless rollPodiumEpoch CTA behind ChainMismatchWriteBarrier.
  • Documented in arena-views §343 with links to time-arena.md; invariant INV-FRONTEND-343-TRANSITION-UX.

Acceptance checklist

Criterion Verification Result
Distinct UX for timer-at-zero vs live countdown arenaTransitionState.test.ts (expired-pending-roll / settling / live) PASS
Hero + side chips share chainNowSec skew arenaChainNow.test.ts, indexerFirstDisplay.test.ts, ArenaTimerChips.test.tsx PASS
Transition model documented in arena-views.md arena-views §343 PASS
No new browser RPC display reads indexerFirstDisplay.test.ts (unchanged indexer-first guards) PASS
data-testid hooks for settling / pending-roll arena-timer-expired-pending-roll, arena-timer-settling, arena-timer-epoch-advanced, arena-podium-roll-cta PASS
Frontend unit suite cd frontend && npm run typecheck && npm run lint && npm test PASS
Manual Anvil warp past deadline Not run in Cloud Agent session SKIP

Third-party verification

  1. cd frontend && npm run typecheck && npm run lint && npm test
  2. Start Anvil stack: bash scripts/start-qa-local-full-stack.sh --no-swarm --no-frontend
  3. cd frontend && npm run dev — open http://127.0.0.1:5173/
  4. Warp Anvil past a podium deadline (cast rpc anvil_increaseTime … or buy until expiry)
  5. Confirm hero shows Waiting for settlement foot (data-testid="arena-timer-expired-pending-roll") and optional Roll epoch CTA
  6. Confirm side chips use the same countdown skew as hero (no drift between hero 00:00 and chip timer during expiry)

Related: #342 (parent gap analysis). E2E transition spec remains sibling follow-up.

## Summary Implements GitLab #343 — play-route podium **transition state model** and UX for `/`: - Pure state machine (`live`, `unarmed`, `expired-pending-roll`, `settling`, `epoch-advanced`, `syncing`) derived from indexer `GET /v1/arena/timers` + podium epochs ([#344](https://gitlab.com/PlasticDigits/yieldomega/-/issues/344) ingest lag). - Hero + side-chip countdowns share **`chainNowSec`** wall-vs-chain skew from `useArenaHeroTimer` (extracted `arenaChainNow.ts`) — no more split `block_timestamp_sec` anchors between polls. - Expired timers show settlement copy + `aria-busy` (not a silent frozen `00:00`); optional permissionless **`rollPodiumEpoch`** CTA behind `ChainMismatchWriteBarrier`. - Documented in [arena-views §343](docs/frontend/arena-views.md#play-route-transition-ux-gitlab-343) with links to [time-arena.md](docs/product/time-arena.md); invariant **`INV-FRONTEND-343-TRANSITION-UX`**. ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | Distinct UX for timer-at-zero vs live countdown | `arenaTransitionState.test.ts` (expired-pending-roll / settling / live) | PASS | | Hero + side chips share `chainNowSec` skew | `arenaChainNow.test.ts`, `indexerFirstDisplay.test.ts`, `ArenaTimerChips.test.tsx` | PASS | | Transition model documented in `arena-views.md` | [arena-views §343](docs/frontend/arena-views.md#play-route-transition-ux-gitlab-343) | PASS | | No new browser RPC display reads | `indexerFirstDisplay.test.ts` (unchanged indexer-first guards) | PASS | | `data-testid` hooks for settling / pending-roll | `arena-timer-expired-pending-roll`, `arena-timer-settling`, `arena-timer-epoch-advanced`, `arena-podium-roll-cta` | PASS | | Frontend unit suite | `cd frontend && npm run typecheck && npm run lint && npm test` | PASS | | Manual Anvil warp past deadline | Not run in Cloud Agent session | SKIP | ## Third-party verification 1. `cd frontend && npm run typecheck && npm run lint && npm test` 2. Start Anvil stack: `bash scripts/start-qa-local-full-stack.sh --no-swarm --no-frontend` 3. `cd frontend && npm run dev` — open `http://127.0.0.1:5173/` 4. Warp Anvil past a podium deadline (`cast rpc anvil_increaseTime …` or buy until expiry) 5. Confirm hero shows **Waiting for settlement** foot (`data-testid="arena-timer-expired-pending-roll"`) and optional **Roll epoch** CTA 6. Confirm side chips use the same countdown skew as hero (no drift between hero `00:00` and chip timer during expiry) Related: #342 (parent gap analysis). E2E transition spec remains sibling follow-up.
PlasticDigits commented 2026-06-25 22:03:51 +00:00 (Migrated from gitlab.com)

Security review — MR !519

Commit reviewed: e05dae569dcc9e0c4f67fcf44966a371854fe7e4 (base 2b5d0240)

Scope: Play-route podium transition state model (arenaTransitionState.ts, arenaChainNow.ts, usePodiumEpochLatch.ts), hero/chip timer UX wiring, and new permissionless rollPodiumEpoch CTA (ArenaPodiumRollCta.tsx). Docs/tests/invariant rows only — no indexer, contract, or dependency changes in this diff.

Method: Traced indexer-sourced fields (podium_deadlines_sec, last_buy_deadline_sec, indexed_through_block, read_block_number, podium_epochs) through the pure derivation layer to UI sinks and the wagmi write path; verified on-chain TimeArena.rollPodiumEpoch enforces category < NUM_PODIUM_CATEGORIES and block.timestamp > podiumDeadline[category] with nonReentrant. Checked prior MR discussion threads (none).

Outcome: FINDINGS: 0 medium+

Inline threads: none

Notes (informational, below reporting threshold)

  • ArenaPodiumRollCta: contractIndex is sourced from static carousel slot constants (0–3), gated by ChainMismatchWriteBarrier, wallet-connected check, and session.arenaPaused. On-chain guards reject early rolls and out-of-range categories; a compromised indexer can at worst prompt a reverting tx (gas grief), consistent with the existing indexer-first display trust model (INV-FRONTEND-301).
  • isSuspiciousDeadline: Corrupt/malicious far-future deadlines degrade to syncing rather than inflated live countdowns — defensive, not exploitable.
  • XSS: Transition copy is static string literals rendered as React text nodes; no attacker-controlled HTML.
  • Reentrancy / fund movement: Settlement logic remains in audited TimeArena.rollPodiumEpoch; this MR only surfaces an optional user-initiated write.

Security review: no medium+ findings on this diff.

## Security review — MR !519 **Commit reviewed:** `e05dae569dcc9e0c4f67fcf44966a371854fe7e4` (base `2b5d0240`) **Scope:** Play-route podium transition state model (`arenaTransitionState.ts`, `arenaChainNow.ts`, `usePodiumEpochLatch.ts`), hero/chip timer UX wiring, and new permissionless `rollPodiumEpoch` CTA (`ArenaPodiumRollCta.tsx`). Docs/tests/invariant rows only — no indexer, contract, or dependency changes in this diff. **Method:** Traced indexer-sourced fields (`podium_deadlines_sec`, `last_buy_deadline_sec`, `indexed_through_block`, `read_block_number`, `podium_epochs`) through the pure derivation layer to UI sinks and the wagmi write path; verified on-chain `TimeArena.rollPodiumEpoch` enforces `category < NUM_PODIUM_CATEGORIES` and `block.timestamp > podiumDeadline[category]` with `nonReentrant`. Checked prior MR discussion threads (none). **Outcome:** `FINDINGS: 0` medium+ **Inline threads:** none ### Notes (informational, below reporting threshold) - **`ArenaPodiumRollCta`:** `contractIndex` is sourced from static carousel slot constants (0–3), gated by `ChainMismatchWriteBarrier`, wallet-connected check, and `session.arenaPaused`. On-chain guards reject early rolls and out-of-range categories; a compromised indexer can at worst prompt a reverting tx (gas grief), consistent with the existing indexer-first display trust model (`INV-FRONTEND-301`). - **`isSuspiciousDeadline`:** Corrupt/malicious far-future deadlines degrade to `syncing` rather than inflated live countdowns — defensive, not exploitable. - **XSS:** Transition copy is static string literals rendered as React text nodes; no attacker-controlled HTML. - **Reentrancy / fund movement:** Settlement logic remains in audited `TimeArena.rollPodiumEpoch`; this MR only surfaces an optional user-initiated write. Security review: no medium+ findings on this diff.
PlasticDigits commented 2026-06-25 22:23:21 +00:00 (Migrated from gitlab.com)

mentioned in commit 304744cae2

mentioned in commit 304744cae27cb05a9b12f2ab202b6ddc3c6b752c
PlasticDigits (Migrated from gitlab.com) merged commit 304744cae2 into main 2026-06-25 22:23:21 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega!519
No description provided.