Time Booster podium: tiered duration score (seconds · mm:ss · hh:mm:ss · dd:hh:mm:ss) #228

Closed
opened 2026-05-21 04:40:54 +00:00 by PlasticDigits · 4 comments
PlasticDigits commented 2026-05-21 04:40:54 +00:00 (Migrated from gitlab.com)

Summary

On /timecurve (Simple reserve podiums) and /timecurve/arena (podium spotlights), Time Booster leaderboard scores render as a raw second count with an s suffix (e.g. Score: 300s added, 300 s). Large cumulative values (totalEffectiveTimerSecAdded / indexer buyer_total_effective_timer_sec) are hard to scan. They should use tiered clock formatting aligned with existing timer helpers in formatTimer.ts.

Current behavior

Surface File Format today
Simple podium meta (1st/2nd/3rd score line) frontend/src/pages/timecurve/timeCurveSimplePodiumScore.ts Score: ${digits}s added when categoryIndex === 3
Arena podium spotlight leader value frontend/src/pages/timecurve/podiumFormat.ts via useTimeCurveArenaModel.tsx ${formatLocaleInteger(bi)} s when categoryIndex === 3

Example from TimeCurveSimplePodiumSection.test.tsx: fixture value 300 renders Score: 300s added.

Onchain / indexer semantics are unchanged: Time Booster ranks wallets by cumulative effective deadline seconds added (docs/product/primitives.md — totalEffectiveTimerSecAdded).

Expected behavior

Format whole seconds only (floor/clamp negatives to 0) with these tiers:

Range (inclusive lower, exclusive upper unless noted) Display
0 – 59 s Seconds only — locale integer, no s suffix and no colons (e.g. 45, not 45s or 00:45)
60 s – 59 m 59 s mm:ss (reuse formatMmSsCountdown; both segments zero-padded, minutes may exceed 99)
1 h – 23 h 59 m 59 s HH:MM:SS (reuse formatCountdown)
≥ 24 h dd:HH:MM:SS — whole days + remainder clock (same day/remainder split as formatLaunchCountdown, but single string with a : between day count and clock, e.g. 2:03:13:07 for 2d 3h 13m 7s)

Copy: Simple meta can stay Score: <formatted> added (e.g. Score: 05:00 added for 300s, Score: 45 added for 45s). Arena spotlight value should use the same formatter (no separate s suffix).

  1. Add a shared pure helper, e.g. formatTimeBoosterPodiumSec(totalSec: number | bigint): string next to formatTimer.ts (or a thin timeBoosterPodiumFormat.ts that imports formatMmSsCountdown / formatCountdown / the day split from formatLaunchCountdown).
  2. Wire it in:
    • formatSimplePodiumScoreLine (category 3)
    • formatPodiumLeaderboardValue (category 3)
  3. Vitest table-driven cases for tier boundaries: 0, 59, 60, 3599, 3600, 86399, 86400, multi-day samples; clamp -1 → 0.
  4. Update existing tests:
  5. Out of scope (optional follow-up): buy projected-effects line +Xs time-booster credit in timeCurveBuyProjectedEffects.ts — podium-only unless product wants parity.

Do not change indexer/API payloads or onchain reads; this is display-only.

Verification criteria

Automated

  • npm test -- --run formatTimer (or new module test file) passes tier boundary table.
  • npm test -- --run timeCurveSimplePodiumScore and TimeCurveSimplePodiumSection pass updated expectations.
  • No new linter issues in touched files.

Manual (local Anvil + indexer optional)

  1. Open /timecurve → Prize podiums → Time Booster.
  2. With a live or mocked leaderboard:
    • < 60 s cumulative: score shows digits only (no s, no colons).
    • ≥ 60 s and < 1 h: mm:ss (e.g. 300 → 05:00).
    • ≥ 1 h and < 24 h: HH:MM:SS.
    • ≥ 24 h: d:HH:MM:SS day prefix.
  3. Open /timecurve/arena → Time Booster spotlight leader value matches the same formatting for the #1 slot raw value.
  4. Other podium categories unchanged (Last Buy “seconds ago”, WarBow BP, Defended Streak copy).

Regression guards

  • Empty / zero Time Booster slot still shows Score: — (or existing empty-winner behavior), not Score: 0 added unless product explicitly wants zero shown.
  • Locale grouping: sub-60 tier may use formatLocaleInteger; colon tiers stay fixed-width numeric segments per formatTimer conventions.

References

## Summary On **`/timecurve`** (Simple reserve podiums) and **`/timecurve/arena`** (podium spotlights), **Time Booster** leaderboard scores render as a raw second count with an **`s`** suffix (e.g. `Score: 300s added`, `300 s`). Large cumulative values (`totalEffectiveTimerSecAdded` / indexer `buyer_total_effective_timer_sec`) are hard to scan. They should use **tiered clock formatting** aligned with existing timer helpers in `formatTimer.ts`. ## Current behavior | Surface | File | Format today | |---------|------|----------------| | Simple podium meta (1st/2nd/3rd score line) | [`frontend/src/pages/timecurve/timeCurveSimplePodiumScore.ts`](frontend/src/pages/timecurve/timeCurveSimplePodiumScore.ts) | `Score: ${digits}s added` when `categoryIndex === 3` | | Arena podium spotlight leader value | [`frontend/src/pages/timecurve/podiumFormat.ts`](frontend/src/pages/timecurve/podiumFormat.ts) via [`useTimeCurveArenaModel.tsx`](frontend/src/pages/timeCurveArena/useTimeCurveArenaModel.tsx) | `${formatLocaleInteger(bi)} s` when `categoryIndex === 3` | Example from [`TimeCurveSimplePodiumSection.test.tsx`](frontend/src/pages/timecurve/TimeCurveSimplePodiumSection.test.tsx): fixture value `300` renders **`Score: 300s added`**. Onchain / indexer semantics are unchanged: Time Booster ranks wallets by **cumulative effective deadline seconds added** ([`docs/product/primitives.md`](docs/product/primitives.md) — `totalEffectiveTimerSecAdded`). ## Expected behavior Format **whole seconds only** (floor/clamp negatives to `0`) with these tiers: | Range (inclusive lower, exclusive upper unless noted) | Display | |-----------------------------------------------------|---------| | **0 – 59 s** | **Seconds only** — locale integer, **no** `s` suffix and **no** colons (e.g. `45`, not `45s` or `00:45`) | | **60 s – 59 m 59 s** | **`mm:ss`** (reuse [`formatMmSsCountdown`](frontend/src/pages/timecurve/formatTimer.ts); both segments zero-padded, minutes may exceed 99) | | **1 h – 23 h 59 m 59 s** | **`HH:MM:SS`** (reuse [`formatCountdown`](frontend/src/pages/timecurve/formatTimer.ts)) | | **≥ 24 h** | **`dd:HH:MM:SS`** — whole days + remainder clock (same day/remainder split as [`formatLaunchCountdown`](frontend/src/pages/timecurve/formatTimer.ts), but **single string** with a `:` between day count and clock, e.g. `2:03:13:07` for 2d 3h 13m 7s) | **Copy:** Simple meta can stay `Score: <formatted> added` (e.g. `Score: 05:00 added` for 300s, `Score: 45 added` for 45s). Arena spotlight value should use the **same formatter** (no separate ` s` suffix). ## Recommended implementation 1. Add a shared pure helper, e.g. `formatTimeBoosterPodiumSec(totalSec: number | bigint): string` next to [`formatTimer.ts`](frontend/src/pages/timecurve/formatTimer.ts) (or a thin `timeBoosterPodiumFormat.ts` that imports `formatMmSsCountdown` / `formatCountdown` / the day split from `formatLaunchCountdown`). 2. Wire it in: - `formatSimplePodiumScoreLine` (category 3) - `formatPodiumLeaderboardValue` (category 3) 3. **Vitest** table-driven cases for tier boundaries: `0`, `59`, `60`, `3599`, `3600`, `86399`, `86400`, multi-day samples; clamp `-1` → `0`. 4. Update existing tests: - [`timeCurveSimplePodiumScore.test.ts`](frontend/src/pages/timecurve/timeCurveSimplePodiumScore.test.ts) (`60` → `Score: 60 added` or `Score: 01:00 added` per tier rule at exactly 60) - [`TimeCurveSimplePodiumSection.test.tsx`](frontend/src/pages/timecurve/TimeCurveSimplePodiumSection.test.tsx) (`300` → `Score: 05:00 added`) 5. **Out of scope (optional follow-up):** buy projected-effects line `+Xs time-booster credit` in [`timeCurveBuyProjectedEffects.ts`](frontend/src/pages/timecurve/timeCurveBuyProjectedEffects.ts) — podium-only unless product wants parity. **Do not** change indexer/API payloads or onchain reads; this is display-only. ## Verification criteria ### Automated - [ ] `npm test -- --run formatTimer` (or new module test file) passes tier boundary table. - [ ] `npm test -- --run timeCurveSimplePodiumScore` and `TimeCurveSimplePodiumSection` pass updated expectations. - [ ] No new linter issues in touched files. ### Manual (local Anvil + indexer optional) 1. Open **`/timecurve`** → **Prize podiums** → **Time Booster**. 2. With a live or mocked leaderboard: - **&lt; 60 s** cumulative: score shows **digits only** (no `s`, no colons). - **≥ 60 s** and **&lt; 1 h**: **`mm:ss`** (e.g. 300 → `05:00`). - **≥ 1 h** and **&lt; 24 h**: **`HH:MM:SS`**. - **≥ 24 h**: **`d:HH:MM:SS`** day prefix. 3. Open **`/timecurve/arena`** → Time Booster spotlight **leader value** matches the same formatting for the #1 slot raw value. 4. Other podium categories unchanged (Last Buy “seconds ago”, WarBow `BP`, Defended Streak copy). ### Regression guards - [ ] Empty / zero Time Booster slot still shows `Score: —` (or existing empty-winner behavior), not `Score: 0 added` unless product explicitly wants zero shown. - [ ] Locale grouping: sub-60 tier may use `formatLocaleInteger`; colon tiers stay fixed-width numeric segments per `formatTimer` conventions. ## References - Product: [`docs/product/primitives.md`](docs/product/primitives.md) (Time booster = most effective timer seconds added) - Existing countdown utilities: [`frontend/src/pages/timecurve/formatTimer.ts`](frontend/src/pages/timecurve/formatTimer.ts) - Manual QA podiums: [`docs/testing/manual-qa-checklists.md`](docs/testing/manual-qa-checklists.md) (four categories, issue #113 area)
Brouie commented 2026-05-21 05:38:40 +00:00 (Migrated from gitlab.com)

Walked what's walkable pre-ship.

Automated baseline (current code on main @ 8fd0841):

Full Vitest suite ran clean — 534/534 tests pass across 93 files. That covers all three test files this issue lists in the automated criteria (formatTimer, timeCurveSimplePodiumScore, TimeCurveSimplePodiumSection). Pre-refactor baseline locked.

Current-state baseline (production /timecurve → PRIZE PODIUMS):

Time Booster category leader values currently render with raw Nnnns added suffix as described in the issue. Walked the podium card visually on production — three leader rows showing whole-second integer scores with s added suffix, no tier formatting applied.

Per the proposed tier rules, the current production leader values all fall in the 1h – 23h 59m 59s band and would render as HH:MM:SS post-refactor. None of the current leaders happen to be in the sub-60s, mm:ss, or multi-day tiers, so those tier boundaries would need fixture data to demonstrate.

Other podium categories (Last Buy "seconds ago", WarBow BP, Defended Streak "sequential buys") render unchanged — confirms the out-of-scope guard from the issue body.

Acceptance criteria items walkable pre-ship:

Item Status
Existing test files pass on current code PASS (subset of 534/534)
No new linter issues in touched files Not separately verified, but build clean

Items blocked pre-ship (require refactor to ship first):

  • Tier boundary tests at 0, 59, 60, 3599, 3600, 86399, 86400, multi-day samples — tests don't exist yet
  • Updated timeCurveSimplePodiumScore.test.ts / TimeCurveSimplePodiumSection.test.tsx expectations
  • Manual walk of sub-60s / mm:ss / HH:MM:SS / d:HH:MM:SS tiers — requires either Anvil with seeded fixtures or a leader hitting each band naturally

Items not exercised in this session:

  • Arena /timecurve/arena Time Booster spotlight leader value — not located in the visible Arena layout (STEAL/GUARD/REVENGE surface). Could be sale-state dependent or below the scroll I captured. If it's user-visible somewhere I missed, point me to it and I'll grab.

Pre-refactor baseline locked. Ready to re-walk when the refactor lands.

Walked what's walkable pre-ship. **Automated baseline (current code on main @ 8fd0841):** Full Vitest suite ran clean — 534/534 tests pass across 93 files. That covers all three test files this issue lists in the automated criteria (`formatTimer`, `timeCurveSimplePodiumScore`, `TimeCurveSimplePodiumSection`). Pre-refactor baseline locked. **Current-state baseline (production /timecurve → PRIZE PODIUMS):** Time Booster category leader values currently render with raw `Nnnns added` suffix as described in the issue. Walked the podium card visually on production — three leader rows showing whole-second integer scores with `s added` suffix, no tier formatting applied. Per the proposed tier rules, the current production leader values all fall in the `1h – 23h 59m 59s` band and would render as `HH:MM:SS` post-refactor. None of the current leaders happen to be in the sub-60s, mm:ss, or multi-day tiers, so those tier boundaries would need fixture data to demonstrate. Other podium categories (Last Buy "seconds ago", WarBow BP, Defended Streak "sequential buys") render unchanged — confirms the out-of-scope guard from the issue body. **Acceptance criteria items walkable pre-ship:** | Item | Status | |------|--------| | Existing test files pass on current code | PASS (subset of 534/534) | | No new linter issues in touched files | Not separately verified, but build clean | **Items blocked pre-ship (require refactor to ship first):** * Tier boundary tests at 0, 59, 60, 3599, 3600, 86399, 86400, multi-day samples — tests don't exist yet * Updated `timeCurveSimplePodiumScore.test.ts` / `TimeCurveSimplePodiumSection.test.tsx` expectations * Manual walk of sub-60s / mm:ss / HH:MM:SS / d:HH:MM:SS tiers — requires either Anvil with seeded fixtures or a leader hitting each band naturally **Items not exercised in this session:** * Arena `/timecurve/arena` Time Booster spotlight leader value — not located in the visible Arena layout (STEAL/GUARD/REVENGE surface). Could be sale-state dependent or below the scroll I captured. If it's user-visible somewhere I missed, point me to it and I'll grab. Pre-refactor baseline locked. Ready to re-walk when the refactor lands.
PlasticDigits commented 2026-05-21 12:04:50 +00:00 (Migrated from gitlab.com)

mentioned in commit 472fc892ef

mentioned in commit 472fc892ef35570f3a24fe16c1b859efc06b070c
PlasticDigits commented 2026-05-21 12:04:58 +00:00 (Migrated from gitlab.com)

Implementation complete (main @ 472fc89)

Tiered Time Booster podium duration formatting is shipped on /timecurve (Simple reserve podiums) and /timecurve/arena (podium spotlight leader values).

What changed

  • Added shared formatTimeBoosterPodiumSec (timeBoosterPodiumFormat.ts) with tier boundaries:
    • 0–59 s: locale integer, no s suffix
    • 60 s – 59 m 59 s: mm:ss
    • 1 h – 23 h 59 m 59 s: HH:MM:SS
    • ≥ 24 h: d:HH:MM:SS (e.g. 2:03:13:07)
  • Wired into formatSimplePodiumScoreLine (category 3) and formatPodiumLeaderboardValue (Arena).
  • Zero-second Time Booster slots show Score: — (not Score: 0 added).
  • Documented INV-FRONTEND-228-TIMEBOOSTER-FMT in invariants, timecurve-views, manual QA, guardrails, and ./skills/ index.

Display-only — indexer/API/onchain payloads unchanged.

Automated verification (passed)

  • npm test -- --run timeBoosterPodiumFormat — 12 tier boundary cases
  • npm test -- --run timeCurveSimplePodiumScore — updated expectations + zero-slot guard
  • npm test -- --run TimeCurveSimplePodiumSection — 300 → Score: 05:00 added
  • Full Vitest suite: 547/547 on worktree before merge

Manual verification checklist

  • /timecurve → Prize podiums → Time Booster: scores use tiered clocks (production leaders in the HH:MM:SS band should no longer show raw Nnnns added)
  • Sub-60 s leader: digits only, no s or colons (e.g. Score: 45 added)
  • ≥ 60 s & < 1 h: mm:ss (e.g. 300 s → Score: 05:00 added)
  • ≥ 1 h & < 24 h: HH:MM:SS
  • ≥ 24 h: d:HH:MM:SS
  • /timecurve/arena: Time Booster spotlight leader value matches same formatting (no trailing s)
  • Regression: Last Buy, WarBow BP, Defended Streak copy unchanged
  • Empty/zero slot: Score: —, not Score: 0 added

Docs

@nullvector_ux — please verify on staging/production when convenient. Leaving issue open until manual sign-off.

## Implementation complete (main @ 472fc89) Tiered **Time Booster** podium duration formatting is shipped on **`/timecurve`** (Simple reserve podiums) and **`/timecurve/arena`** (podium spotlight leader values). ### What changed - Added shared **`formatTimeBoosterPodiumSec`** ([`timeBoosterPodiumFormat.ts`](frontend/src/pages/timecurve/timeBoosterPodiumFormat.ts)) with tier boundaries: - **0–59 s:** locale integer, no `s` suffix - **60 s – 59 m 59 s:** `mm:ss` - **1 h – 23 h 59 m 59 s:** `HH:MM:SS` - **≥ 24 h:** `d:HH:MM:SS` (e.g. `2:03:13:07`) - Wired into **`formatSimplePodiumScoreLine`** (category 3) and **`formatPodiumLeaderboardValue`** (Arena). - Zero-second Time Booster slots show **`Score: —`** (not `Score: 0 added`). - Documented **`INV-FRONTEND-228-TIMEBOOSTER-FMT`** in invariants, timecurve-views, manual QA, guardrails, and `./skills/` index. Display-only — indexer/API/onchain payloads unchanged. ### Automated verification (passed) - [x] `npm test -- --run timeBoosterPodiumFormat` — 12 tier boundary cases - [x] `npm test -- --run timeCurveSimplePodiumScore` — updated expectations + zero-slot guard - [x] `npm test -- --run TimeCurveSimplePodiumSection` — `300` → `Score: 05:00 added` - [x] Full Vitest suite: **547/547** on worktree before merge ### Manual verification checklist - [ ] **`/timecurve` → Prize podiums → Time Booster:** scores use tiered clocks (production leaders in the **HH:MM:SS** band should no longer show raw `Nnnns added`) - [ ] **Sub-60 s leader:** digits only, no `s` or colons (e.g. `Score: 45 added`) - [ ] **≥ 60 s & < 1 h:** `mm:ss` (e.g. 300 s → `Score: 05:00 added`) - [ ] **≥ 1 h & < 24 h:** `HH:MM:SS` - [ ] **≥ 24 h:** `d:HH:MM:SS` - [ ] **`/timecurve/arena`:** Time Booster spotlight leader value matches same formatting (no trailing ` s`) - [ ] **Regression:** Last Buy, WarBow BP, Defended Streak copy unchanged - [ ] **Empty/zero slot:** `Score: —`, not `Score: 0 added` ### Docs - [invariants §228](docs/testing/invariants-and-business-logic.md#time-booster-podium-tiered-duration-gitlab-228) - [timecurve-views §228](docs/frontend/timecurve-views.md#time-booster-podium-tiered-duration-gitlab-228) - [manual QA #228](docs/testing/manual-qa-checklists.md#manual-qa-issue-228) @nullvector_ux — please verify on staging/production when convenient. Leaving issue **open** until manual sign-off.
nullvector_ux commented 2026-05-21 14:58:30 +00:00 (Migrated from gitlab.com)

Manual verification checklist @PlasticDigits

  • /timecurve → Prize podiums → Time Booster: scores use tiered clocks (production leaders in the HH:MM:SS band should no longer show raw Nnnns added)
  • Sub-60 s leader: digits only, no s or colons (e.g. Score: 45 added)
  • ≥ 60 s & < 1 h: mm:ss (e.g. 300 s → Score: 05:00 added)
  • ≥ 1 h & < 24 h: HH:MM:SS
  • ≥ 24 h: d:HH:MM:SS
  • /timecurve/arena: Time Booster spotlight leader value matches same formatting (no trailing s)
  • Regression: Last Buy, WarBow BP, Defended Streak copy unchanged
  • Empty/zero slot: Score: —, not Score: 0 added
### Manual verification checklist @PlasticDigits * [x] **`/timecurve` → Prize podiums → Time Booster:** scores use tiered clocks (production leaders in the **HH:MM:SS** band should no longer show raw `Nnnns added`) * [x] **Sub-60 s leader:** digits only, no `s` or colons (e.g. `Score: 45 added`) * [x] **≥ 60 s & \< 1 h:** `mm:ss` (e.g. 300 s → `Score: 05:00 added`) * [x] **≥ 1 h & \< 24 h:** `HH:MM:SS` * [x] **≥ 24 h:** `d:HH:MM:SS` * [x] **`/timecurve/arena`:** Time Booster spotlight leader value matches same formatting (no trailing ` s`) * [x] **Regression:** Last Buy, WarBow BP, Defended Streak copy unchanged * [x] **Empty/zero slot:** `Score: —`, not `Score: 0 added`
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-22 05:40:23 +00:00
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#228
No description provided.