[visual][arena] USD-equivalent displays: add staleness / last-updated affordance #192

Closed
opened 2026-05-11 09:02:29 +00:00 by PlasticDigits · 13 comments
PlasticDigits commented 2026-05-11 09:02:29 +00:00 (Migrated from gitlab.com)

Summary

During local-stack visual QA (#164), USD-equivalent figures (e.g. total raise / podium prize hints) were present and plausible, but there was no indication of quote age or oracle/feed freshness. P3 polish — reduces silent-stale trust issues.

What is wrong

Users see USD or ≈ $ amounts with no “as of” context, so a delayed or cached price read looks the same as a live one.

Expected

When USD is derived from an off-chain or secondary feed, the UI should communicate staleness or refresh cadence (subtle text, tooltip, or icon), consistent with product risk appetite — without alarming users on every tick.

Relevant docs / skills / files

  1. Identify where USD strings are computed (single hook vs duplicated).
  2. If the feed exposes timestamp / block, surface “Updated …” or a non-blocking stale badge after a threshold.
  3. If no timestamp exists, document implicit refresh interval in UI copy (minimal) or add telemetry-first iteration.

Acceptance criteria

  • Any visible USD aggregation on /timecurve routes either shows last-updated context or documents why USD is omitted.
  • No noisy banners on healthy stacks; copy matches P3 polish bar.

Verification checklist

  • Local stack + prod-shaped env: confirm behavior when RPC/indexer is slow vs fast.
  • Mobile width: staleness affordance does not wrap awkwardly in Arena/Simple headers or cards.
  • Cross-link manual QA row in docs/testing/manual-qa-checklists.md if a repeatable visual check is added.

/cc @Brouie — tracked from #164 §6.7 polish nit.

## Summary During local-stack visual QA ([#164](https://gitlab.com/PlasticDigits/yieldomega/-/issues/164)), **USD-equivalent** figures (e.g. total raise / podium prize hints) were present and plausible, but there was **no indication of quote age** or oracle/feed freshness. **P3 polish** — reduces silent-stale trust issues. ## What is wrong Users see **USD** or **≈ $** amounts with **no “as of”** context, so a delayed or cached price read looks the same as a live one. ## Expected When USD is derived from an **off-chain or secondary feed**, the UI should communicate **staleness or refresh cadence** (subtle text, tooltip, or icon), consistent with product risk appetite — without alarming users on every tick. ## Relevant docs / skills / files - Umbrella: [#164](https://gitlab.com/PlasticDigits/yieldomega/-/issues/164) §6 item 7 (partial — staleness called out as nit). - [`docs/frontend/timecurve-views.md`](docs/frontend/timecurve-views.md), [`docs/testing/invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md). - Search frontend for USD/total raise formatting and any price hooks (Arena + Simple). ## Recommended fix 1. Identify **where** USD strings are computed (single hook vs duplicated). 2. If the feed exposes **timestamp / block**, surface **“Updated …”** or a **non-blocking** stale badge after a threshold. 3. If no timestamp exists, document **implicit refresh interval** in UI copy (minimal) or add telemetry-first iteration. ## Acceptance criteria - [ ] Any **visible USD aggregation** on `/timecurve` routes either shows **last-updated context** or documents why USD is omitted. - [ ] No noisy banners on healthy stacks; copy matches **P3** polish bar. ## Verification checklist - [ ] Local stack + prod-shaped env: confirm behavior when RPC/indexer is slow vs fast. - [ ] Mobile width: staleness affordance does not wrap awkwardly in Arena/Simple headers or cards. - [ ] Cross-link manual QA row in [`docs/testing/manual-qa-checklists.md`](docs/testing/manual-qa-checklists.md) if a repeatable visual check is added. /cc @Brouie — tracked from [#164](https://gitlab.com/PlasticDigits/yieldomega/-/issues/164) §6.7 polish nit.
PlasticDigits commented 2026-05-11 09:02:30 +00:00 (Migrated from gitlab.com)

marked as related to #164

marked as related to #164
PlasticDigits commented 2026-05-11 09:05:06 +00:00 (Migrated from gitlab.com)

mentioned in issue #164

mentioned in issue #164
PlasticDigits commented 2026-05-11 09:15:31 +00:00 (Migrated from gitlab.com)

mentioned in commit e3d5f2281c4b7c50d1c5ed32de1ddbaf604cf51d

mentioned in commit e3d5f2281c4b7c50d1c5ed32de1ddbaf604cf51d
PlasticDigits commented 2026-05-11 09:16:28 +00:00 (Migrated from gitlab.com)

Shipped (GitLab #192 — USD staleness affordance)

Merged to main (commit on main includes INV-FRONTEND-192-USD-AFFORDANCE).

What changed

  • Arena (/timecurve/arena, sale live): TOTAL USD block now has a title explaining the 1 CL8Y = 1 USD placeholder + ~1s core read poll, plus a muted line CL8Y total seen <relative> · USD is illustrative (1 CL8Y = $1) when totalRaised reads succeed.
  • Simple podiums: each ≈ $… USD row has a title for the static 0.98× CL8Y→USDM display shape; a second footnote states the static rate and Prize CL8Y preview seen … when preview data updates.
  • Shared helpers: cl8yUsdEquivalentDisplay.ts, useLastObservedAtForSerializedDep.ts, useRelativeFreshnessLabel.ts (~8s tick, no per-second churn).
  • Docs: docs/testing/invariants-and-business-logic.md, docs/frontend/timecurve-views.md, docs/testing/manual-qa-checklists.md, skills/README.md, skills/play-timecurve-doubloon/SKILL.md, .cursor/skills/yieldomega-guardrails/SKILL.md.

Verification checklist for @Brouie

  • Arena: hero shows TOTAL RAISE + TOTAL USD; hover/long-press TOTAL USD area → full tooltip; muted CL8Y total seen line present after reads.
  • Simple: podium ≈ USD rows + second footnote; hover ≈ row → static-rate tooltip; Prize CL8Y preview seen appears when podiumPayoutPreview is wired.
  • Mobile ~375px: affordance text wraps without clipping the hero.
  • Regression: npm ci && npx vitest run src/lib/cl8yUsdEquivalentDisplay.test.ts src/pages/timecurve/TimeCurveSimplePodiumSection.test.tsx && npm run typecheck in frontend/.

Issue left open for your sign-off.

/cc @Brouie — please verify when you can.

## Shipped (GitLab #192 — USD staleness affordance) Merged to `main` (commit on `main` includes **INV-FRONTEND-192-USD-AFFORDANCE**). ### What changed - **Arena** (`/timecurve/arena`, sale live): **TOTAL USD** block now has a **`title`** explaining the **1 CL8Y = 1 USD** placeholder + ~1s core read poll, plus a muted line **CL8Y total seen \<relative\> · USD is illustrative (1 CL8Y = $1)** when `totalRaised` reads succeed. - **Simple** podiums: each **≈ $… USD** row has a **`title`** for the static **0.98×** CL8Y→USDM display shape; a second footnote states the static rate and **Prize CL8Y preview seen …** when preview data updates. - Shared helpers: `cl8yUsdEquivalentDisplay.ts`, `useLastObservedAtForSerializedDep.ts`, `useRelativeFreshnessLabel.ts` (~8s tick, no per-second churn). - **Docs:** `docs/testing/invariants-and-business-logic.md`, `docs/frontend/timecurve-views.md`, `docs/testing/manual-qa-checklists.md`, `skills/README.md`, `skills/play-timecurve-doubloon/SKILL.md`, `.cursor/skills/yieldomega-guardrails/SKILL.md`. ### Verification checklist for @Brouie - [ ] **Arena:** hero shows **TOTAL RAISE** + **TOTAL USD**; hover/long-press **TOTAL USD** area → full tooltip; muted **CL8Y total seen** line present after reads. - [ ] **Simple:** podium **≈ USD** rows + second footnote; hover **≈** row → static-rate tooltip; **Prize CL8Y preview seen** appears when `podiumPayoutPreview` is wired. - [ ] **Mobile ~375px:** affordance text wraps without clipping the hero. - [ ] **Regression:** `npm ci && npx vitest run src/lib/cl8yUsdEquivalentDisplay.test.ts src/pages/timecurve/TimeCurveSimplePodiumSection.test.tsx && npm run typecheck` in `frontend/`. Issue left **open** for your sign-off. /cc @Brouie — please verify when you can.
Brouie commented 2026-05-11 09:51:14 +00:00 (Migrated from gitlab.com)

mentioned in issue #191

mentioned in issue #191
Brouie commented 2026-05-11 10:01:43 +00:00 (Migrated from gitlab.com)

verified at HEAD c3b43b8 — full pass on staleness affordance.

source spot-checks (4/4):

  • new module cl8yUsdEquivalentDisplay.ts with ARENA_TOTAL_USD_EQUIV_TITLE + SIMPLE_PODIUM_USD_EQUIV_TITLE copy strings explicitly framing the figures as not-live-oracle (1× placeholder for Arena, static 0.98× CL8Y→USDM shape for Simple podium)
  • formatRelativeFreshnessEnglish thresholds non-alarming: <8s=just now, <60s=Xs ago, <60m=Xm ago, <48h=Xh ago, else Xd ago
  • useRelativeFreshnessLabel hook ticks every 8s, skips the interval entirely when fromMs === undefined, cleans up on unmount
  • useLastObservedAtForSerializedDep hook uses useRef + useLayoutEffect, records Date.now() only when serialized dep changes, handles undefined reset

wiring confirmed:

  • Arena: title at TimeCurveArenaView.tsx:1063, freshness hook at line 69 (driven by totalRaiseCl8yObservedAtMs from useTimeCurveArenaModel.tsx:1707)
  • Simple podium: title at TimeCurveSimplePodiumSection.tsx:107 and :248, both hooks at lines 195-196

tests:

  • vitest 313/313 pass (was 307 pre-fix)
  • targeted: cl8yUsdEquivalentDisplay.test.ts (5/5)

live walk on fresh stack at HEAD with active swarm:

  • /timecurve/arena Total USD — tooltip on hover shows the ARENA_TOTAL_USD_EQUIV_TITLE copy, freshness label tracks the ~1s contract poll cadence (ticks every ~8s per the hook design)
  • /timecurve Simple podium ≈ USD — tooltip on hover shows the SIMPLE_PODIUM_USD_EQUIV_TITLE copy
  • no noisy banners on healthy stack — staleness affordance reads as subtle as designed
  • mobile-width responsive — no awkward wrap on Arena hero or Simple podium

ready for close on your side @PlasticDigits

verified at HEAD c3b43b8 — full pass on staleness affordance. source spot-checks (4/4): - new module `cl8yUsdEquivalentDisplay.ts` with `ARENA_TOTAL_USD_EQUIV_TITLE` + `SIMPLE_PODIUM_USD_EQUIV_TITLE` copy strings explicitly framing the figures as not-live-oracle (1× placeholder for Arena, static 0.98× CL8Y→USDM shape for Simple podium) - `formatRelativeFreshnessEnglish` thresholds non-alarming: <8s=`just now`, <60s=`Xs ago`, <60m=`Xm ago`, <48h=`Xh ago`, else `Xd ago` - `useRelativeFreshnessLabel` hook ticks every 8s, skips the interval entirely when `fromMs === undefined`, cleans up on unmount - `useLastObservedAtForSerializedDep` hook uses useRef + useLayoutEffect, records `Date.now()` only when serialized dep changes, handles undefined reset wiring confirmed: - Arena: title at TimeCurveArenaView.tsx:1063, freshness hook at line 69 (driven by `totalRaiseCl8yObservedAtMs` from useTimeCurveArenaModel.tsx:1707) - Simple podium: title at TimeCurveSimplePodiumSection.tsx:107 and :248, both hooks at lines 195-196 tests: - vitest 313/313 pass (was 307 pre-fix) - targeted: cl8yUsdEquivalentDisplay.test.ts (5/5) live walk on fresh stack at HEAD with active swarm: - `/timecurve/arena` Total USD — tooltip on hover shows the `ARENA_TOTAL_USD_EQUIV_TITLE` copy, freshness label tracks the ~1s contract poll cadence (ticks every ~8s per the hook design) - `/timecurve` Simple podium ≈ USD — tooltip on hover shows the `SIMPLE_PODIUM_USD_EQUIV_TITLE` copy - no noisy banners on healthy stack — staleness affordance reads as subtle as designed - mobile-width responsive — no awkward wrap on Arena hero or Simple podium ready for close on your side @PlasticDigits
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-11 10:26:14 +00:00
Brouie commented 2026-05-13 00:33:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #197

mentioned in issue #197
PlasticDigits commented 2026-05-13 15:13:34 +00:00 (Migrated from gitlab.com)

mentioned in commit 4294c9a706

mentioned in commit 4294c9a7065e5d3f417cada50b8ebff893ad6c6d
PlasticDigits commented 2026-05-13 16:03:46 +00:00 (Migrated from gitlab.com)

mentioned in issue #206

mentioned in issue #206
Brouie commented 2026-05-14 08:30:11 +00:00 (Migrated from gitlab.com)

mentioned in merge request !389

mentioned in merge request !389
PlasticDigits commented 2026-05-21 04:41:43 +00:00 (Migrated from gitlab.com)

mentioned in issue #229

mentioned in issue #229
PlasticDigits commented 2026-05-21 13:21:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #235

mentioned in issue #235
PlasticDigits commented 2026-06-07 13:57:54 +00:00 (Migrated from gitlab.com)

mentioned in issue #302

mentioned in issue #302
Sign in to join this conversation.
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#192
No description provided.