feat: the redesigned Event Clock

The new reader app replaces the old UI: Checklist with a rail (focus
pills, Next to expire, today's dailies, pinned routines), Timeline in a
new and a classic look, Routines, Archive and a Settings area, in a
Structured (default) and a Relaxed interface. It follows the approved
Round 6 B and Round 7 mockups, corrected screen by screen with the owner.

Reader data moves to a v2 store (gacha-tracker:v2:*). Existing v1 data
is migrated on first load, a recovery copy is written first, and the v1
keys are never removed, so a rollback finds them unchanged. Redeem codes
for Genshin arrive as their own feed.

Also: live's urgency ramp, editable per theme; completion and
section-complete fireworks; a returning-user tour; the design mockups,
specs, plans, reviews and handoff record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-18 01:55:16 +02:00
co-authored by Claude Opus 5.5
parent ddd9673f86
commit 50c6e91b00
284 changed files with 45100 additions and 1628 deletions
+53
View File
@@ -364,3 +364,56 @@ other, so `test/update.test.tsx` pins both ends of the `skip-waiting` handshake
cleanly because state is in SQLite.
- **No server-side rendering.** The feed is small and cacheable; a static SPA is enough.
- **No websockets.** Events change on a scale of hours; a 5-minute cache is more than adequate.
## Reader application (2026-10-16 redesign)
`App.tsx` exports `ReaderApp`; only the v2 reader store writes production progress. Legacy hooks
remain as compatibility helpers, validators and tests, not mounted writers. ReaderContent coordinates
shared Checklist/Timeline/Routines/Archive/Settings, account-aware detail adapters and timed archive
transitions. `components/redesign/` contains the feature pages. `redesign.css` scopes Relaxed and
Structured via data-interface, with one application tree and independent theme/density.
`state/reader/` owns migration, schema, reducer, store, clocks, accounts, archive, backups, restore,
imports, routines and event selectors. Operations re-read the persisted envelope under Web Locks;
an IndexedDB readwrite transaction serializes browsers without Web Locks. Failed locking/storage
never publishes an optimistic success. Storage events update other tabs. Field timestamps protect
newer edits and tombstones; account writes rebase changed routine choices. Legacy deltas reconcile
on startup into the original migrated accounts; raw keys remain preserved.
Clock revisions freeze automatic metadata and overrides. Imported historical clocks are excluded
from active clock resolution; history resolves each explicit revision. Display timezone lives in
DisplayTimeContext and never changes the feed.
Events and redeem codes use separate validated static feeds. `build-codes.ts` reads pinned snapshots
or fixtures offline and preserves firstObservedAt in snapshots/codes-first-seen.json. The refresh
runner combines event adapters and count-only code sources while sharing robots, floors, per-host
spacing and snapshot gates. No fake GachaEvents represent codes. Both feeds are network-first in
the worker; manual refresh bypasses the browser cache. Shell update waiting/activation is unchanged.
Backups are outside the reader envelope. Restore validates first, writes a recovery export, prepares
a pending transaction, then replaces live state. Startup finishes an interrupted transaction before
opening the reader. Browser UI restore uses the same cross-tab lock as ordinary edits.
### Ordinary HTTP / WireGuard hosts
Reader identity minting uses `state/reader/ids.ts`: 128-bit `crypto.getRandomValues` tokens for
accounts, operations, reset revisions and backups. The redesign's initial `randomUUID` calls were a
startup bug on ordinary HTTP network hosts; unlike localhost, these are not browser secure contexts,
even when reached through a WireGuard tunnel. Existing IDs are unchanged and valid. Initialization
preserves migration recovery before minting, so the failed first visit did not discard legacy bytes.
Regression verification uses a Chrome test hostname mapped to loopback with isSecureContext false,
randomUUID unavailable and actual getRandomValues/IndexedDB available. Existing-save migration,
new setup, editing, additional servers, reset override/revert, backup and reload all pass there.
### Presentation correction (2026-10-17)
`ChecklistEvent`/`NextDeadline` render the approved list hierarchy; `TimelinePage` owns the shared
axis for event spans and routine periods. `RoutinesPage` groups by reset or account, with native
`RoutineEditor` dialogs. `ArchiveDetail` reads saved archive snapshots and never routes historical
progress through the live event editor. Settings keeps one shared tree for both interfaces, with
separate default-view/custom-content pages and direct game profiles. Structured is the absent-value
default at prepaint, preference normalization, setup and appearance application. No state keys,
event identities or stored clock revisions changed for this presentation correction.
+49 -1
View File
@@ -244,7 +244,10 @@ CREATE TABLE snapshots (
`region_ends` and `payload` hold JSON as TEXT; parse them through the Zod schema on read so a
malformed row surfaces at the boundary rather than deep in the UI.
## Client-side storage
## Legacy client-side storage (v1 compatibility)
Production uses the [active v2 reader envelope](#active-reader-state-v2-2026-10-16). The keys and
merge rules in this section describe migration input and retained compatibility helpers.
Namespaced, versioned, and small. Nothing here ever goes to the server.
@@ -772,3 +775,48 @@ Two timestamp fields distinguish crawler activity from site updates: `lastConfir
the crawler last successfully contacted and verified the source (HTTP 200 or 304 Not Modified), and
`contentChangedAt` records when the served bytes last changed. Both default to `null` so feeds built
before their introduction continue to validate.
## Active reader state (v2, 2026-10-16)
The localStorage/export descriptions above document legacy v1 compatibility. Production mounts
only `ReaderApp` and persists `gacha-tracker:v2:reader`, validated by state/reader/schema.ts.
One stable `account:<id>` represents one tracked game/server; it is browser-local, not a login.
Original event/task/daily IDs remain unchanged inside account data. Account data contains progress,
ignored, and daily/amount maps nested by clock revision. A revision freezes effectiveAt, region,
reset override and automaticBase (offset/hour/minute). Changes append revisions; imported clocks
can be historicalOnly and never replace the local active reset.
The envelope includes accounts, data, archive, seenEvents, prefs, customGames/customEvents/tasks,
codeSeen (per game), codeRedeemed (per account), and eventSeen (per account). Archive/seen keys are
JSON.stringify([accountId,eventId]); archive snapshots retain event, outcome/time, resolved end,
clockRevision, progress, days and amounts. Account/revision cross-references are validated before
restore. Field timestamps and operation IDs rebase writes and preserve deletion tombstones. Unknown
fields pass through exports; progress imports retain opaque root/account extensions in
importedExtensions as well as mergeable known fields. Existing same-server settings remain local.
Migration preserves raw v1 keys in place and in migration recovery, creates one account per game at
the saved region and maps old follows/pins/logs. Newly offered games remain off. Startup compares
legacyRaw with surviving v1 keys to carry older-tab deltas into the original account without
resurrecting newer v2 deletions. Parser aliases carry progress, ticks, amounts and archive/seen IDs.
Keys outside the envelope:
- gacha-tracker:v2:migration-recovery — raw pre-migration bytes.
- gacha-tracker:v2:backups — at most 7 daily + 4 weekly + 6 monthly UTC bucket snapshots.
- gacha-tracker:v2:restore-recovery — the complete pre-restore export.
- gacha-tracker:v2:restore-pending — prepared replacement, resumed before mounting on startup.
Exports use format gacha-tracker-export, version 2, exportedAt, reader and legacy. Progress exports
mark scope progress; full imports preview replacement, progress imports preview a merge. Backups
are never recursively included. Original v1 export envelopes remain convertible.
Prefs add interface (structured/default or relaxed), density (standard/default or compact), scheme
(slate/warm/plum/custom), separate light/dark customPalette, per-game gameColors, timezone
(null/Automatic or IANA), checklist daily placement/expansion and redesignTourSeen. Main view remains
soon or timeline; utility navigation is transient. Future/unreadable records remain exportable even
when this build cannot offer editing controls.
The fidelity correction adds `prefs.routineGroup` (`reset`, the absent-value default, or `game`)
for the Routines page's grouping. It changes no routine IDs or placement keys. Structured is the
interface default as of the owner's 2026-10-17 instruction; explicit saved Relaxed is preserved.
+137 -31
View File
@@ -2,84 +2,84 @@
name: Event Clock
description: Live and upcoming gacha events, sorted by what expires next.
colors:
ground: "#12141c"
surface: "#1b1e2a"
raised: "#232739"
hairline: "#2c3145"
ink: "#e9ebf3"
ground: "#1a1c24"
surface: "#22252f"
raised: "#30333c"
hairline: "#43454c"
ink: "#eeeae3"
ink-strong: "#ffffff"
muted: "#888fa6"
faint: "#5a6178"
muted: "#b1afad"
faint: "#9c9b9b"
scrim: "rgb(18 20 28 / 0.8)"
glow: "#1a1e2c"
calm: "#55607f"
near: "#5b8def"
soon: "#e8b33c"
critical: "#ff4d6a"
ground-light: "#edf0f7"
surface-light: "#ffffff"
raised-light: "#dfe4f0"
hairline-light: "#c9d1e2"
ink-light: "#151824"
near: "#8eb3f4"
soon: "#e9b95b"
critical: "#ff8598"
ground-light: "#f3f1ec"
surface-light: "#fffdf8"
raised-light: "#f0eeea"
hairline-light: "#dddbd8"
ink-light: "#28272d"
ink-strong-light: "#000000"
muted-light: "#535b73"
faint-light: "#767e94"
muted-light: "#69676a"
faint-light: "#6e6d6e"
scrim-light: "rgb(26 31 48 / 0.42)"
glow-light: "#ffffff"
calm-light: "#8a92a8"
near-light: "#2b62c6"
soon-light: "#8a6008"
critical-light: "#c81e3c"
near-light: "#315fa5"
soon-light: "#805a12"
critical-light: "#b12947"
typography:
numeral:
fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "2.75rem"
fontWeight: 700
lineHeight: 1
letterSpacing: "-0.025em"
fontFeature: "\"tnum\" 1"
display:
fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "1.75rem"
fontWeight: 600
lineHeight: 1.15
letterSpacing: "-0.025em"
wordmark:
fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.9375rem"
fontWeight: 700
letterSpacing: "0.02em"
reading:
fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.875rem"
fontWeight: 600
fontFeature: "\"tnum\" 1"
title:
fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.9375rem"
fontWeight: 500
lineHeight: 1.375
body:
fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.875rem"
fontWeight: 400
lineHeight: 1.625
summary:
fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.8125rem"
fontWeight: 400
lineHeight: 1.375
control:
fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.75rem"
fontWeight: 500
label:
fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.6875rem"
fontWeight: 600
letterSpacing: "0.14em"
tag:
fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.625rem"
fontWeight: 500
rounded:
@@ -170,7 +170,11 @@ components:
# Design System: Event Clock
## Overview
The active Relaxed/Structured layout and palette contract is recorded in
[Active redesign system](#active-redesign-system-2026-10-16). The component rationale below describes
the preceding instrument-panel system; use the active section and live token resolver for new work.
## Original component rationale
**Creative North Star: "The Lit Instrument Panel"**
@@ -528,3 +532,105 @@ rather than being toned down.
chooses.
- **Don't** put a second control inside a row, or a drag target on the focus bar or the dailies
strip.
## Shipped redesign system (2026-10-18)
The owner accepted this build as the initial redesign on 2026-10-18, after screen-by-screen review
against the approved mockups (`design/mockups/round6/b-rail.html` for Structured,
`design/mockups/round7/converged.html` for Relaxed) and the corrections recorded round by round in
`docs/handoffs/2026-10-17-redesign-second-rejection-handoff.md`. This section supersedes the original
shell/type/palette/layout descriptions above. Those older sections remain the rationale for the detail,
timeline and uncertainty components that carried over. Overrides live in `src/client/redesign.css`
and `src/client/state/appearance.ts`.
### Shell and layout
- **Interfaces.** Settings → Appearance offers Structured (the Round 6 B rail; the default for new
installs and missing preferences) and Relaxed (Round 7). A saved Relaxed choice stays Relaxed.
Structured uses Barlow Condensed/Barlow and adjoining ruled sections. Relaxed uses Bricolage
Grotesque/Figtree with separated panels. Fonts keep system fallbacks offline.
- **Navigation.** The tabs are Checklist, Timeline, Routines and Archive, with Settings on the right.
The EVENTCLOCK logo returns to the default view. Each page is in the address bar (`#checklist`,
`#settings/games/genshin`, …), and changing tab starts at the top of the page.
- **Width.** The page fills up to 1920px (Relaxed 1840px) and centres beyond that.
- **Two insets on `.reader-ui`.** `--edge` (16px) between content and the page's outer edges.
`--gutter` (24px) where a side column (Checklist rail, Settings nav, Archive sidebar) meets the main
content. At ≤700px, where everything stacks, both are the edge.
- **Checklist rail.** 360px wide (Relaxed 380px; 280/300px at medium widths). Top to bottom: the
focus pills, Next to expire (headline, meter and the next three), Today's dailies, then pinned
routines grouped by game.
### Urgency and colour
- **Two axes, never crossed.** Game hue answers whose event: the 3px strip on the left of each
Standard row, the Compact game cell, the pills. The urgency ramp answers how long: countdowns and
meter ticks.
- **The urgency ramp is live's.** Grey for a week or more, blue under a week, yellow under 3 days, red
under a day: `--color-urgency-{calm,near,soon,critical}`, set by `urgencyTokens`. It is separate
from the accent, so a chosen accent never changes urgency.
- **Reader-editable.** Settings → Appearance → Urgency colours sets each step per theme
(`prefs.urgencyPalette`), with any scheme. Values are nudged only as far as 3:1 contrast, the
large-text bar.
- **Fallback for the live UI.** `URGENCY_COLOR` falls back to the old `--color-*` tokens, so the
live UI is unchanged.
- **Schemes.** Slate, Warm, Plum and Custom shift surfaces. Custom stores ground/surface/text/accent
per theme. Text and accent derive at least 4.5:1 contrast on ground and surface. Game colours are
set per game in Your games.
Default Slate tokens:
| Role | Structured dark | Structured light | Relaxed dark | Relaxed light |
| --- | --- | --- | --- | --- |
| Ground | #0e1117 | #eef0f5 | #1a1c24 | #f3f1ec |
| Surface | #141923 | #ffffff | #22252f | #fffdf8 |
| Primary text | #eeeae3 | #28272d | #eeeae3 | #28272d |
| Accent | #8eb3f4 | #315fa5 | #8eb3f4 | #315fa5 |
### Checklist
- **Heading.** "Running now" sits with the sort and density buttons on one row. Under them is one
line: the count on the left and live's legend on the right ("bars = time left · colour = ends in a
week+ / under a week / under 3 days / under a day").
- **Standard rows.** Game, title, optional description, then the countdown, date and meter, with the
chevron 10px from the date. Every row has a meter, with or without a description; the mockup hid
it on rows without one, and the owner chose consistency.
- **Compact rows.** A ledger with Game / Event / Time left / Ends in columns.
- **"Not started yet."** It opens a second section: a 48px gap, a 2px accent rule and a raised band
with a "Coming up" eyebrow. In Compact its columns read Duration / Starts in.
### Focus pills
They have live's chip feel: 30px tall, 12px text, a 30% hue border unpressed, the full hue with a 14%
wash when pressed. State changes ease over 130ms. Hover adds a hue ring and a 1px lift, a press
scales to 0.96, and games with nothing outstanding dim to 50%. The "Focus" label is a 12px body-face
legend on the same baseline as "Next: …".
### Ticking things off
- **Today's dailies.** The whole row is the target. A tick washes the row in its game's hue, pops the
box and draws the check. A pip bar and "N of M done" sit under the heading. A check-in goal (for
example "5/5 wk") counts as done once met, with a dotted check, and shows its goal count instead of
a streak.
- **Pinned routines** use the same card language. A box ticks a check-in routine, or completes an
amount routine in one click. "+1" steps an amount, and the count opens an editor. Typing a value at
or over the target completes it after a 450ms pause.
- **Completion animation.** The bar fills, the row warms, then the box pops. Unticking undoes it:
the amount from before goes back, or the editor opens when that value isn't known.
- **Celebration.** A single completion fires live's small `Fireworks` in the row. Finishing a whole
section (every daily, or every routine in a game's group) fires `Fireworks grand`: seven bursts in
two waves with the game's hue mixed in, a light sweep, and the "All done" line popping.
- **When it fires.** Only an action taken now that completes the item or section, never arriving
complete. Reduced motion removes all of it.
### Other pages
- **Routines.** Bands by reset (Dailies, Weeklies, Monthlies, Cycles) or by game. The table has tick
and click-to-edit amount cells. The library and create/edit drawers are shelves.
- **Timeline.** Two looks (new and classic) chosen in Appearance. Dailies are not on the timeline.
- **Settings.** About, Changelog, Data & sources (list options first), Region & time, Appearance
(interface, theme, scheme, urgency colours, default view, game order, timeline design, focus bar,
checklist group), Your own events and games, Backup (tiers plus a manual "Your backups" list), and
per-game Setup / Codes / About.
- **Returning-user tour.** Routines → Checklist → Timeline → Redeem codes → Archive → Settings.
Continue stays in a fixed place, Back sits beside it, and Skip is apart. "Replay the introduction"
starts it at step 1.
+19
View File
@@ -640,3 +640,22 @@ fixture is the regression test proving the parser still handles the previous for
guessing, impossible calendar dates are rejected, ranges crossing New Year roll the start year back,
and abbreviated months parse. `test/merge.test.ts` covers cross-source agreement, disagreement, and
rerun disambiguation. These are the last line of defense before a wrong date reaches a user.
## Redeem codes source (2026-10-16)
Genshin codes use https://game8.co/games/Genshin-Impact/archives/304759, separately from events.
One descriptive-agent robots request and one page request returned HTTP 200 during capture; no
access workaround or repeated source fetch was used. The raw page and capture metadata are pinned
as fixtures/genshin/game8-codes-2026-10-16.*, with ten independently counted current codes. The
parser reads actual code inputs/current reward rows and excludes expired programme sections; a
future-dated programme fixture variant verifies that still-current programmes are admitted.
Unannounced expiry remains null. A printed calendar date is expiresOn, never a fabricated exact
expiresAt instant. Attribution is retained on every code.
src/ingest/codes/registry.ts registers a count-only refresh source. The existing runner shares robots,
six-hour floors, conditional requests, per-host spacing and snapshot gates across event/code sources.
Event adapter schemas are unchanged. scripts/build-codes.ts builds data/codes.v1.json offline from
snapshots or fixtures and preserves observation timestamps in snapshots/codes-first-seen.json.
Code newness is per game; redemption is per local game/server account and never clears newness by
itself. No code row is synthesized into the event feed.
+81 -103
View File
@@ -111,14 +111,9 @@ few pixels apart the board is being asked the one question it exists to answer a
reader to squint. It also gives a one-day event a bar to read and to tap rather than the 34px
minimum every short bar collapses to.
**What has not started yet is shown to a new reader, and either can switch it** — `prefs.showUpcoming`,
which governs **both views**: the board plots them, and the checklist keeps its "Not started yet"
section (F2). One question, asked once, because they are the same events. It was off by default,
because this app answers *what expires next* and a reader with fourteen lanes has a next patch queued
behind every one of them. Since 2026-10-13 (repository owner) first-run setup turns it **on**: a new
reader should see what is coming before deciding they do not want it. The stored default is still
`false`, so an existing install whose saved prefs predate the field keeps today's answer rather than
having it switched on underneath its reader.
**Upcoming events are optional and off initially.** New setup asks “Show events that haven't
started” under “What do you want to track?”. The same saved answer governs Checklist and Timeline;
Checklist separates them under “Upcoming events”. Existing saved choices survive migration.
On the board that is structural as well as editorial: the window is drawn from what is plotted, so
showing the future stretches the right edge weeks past today and squeezes the running bars the
@@ -129,10 +124,8 @@ quiet fortnight, and this app does not leave a reader to infer what it is not sh
page header states `N live · N upcoming` whatever the setting says, and either view left with
nothing to show says so in words and names the setting rather than rendering an empty column.
The switch is in settings (F4), with the two other answers to *what am I allowed to look at* —
`showCompleted` and `showIgnored` — and **not** in the board's own header beside the stacking and
scale controls. That is the line those two draw: they reshape what is already on the board, which is
why they are reached for while reading it, and this one decides what is on it at all.
The switch lives in Settings → Lists, because it decides which events enter both main views.
Done and ignored events live in Archive; Lists has no redundant Archive link or reveal switches.
**Switched on, the board says in words where they begin.** A bar drawn to the right of the "now"
rule is only *implicitly* in the future, and implicitly is not a standard this product holds itself
@@ -198,42 +191,17 @@ an explicit "show all N". This truncates the *view* only: the order is untouched
the cut are still counted in the header, still on the timeline, and one tap away.
**F3 — Mark completed.**
A toggle on every event, in both views. State is written to `localStorage` immediately and
optimistically — there is no server round trip and no failure case. Completed events stay visible
but de-emphasized; a filter toggles them out entirely.
Done writes durable progress and a metadata snapshot immediately. A confirmation remains for about
1.5 seconds before the row fades/collapses into Archive. Undo is available for eight seconds;
Archive Restore remains available afterwards. A failed write is disclosed and never presented as
saved. Reduced motion removes the fade.
**F4 — Filters.**
Filter by game (multi-select, persisted) and by event type. Hiding a game hides it from both views.
Preferences persist in `localStorage`.
The panel holding them is **six collapsed groups, each stating its own answer on the summary line**
— games, server region, appearance, what you see, your own games and events, your progress. It was
one open block of everything, laid out in two columns on a wide screen, which read fine at four games
and stopped working at eighteen: the game list alone is eighteen rows of four controls, and it sat
above the checkbox a reader had come down here to tick. Collapsing on its own would only trade that
friction for another, though — a group that shows nothing but its name turns *what is my region set
to?* into a click — so each summary answers its group's question, and the closed panel is a six-line
report of how the app is configured. Opening one is for changing an answer, not for reading it.
Three things follow. **The groups ship closed**, including the one holding the filters that other
copy points at: the empty states name both the switch and the group it is in, which is more findable
than a checkbox in a wall was. **The summary states are derived, never stored** — they are a reading
of `prefs`, so they cannot disagree with the controls inside. And **a group answers one question**.
The region and the theme began as one group called *Reading*, on the argument that both are "how do I
read this?" — which summarised as `Europe · Dark`, two unrelated answers joined by a dot, under a
name for neither. The region is not a reading preference: it is which server the reader's account is
on, and region-scoped ends, undated deadlines and every daily streak are read off that server's
clock, so it is the one control in this panel that can make a countdown wrong. It gets its own line,
and says as much when opened.
**The panel uses the page's width**, like every other block on it. The rows were held to a narrower
measure on the argument that a name and its state stop reading as one line across a wide screen, but
the panel's own rule spans the shell, so the rules inside it stopped a third of the column short and
the panel became the one thing on a desktop page not using the width — sitting between a two-column
checklist and a three-column footer that both do. A title at one end of a full-width row and its
countdown at the other is what "running now" does directly above it. Prose inside a group keeps a
readable measure, and the game list takes two columns of its own on a wide screen once there are nine
or more games, because eighteen rows in one column is a thousand pixels of ribbon down the left.
**F4 — Settings and visibility.**
Settings has named pages: General, Your games, Appearance, Lists, Region & time, Data & sources,
Backup, About Event Clock, and Changelog. Your games controls visibility/order, tracking choices,
servers, reset overrides and colours; Lists controls upcoming events and timeline grouping.
Routines owns follows and independent Checklist/Timeline pins. Utility navigation does not replace
the saved default main view. Empty states name where to change the relevant choice.
**F4a — Focus one game at a time.**
Switching games on and off says *which games the reader plays*, and is set once. It is the wrong
@@ -253,15 +221,26 @@ has left the feed is ignored rather than obeyed, so it can never strand the read
whose cause is elsewhere. Each chip carries that game's outstanding count, so a game with nothing
waiting says so before it is visited.
**F5 — Region selection.**
A user picks Asia / America / Europe once. For events where `regionScoped` is true, all displayed
end times resolve to that region's server reset. This is stored in `localStorage` and defaults to a
guess from the browser timezone, shown as a dismissible "showing America server times — change".
**F5 — Servers and display timezone.**
Default server is chosen in setup and Region & time. Each locally tracked game/server has its own
stable account identity and progress. “Add another server” is for simultaneously playing multiple
servers: it starts fresh tracking choices and never clones marks. Server/reset changes append clock
revisions so history stays on its original clock. Overrides can revert to Automatic. Display timezone
is Automatic by default or a searchable IANA zone; it changes printed dates and manual date entry,
never a source's exact timestamps or game-day keys.
**F6 — Export / import.**
Because there are no accounts, moving between devices is manual: download a JSON file of completed
IDs and preferences, upload it elsewhere. Import merges rather than replaces, and never removes a
completion the user already has.
**F6 — Export / import and backup.**
No login is involved. Export progress / Import progress merges per game/server, retaining local
preferences and active reset configuration. Day logs union; newer records/amounts win conflicts.
Imported clock revisions remain accessible as historical clocks. Unknown extensions are retained in
the envelope. Export all / Import all restores the complete validated envelope through a preview,
with a recovery copy before replacement. Undo last restore reverses it. Version-1 exports remain
convertible; original v1 browser keys are retained after migration.
Local backup history keeps seven daily, four weekly and six monthly snapshots while the app is
used. UTC buckets keep their latest snapshot; backups never contain other backups. View backup
opens the preview; Restore this backup is the separate committing action. Storage failures are
visible. Browser deletion removes local history; export is the way to keep an external copy.
**F12 — Record your own progress and effort.**
Three states, not two: untouched, doing it, done. Plus an optional effort estimate — quick, short,
@@ -418,15 +397,16 @@ the one after it — because they answer "what ends soonest". The timeline draws
every occurrence in view, because it answers "what is the rhythm".
**F8 — First-run setup.**
Before any events are shown, the reader picks which games they play, and how they want to read
them. A calendar full of games they
don't play is worse than an empty one — it buries the thing they came for. Nothing is preselected
and the button stays disabled until something is chosen; guessing on their behalf and hoping they
notice is worse than asking. **The games are listed alphabetically by name**, not in feed order:
everywhere else a lane's position carries meaning, but here the reader is scanning for the two or
three names they already know, and a list ordered by which game happened to hold the first event row
gives them nothing to scan by. The choice is stored as *hidden* games, the inverse — which is a storage
shape, not a policy: what happens to a game added later is decided separately, below.
Before events are shown, a four-step wizard asks for games, default server and preferred appearance,
tracking kinds, and the default main view. Games are alphabetical, nothing selected. Events are the
only initially selected tracking kind. Dailies, weekly/monthly and cycles have short descriptions;
unavailable kinds remain selectable and say nothing is currently detected. Upcoming visibility
belongs to that tracking step and starts off. Server and appearance can be changed later.
Returning readers get a one-time introduction and optional four-step tour. Skipping or finishing
preserves progress and the saved main view; General can replay it.
**A game added later arrives switched off.** Adding a source is our decision, not the reader's, and
someone who plays two games did not ask for the other twelve; a calendar that fills itself up is the
@@ -447,10 +427,13 @@ The view question (F2) sits under it, with each option drawn rather than only de
it arrives already answered, and the screen says where to change it afterwards: the tabs are small
text in a corner, which is the one control a first-time reader will not find on their own.
**F9 — Ignore an event.**
Distinct from completing one. "Done" keeps an event visible and counted; "not interested" removes it
from both views entirely. Ignored events stay recoverable: a count and a reveal toggle appear in
settings once there is something to reveal.
**F9 — Ignore and Archive.**
Ignore saves immediately and follows the same confirmation/Undo transition as Done. Both move to
Archive with event metadata, notes, goals, logs, resolved end and clock revision. Previously seen
followed events that expire unfinished also enter Archive; unknown-end events never expire by a
guessed date. Per-game history is default, with an alternate chronological history. Story statistics
include completion, check-ins and shortfalls where known; ignored events are excluded from success
rates. Restore recovers saved progress without pretending an expired event is live.
**F10 — Works offline.**
The reader's question is answered entirely by data already on the device, and countdowns run off the
@@ -459,9 +442,9 @@ the last feed it downloaded. Offline is disclosed in the header and above the fo
data must never be presented as current.
**F11 — Credit and disclaimer.**
The sources that compile these calendars, and the studios that make the games, are named on the same
screen as the data rather than one navigation step away. The page states plainly that it is
unofficial and unaffiliated, and that the source page is the authority when the two disagree.
Settings → About Event Clock preserves the full colophon, source credits, unofficial/unaffiliated
disclaimer and feedback links. The footer links About. Each game's About lists the real data sources,
links and thanks; event detail links its authoritative source.
**F14 — Say when a new version of the app is ready.**
F10 caches the shell so the app survives losing signal, and the same cache is why a reader who never
@@ -483,42 +466,29 @@ arrive without a reload, and calling that a new version would train readers to d
unread.
**F7 — Freshness disclosure.**
The footer shows when the feed was last updated, per game. If a game's data is more than 48 hours
stale, its lane carries a warning badge. Never present stale data as current — the whole value
proposition is trust in the dates.
The footer always states data age, never build age. Only tracked games with stale data produce a
compact “Event data may be out of date” row in the shared updates area. New events and new codes
remain distinct notices. Data & sources and event detail expose the last successful check, last
content change, source links and recorded reasons. A successful 304 confirms a check without
claiming the wiki changed.
**The reader can ask for the newest data.** A reload is not enough — the browser may keep the feed
for minutes — so *Check for new data* beside the age asks the server past that copy and swaps a newer
feed in without a reload. It says what happened: updated, nothing newer published yet (sources are
read about twice a day, and the button cannot make that sooner), or the server could not be reached.
"Newer" is judged on events and source health, never on the build stamp. Disabled offline; nothing
polls on its own.
Check for new data revalidates event and code feeds, compares their content rather than generatedAt,
and reports updated, unchanged or failure. It does not scrape source sites from the browser.
**The staleness notice names only the games the reader has switched on, and says that it does.**
The headline age is about the feed and stays whole; the notice under it is an *instruction*, and the
only remedy it offers is "go and check that game's source page" — which a reader cannot act on for a
game they turned off and cannot see a single row of. With nineteen lanes and two switched on, naming
eleven they do not play buries the one they do, and a warning about games nobody reads is a warning
nobody reads (F8: a game we add is a game they never asked for, and arrives switched off, so an
untouched install would be warned about the whole catalogue).
**F15 — Appearance.**
Relaxed (Round 7) and Structured (Round 6 B rail) share one component/state tree. Structured is the default (owner decision, 2026-10-17); an explicitly saved Relaxed preference is preserved.
Interface choice, Standard/Compact density, Dark/Light/System theme and Slate/Warm/Plum/Custom
scheme are independent. Custom stores ground/surface/text/accent separately for light and dark;
rendered colours derive readable contrast without overwriting the saved choices. Per-game colours
have a picker, hex field, preview and reset. Checklist dailies can occupy a separate direct-tick panel
or grouped routines, with collapsed/expanded/always-expanded policies; these controls also live in
Routines. Numeric routines use native step-one inputs.
Both halves of that are load-bearing. Scoping it silently would be the worse half: "nothing has
refreshed" is a claim about the whole calendar, so a reader with fourteen of eighteen lanes off would
read a sentence about four as one about all of them. The notice therefore states whose games it
counted, in the same words the empty list uses — *the games you have switched on*. Credit in the
column beside it is owed to every source we read and is not scoped; nor is the "last refreshed" line,
which reports the feed and feeds the bug form.
**The staleness notice differentiates crawler checks from site updates.** If our crawler successfully
checked the wiki recently (e.g. confirming HTTP 304) but the site's content has not changed in over
two days, the notice explicitly distinguishes the two: "data pulled X ago, site updated Y ago", with
an expandable breakdown detailing each source's check and content update times.
**F15 — Light mode, with dark still the default.**
The earlier theme rationale below still applies to both interfaces.
The app is a lit instrument panel and that is what it should be on first sight, but it is also read
on a train in daylight and by people who find a dark UI harder rather than moodier. A public fork
worked a theme toggle out first and is credited for the idea in the colophon. So the ground is
the reader's choice: **Dark**, **Light**, or **System** to follow the device, in settings next to the
the reader's choice: **Dark**, **Light**, or **System** to follow the device, in Settings → Appearance, separately from the
region — both are "how do I read this?" and neither changes what the page knows.
Dark is what it ships as, and deliberately not `System`. A reader whose laptop is in light mode has
@@ -571,7 +541,15 @@ placed. It also arrives switched off (F8), so settings is where they meet it, wh
would move it anyway. **A game's dailies stay together** — a repeating event the reader marked sits
with the standing chore of the game it came from, because the reader thinks in games and not in kinds.
**F17 — Weekly and monthly tasks.**
**F17 — Routines.**
Routines is the library and management page for daily, weekly, monthly and cycle tasks. Follow each
per server; pin independently to Checklist and Timeline. Built-ins offer Make it mine, copying only
that account's logs; custom routines remain editable. Catch up & recorded history exposes logs on
their original reset revisions. A separate Timeline reset strip sits directly after its game's events;
with strips off, Ending soonest remains a single cross-game deadline queue.
The cadence and goal semantics below remain the domain contract; the redesign moves their controls
from the former Your own section to Routines.
Some chores roll weekly or monthly rather than daily — earning ZZZ's weekly activity points,
finishing Endfield's weekly routine — and no source publishes them, so they were nowhere in the app.
A reader can now set them up per game in *Your own*: a title, every week (from a weekday they pick),
@@ -624,7 +602,7 @@ instant on every server rolls on that server's reset for every reader.
The app now says what changed, in two tiers, both tied to git. **Short entries** — one line each,
tagged New, Fixed, or Games & sources — appear once as *What's new*, above the lists, after a new
version loads; *Got it* dismisses them. **Release notes** — a few paragraphs per feature or
milestone, summarised from the history and the design specs — are in the footer's *Changelog*, each
milestone, summarised from the history and the design specs — are in Settings → *Changelog*, each
linked to the commits it covers and the specs it draws on. Data refreshes are not changes in this
sense; the footer already states the data's age.
+8 -7
View File
@@ -71,7 +71,7 @@ away for more coverage (see Product Principles).
## Capabilities and Constraints
**Capabilities.** The full feature set is specified as F1–F16 in `docs/PRD.md`:
**Capabilities.** The full feature set is specified as F1–F18 in `docs/PRD.md`:
- A timeline: one lane per game, or a single queue sorted by soonest end.
- A checklist of everything running, sorted by soonest end.
@@ -79,16 +79,17 @@ away for more coverage (see Product Principles).
- Daily checklists, with past days that can still be filled in.
- Ignoring an event, and filters by game or type.
- Focusing on one game at a time.
- Region selection.
- JSON export and import.
- Separate game/server tracking, reset corrections and Automatic or named display timezone.
- JSON export/import, recoverable restore and 7 daily / 4 weekly / 6 monthly local backups.
- The reader's own games and events, one-off or repeating.
- First-run setup.
- Events-first new setup and a one-time returning-reader introduction.
- Offline use.
- A notice when a new version of the app is ready.
- Dark or light mode, plus a "follow the system" option.
- The reader's own game order.
- The reader's own game order, per-game colours, Relaxed/Structured interfaces and custom schemes.
- A Routines library with independent view pins, an Archive with story statistics, and sourced redeem codes.
The app currently covers twenty games from twenty-two sources.
The app covers twenty games; source registries are the authoritative current count.
**Hard constraints** (`AGENTS.md` § Three constraints):
@@ -112,7 +113,7 @@ The app currently covers twenty games from twenty-two sources.
**Terminology:**
- The user is a **reader**.
- The two views are **Checklist** (stored id `"soon"`) and **Timeline**.
- The main views are **Checklist** (stored id `"soon"`) and **Timeline**; Routines, Archive and Settings are utility pages.
- An event's states are **untouched**, **doing it** and **done**.
- **Ignore** is not the same as done.
- A **lane** is one game's row or source.
+19
View File
@@ -976,3 +976,22 @@ Facts stated above that a future reader may want to re-check, with how they were
| `marisaimpact.com` states no year on any calendar | stripped tags from `/calendar89` and `/calendar90` and grepped the text for `20\d\d` — zero matches on either |
| Its calendar URL is per-version, with no stable alias | `/calendar90` is `200` and covers `Aug 20 - Oct 22`; `/valk/calendar90` — the scheme its own `og:url` uses — is `404`; the nav links only `/calendar88` and `/calendar89` |
| Its schedule is not behind an API | `GET /calendar90/__data.json` returns 144 bytes: an `ogImage` URL and nothing else |
## Redeem codes source (2026-10-16)
Genshin codes use https://game8.co/games/Genshin-Impact/archives/304759, separately from events.
One descriptive-agent robots request and one page request returned HTTP 200 during capture; no
access workaround or repeated source fetch was used. The raw page and capture metadata are pinned
as fixtures/genshin/game8-codes-2026-10-16.*, with ten independently counted current codes. The
parser reads actual code inputs/current reward rows and excludes expired programme sections; a
future-dated programme fixture variant verifies that still-current programmes are admitted.
Unannounced expiry remains null. A printed calendar date is expiresOn, never a fabricated exact
expiresAt instant. Attribution is retained on every code.
src/ingest/codes/registry.ts registers a count-only refresh source. The existing runner shares robots,
six-hour floors, conditional requests, per-host spacing and snapshot gates across event/code sources.
Event adapter schemas are unchanged. scripts/build-codes.ts builds data/codes.v1.json offline from
snapshots or fixtures and preserves observation timestamps in snapshots/codes-first-seen.json.
Code newness is per game; redemption is per local game/server account and never clears newness by
itself. No code row is synthesized into the event feed.
@@ -0,0 +1,125 @@
# Approved redesign — implementation review, 2026-10-16
**Status: rejected by the owner for substantial divergence from the approved Round 7 markup.**
The completion claims below describe the earlier implementation report and are withdrawn as
evidence of design acceptance. Functional tests and overflow checks did not establish visual or
interaction fidelity. A faithful markup-to-component correction is still required.
The approved [spec](../superpowers/specs/2026-10-16-ui-redesign-design.md) and
[16-task plan](../superpowers/plans/2026-10-16-ui-redesign.md) are implemented in the working tree.
Execution was Native. Nothing has been committed, pushed or published.
## Delivered
Relaxed (Round 7, default) and Structured (Round 6 B rail) share one reader/component tree; Appearance
switches between them independently of theme, palette and Standard/Compact density. The redesign
includes Routines and view pins, direct daily ticks, configurable grouping, multi-server progress,
reset override/revert with accessible clock history, game colours/source credits, custom palettes,
Automatic/IANA timezone, new and returning onboarding, compact source/new-event/code notices,
Archive/story statistics, timed Done/Ignore/Undo, local rolling backups and recoverable restore.
Settings About preserves the colophon; Changelog has its own page and no footer link.
The codes feed contains ten real Genshin codes from the pinned Game8 capture. Source fetching remains
deterministic and shares the existing conduct gates; no additional community requests were needed
while implementing or reviewing. Unstated expiry stays unknown. Codes newness and per-server
redemption are separate. Manual refresh checks both feeds and discloses partial code-feed failure.
## Verification
- **1,587 tests pass**, zero failures, across 74 files. Strict TypeScript and whitespace checks pass.
- The suite also runs without `public/` or `docs/` in the Docker COPY layout: 1,586 pass, one
intentional README-only skip, zero failures. No tests depend on a preceding build.
- Full offline build passes: 209 events across 20 games, ten codes, production JS/CSS/static shell.
Final shell build `f5b25675010f` was loaded on a fresh origin (port 41880).
- Chrome checks cover both interfaces, both themes, 1440/390 widths, Standard/Compact, reduced motion
and an effective 200% CSS viewport. No horizontal page overflow or page errors in that matrix.
- Browser journeys pass for migration, saved preferred Timeline, events-only setup, selectable empty
tracking kinds, upcoming placement, isolation from unselected games, extra server with fresh state,
direct ticks, reset override/revert/history, per-server redemption, Done/Ignore/Undo, unknown-end
custom creation, real export download/upload/restore, backup preview/restore/Undo, custom palette,
timezone selection/search, offline reload and the real service-worker update/reload handshake.
- Two real browser tabs save concurrently with Web Locks explicitly disabled: IndexedDB serialization
preserves both edits. Unit regressions cover stale progress fields and independent routine pins.
- A fresh independent reviewer completed whole-change review and focused re-reviews. All important
findings were fixed with reproductions; final review reports no remaining important findings.
## Review fixes and durable decisions
Deleted custom definitions no longer reappear from seen snapshots. Progress imports preserve local
active reset configuration, retain imported clocks as history, and reuse those revisions on repeated
imports. Opaque extensions survive subsequent transfers without duplicate accumulation. Timeline
focus applies to routines as well as events; Ending soonest uses one global queue when reset strips
are off. The browser fallback uses an IndexedDB transaction rather than unlocked whole-envelope
writes. Account rebase preserves independent routine/pin edits and explicit retirement removal.
Original v1 bytes remain available after migration. Restore validates account/clock references and
saves recovery before replacement. Setup completes before seen-history capture. Source diagnostics
state what the feed records; absent failure reasons are described as unavailable, never guessed.
## Practical limits
Backups are browser-local and created while the app is used; they cannot recover storage a reader
clears. Older completion IDs whose metadata was never retained remain recoverable raw data, but
cannot produce invented archive descriptions or historical statistics. Codes currently have one
registered game/source. External fonts have system fallbacks offline. Calendar-date code expiry is
shown as a date without an invented instant. No accounts, remote sync or push notifications added.
## Captures inspected
[Relaxed, light, desktop](assets/2026-10-16-redesign/relaxed-light-1440.png) ·
[Structured, dark, desktop](assets/2026-10-16-redesign/structured-dark-1440.png) ·
[Structured, dark, phone](assets/2026-10-16-redesign/structured-dark-390.png)
Additional transient browser logs and captures are in `/tmp/redesign-browser-*` and
`/tmp/redesign-final-*`; the facts above and representative images are recorded here for handoff.
## Follow-up: first-visit HTTP/WireGuard startup failure
The owner reported the recovery screen on first visit through WireGuard `10.20.20.2`. The precise
failure reproduced in Chrome at an ordinary HTTP hostname: `crypto.randomUUID` was undefined,
initial account minting threw, and initialization returned the generic unavailable/saved-data error.
Localhost-only verification had missed this because localhost is a browser secure context.
All reader ID consumers now use 128-bit `crypto.getRandomValues` through `state/reader/ids.ts`.
Three new regressions failed before the fix and pass afterwards. Browser verification on an actual
insecure HTTP origin passes both existing-save migration and fresh setup, ordinary edits, adding a
server, reset override/revert, backups and reload; the original v1 bytes remain unchanged. Focused
independent review found no important issues. The full suite now passes **1,590 tests** across 75
files. Typecheck, production rebuild and whitespace checks pass. Rebuilt shell: `9752bd6bf1d2`.
The existing preview serves the rebuilt files; hard-refreshing bypasses the browser's cached JS.
No storage deletion or identity remapping was performed.
## Owner rejection: markup fidelity
Direct comparison confirms substantial divergence despite matching outer gutters/panels/fonts:
- Round 7's event-entry structure separates event content, a large deadline with supporting text,
and chevron. Production reused EventRow's prior internal structure and decorated it with CSS.
- Round 7's Routines uses game bands and aligned columns for progress, cycle/reset, pins and menus.
Production renders a flattened library with checkbox options and no equivalent table structure.
- Round 7's settings sidebar includes individual game entries with descriptions, leading into their
profiles. Production substitutes a generic Your games page and game selector.
- Round 7's Timeline places routine periods/progress/reset markers on the shared time axis.
Production appends ordinary routine controls below event timelines and uses textual reset strips.
- Round 7's Archive story includes interactive per-game outcome breakdowns. Production substitutes
per-game text totals.
- The approved header has navigation counts and a dedicated Settings placement. Production uses
the simpler generic navigation and a different header summary.
References: design/mockups/round7/converged.html (final CSS from line 1003 onward and final renderer
overrides); production ReaderApp.tsx, EventRow.tsx, TimelinePage.tsx, RoutinesPage.tsx,
SettingsLayout.tsx, GameSettings.tsx, ArchiveStory.tsx. Earlier render functions/CSS in the mockup
are superseded by later overrides and must not be used in isolation.
Root delivery error: the implementation preserved/reused existing presentation components and
added selected reference styling instead of faithfully translating the approved rendered markup.
Verification checked functionality and overflow without comparing equivalent reference/production
screens and interactions. The final review concentrated on data preservation and did not close that
design-fidelity gap. The owner should not have had to identify it after a completion claim.
No implementation edits or data resets were made during this rejection audit. Existing storage and
HTTP fixes remain in place. Required correction: use the approved rendered markup, hierarchy and
interactions as the presentation contract, then connect real reader data and compare matching states.
@@ -0,0 +1,167 @@
# Routine redesign: Round 4 review
This records the owner's decisions from three mockup rounds and the subsequent review on
2026-10-16. These are proposed product changes, **not shipped behaviour**. The implementation spec
and migration plan come after review of this combined prototype.
## Review artifact
Open [the standalone Round 4 prototype](../../design/mockups/round4/converged.html) in a browser.
The review copy is also placed in `~/gacha-mockups/round4/converged.html`, linked from the existing
Samba index. Earlier rounds remain intact there.
The prototype extends the Round 3 archive variant, keeping its ruled timetable visual language and
routine library. It is deliberately separate from the production app. Its storage namespace is
`event-clock:mockup:round4:v1`, never a production `gacha-tracker` key. Reset demo restores sample
state; reloading otherwise keeps density, choices and progress where browser storage is available.
## Decisions to preserve
- Checklist and Timeline retain their names. Routines and Archive have their own tabs, as does
Settings. Settings uses a sidebar with General first.
- Routines owns follows, individual editing and independent Checklist/Timeline pins. Built-ins do
not appear under Your own events and games. The library puts Create your own routine first.
- Checklist keeps compact per-game routine summaries in its rail. Focusing a game expands its
routines; All collapses them. Remove the unrelated Your own events callout from the rail.
- The main event list has two densities: Standard by default, with wrapping titles, breathing room
and supporting copy, and Compact using the dense table. Density is remembered and independent of
routine pins, filters, sort order and Timeline. Both densities open the same event detail.
- Game setup offers composable Events / Dailies / Weeklies & monthlies / Cycles shortcuts; individual
follows remain editable in Routines. Events is a visibility choice, rather than a routine.
- Each game can have multiple servers, with separate routine and daily progress. An additional
server starts with events only and fresh routine choices; it does not copy the first server's
follows, pins or ticks.
- Onboarding begins with events only. Explain each type in plain language, especially cycles:
routines following the game's own cadence, such as a mode resetting every fourteen days. Ask
whether Checklist or Timeline should open first. Defer pin placement until readers have seen the
views.
- Returning readers receive an introduction to the new layout in either main view, with existing
choices and progress preserved. Changelog remains accessible afterward.
- Codes and About live beside Setup in the game's settings area. In-app notices appear in both
main views. New means discovered since the reader last viewed that game's codes, not unredeemed.
Viewing clears the new indicator without marking redemption. Redemption is separate per server.
- Archive defaults to per-game groups, with the ledger's story panel and small statistics above
them. History timeline is an additional view. Records remain in the save, with description,
source and progress available after the live source changes.
- Done and Ignore immediately save an archive record, then retain the active row for about 1.5
seconds with Completed/Ignored · Moved to Archive before fading and closing the gap. Undo remains
available afterward; Restore also lives in Archive. Reduced motion keeps the confirmation delay
and removes the fade. Unknown end dates stay unknown in archived records.
- Expired unfinished events belong in Archive as unfinished, rather than silently disappearing.
- Timeline uses full rows for routines. It offers Ending soonest and By game. Separate reset strips
are optional and sit directly below their own game's events on the same axis. In separate-strip
mode, Ending soonest orders game groups by their next deadline to preserve this adjacency.
## What to review
1. Compare the same 21 events in Standard and Compact; focus Genshin and inspect its routine rail.
2. Complete or ignore an event, observe confirmation/fade, Undo, then open its saved Archive detail
and Restore it. Try an event with no announced end.
3. Follow/unfollow a routine from the library and pin it independently to each view.
4. Add a server in game settings and confirm the other server's choices and ticks stay separate.
5. Open Codes, mark one redeemed on a single server, and simulate a new arrival from the demo bar.
6. Compare the per-game Archive with History timeline, and check both timeline reset layouts.
7. Walk through New user, then Returning user with Timeline as the default.
## Owner's feedback after Round 4
The combined prototype was received positively. Further requirements:
- Restore the richer statistics from Round 3 H's Ledger with a story. Round 4 reduced that panel
to completed/ignored/unfinished counts, which lost what the owner liked. Keep the per-game
archive as the default body, but restore the completion percentage for the current month with
an overall comparison, current/best completion run, logged event check-in days, and unfinished
events with near-miss detail. Retain the compact per-game outcome breakdown from that panel.
Ignored events must not be counted as failures in the completion percentage. Definitions and
scope labels must be explicit: recorded archive history is not all play history, event check-in
totals are not unique calendar days, and incomplete/unknown progress cannot support a fabricated
near-miss claim. These statistics should use saved progress and dates, not invented telemetry.
- Keep the colophon, potentially in Settings, and add an app-level About section distinct from a
game's About tab. Preserve source/studio credits, author and contributor acknowledgements and
useful project links. Moving credits must not hide the feed-age and stale-source disclosures
that readers need while looking at deadlines.
- The grouped routine summaries are easier to scan but add friction to ticking daily chores.
Explore a separate daily panel with directly available one-click ticks, selectable as an
alternative to grouping dailies with the other routines. Keep server progress separate and
avoid showing the same tick in both panels. Placement and default are not yet settled.
- Appearance should support colour schemes in addition to dark/light/system. The choice between
curated palettes and reader-edited colours remains open. Any scheme must preserve game identity,
urgency meanings, and readable contrast in both modes.
- Each game's Settings → Setup includes a custom game-colour control, a preview and Reset to
default. Remember the override and apply it consistently wherever that game appears, including
Checklist, Timeline, Routines and Archive. It belongs to the game, shared across its servers,
separately from the global interface colour scheme. Use the existing readable-hue approach to
preserve legibility in dark and light appearances without changing the reader's saved base
colour. Game colour expresses identity; urgency colours retain their meaning. Export/import
must retain these overrides in the eventual implementation.
- Clarify the additional-server action as simultaneous account tracking. Use “Track another
server” with helper text: “Play on more than one server? Add your other server to track both
accounts separately.” Keep changing the existing server as a distinct control. The added-server
setup still starts with fresh routine choices and separate progress, as previously agreed.
- Standard event rows without descriptions currently feel too empty. Size rows to their actual
content: reserve no description line when absent, reduce vertical padding for title-only rows,
and keep the countdown/meter stack from imposing a description-sized minimum height. Preserve
wrapping titles and comfortable tap targets; Standard remains roomier than Compact without
forcing every event to the same tall height. Do not invent fallback description text to fill
the space.
- Region & time must let the reader select their display timezone. Automatic, using the browser's
timezone, is the default; a manual selection is remembered and can be returned to Automatic.
Display named IANA zones with their current UTC offsets, so daylight-saving changes follow the
selected zone rather than a fixed offset. This affects displayed dates/times, not the server
clocks that determine deadlines, reset boundaries and stored game-day keys. Reader-authored
event entry must explicitly identify the timezone used to interpret its input.
- In onboarding's What do you want to track step, all type choices stay selectable even when a
game has no current matching routines or windows. Explain the empty state beside the choice;
distinguish no current active window from no supported built-in routine. An empty result is not
a disabled control. Retain the reader's selected tracking intent rather than deriving it solely
from which routines happen to exist today. The implementation spec must reconcile this intent
with the earlier shortcut-only composition model and define how newly supported routines are
offered without silently expanding what existing readers track.
- Initial setup also asks for default game server, preferred appearance and whether to show
events that have not started. The selected server initializes game setup and individual games
can override it. Appearance includes Dark / Light / System and the agreed colour-scheme choices
once those are settled. Upcoming events remain off initially, consistent with the existing
default, until the reader selects otherwise. The previously agreed Checklist/Timeline default
question remains. Present these as a short preferences step rather than crowding the tracking
matrix with unrelated controls.
## Prototype boundaries and implementation work still ahead
- “Now” and reset clocks are fixed for review. The final app must use the existing per-game reset
rules and actual region-specific end times.
- Most event content was carried over from the earlier prototypes. Codes, historical archive
records, version groups and some descriptions are illustrative. No new source fetch runs here.
Live-list source links are matched by title against the existing local feed; unmatched sample
events receive no invented source URL.
Game8's supplied Genshin code page is a source-shape reference, not an implemented adapter.
- Only in-app code notices are proposed. Push, email and background notifications remain outside
the product scope.
- Some inherited controls remain placeholders: backup and portions of own-event/routine editing.
- The archive snapshot works within this prototype's save. Production needs an explicit snapshot
retention/export format and a policy for capturing events before they drop from the feed.
- Production needs a reviewed migration for existing progress into per-server storage; no existing
event ID, daily ID or day key may be changed casually. The archive and code state also need a
documented export/import contract. This artifact is not a storage implementation to copy.
- `docs/PRD.md`, `docs/DATA-MODEL.md`, `docs/ARCHITECTURE.md`, `docs/DESIGN.md` and the matching
AGENTS guidance must be updated with the eventual implementation. They describe the shipped app
today, so this review record does not rewrite them as though the redesign had landed.
## Verification
Browser verification and screenshots are performed with scratch-installed puppeteer-core and
Chrome, at 1440px and 390px. The prototype needs no Bun build or production feed changes. The test
journeys cover density parity, navigation, completion/ignore delay and Undo, archived detail and
Restore, separate code visibility/redemption, server setup, reset-strip adjacency, onboarding,
returning users, persistence, reduced motion and unknown end dates.
Both width passes completed with no JavaScript errors and no page-level horizontal overflow in
the checked screens. The initial mobile pass caught a confirmation notice intercepting Archive
row taps; informational notice surfaces now let pointer events through while Undo remains a real
button. An early verification-script failure also tried to open a Star Rail event after onboarding
had selected only Genshin; the scenario now resets sample state before testing an unknown end.
Impeccable's mechanical detector reports palette, typography and size differences against the
**shipped** DESIGN.md, plus inherited stripe treatments. The new Barlow timetable appearance is
intentional and selected in earlier rounds; this prototype does not rewrite the shipped design
reference or add detector suppressions. Its visual language remains subject to this review.
@@ -0,0 +1,112 @@
# Routine redesign: Round 5 review
This is a review prototype, not a production release or an approved implementation spec. It
combines the owner's feedback after Round 4, recorded in
[the preceding review](2026-10-16-redesign-round4.md), and the subsequent freshness, backup and
focus-bar requests.
Open [Round 5](../../design/mockups/round5/converged.html), or its copy at
`~/gacha-mockups/round5/converged.html` through the shared index. Earlier rounds remain unchanged.
## What changed
- **One-click dailies:** an optional panel exposes daily ticks directly, separately per server.
The alternative groups dailies with the routine summaries. A daily appears in only one place;
pins still decide which dailies appear. The separate panel is the prototype's starting choice,
to test the owner's request for immediate access; this default remains reviewable.
- **Focus alternatives:** a labelled game toolbar is the starting choice. The review bar's Focus
selector also offers a compact game switcher and a picker at the top of the Checklist rail.
Each changes the same focus state and counts, and preserves auto-expanding focused routines.
Timeline retains a visible toolbar/switcher rather than acquiring a Checklist rail.
- **Content-sized Standard rows:** events lacking descriptions use less vertical padding and
omit the segmented meter. They retain a wrapping title, countdown and tap target. No filler
descriptions are added.
- **About and colophon:** Settings → About Event Clock retains the author, source communities,
studio credits, acknowledgements, reset-clock reference, disclaimer and project links. The
footer links there. Source credits come from the existing local feed, not the selected games.
- **Freshness:** Checklist and Timeline visibly state data age and warn about older schedules for
the games switched on. Source breakdown distinguishes last successful check from last content
change and explains unchanged pages, overdue pulls and never-fetched data. Unknown causes are
described as unreported, not invented. Settings → Data & sources exposes the same information.
The review bar offers Current / Some stale / All stale / Never fetched / Offline. These are
labelled illustrative states; no real fetch runs. Check for new data demonstrates a no-newer-feed
result and is disabled offline. The headline data timestamp covers the demo feed as a whole;
warnings and named source rows cover selected games. No build timestamp is used.
- **Appearance:** Dark / Light / System plus three curated scheme previews, Slate / Warm / Plum.
This answers the palette request for review, not the unresolved possibility of a fully editable
interface palette. Semantic deadline colours remain separate from game identity.
- **Game colour:** Setup adds a colour picker, hex input, preview and Reset to default. An override
applies to all the game's servers and all its views. The stored base colour remains unchanged
while its rendered colour is adjusted for contrast in the chosen appearance.
- **Timezone:** Automatic is the default; named zones can be selected and searched in Region &
time. Dates use the selected zone, including daylight saving, without changing server reset
boundaries. The prototype's per-game reset clocks remain simplified.
- **Additional servers:** “Track another server” explains simultaneous separate account tracking.
Added servers still start with fresh routine choices and progress.
- **Onboarding:** initial preferences now ask default server, mode/scheme, timezone and whether
upcoming events should appear. Game selection, tracking types and Checklist/Timeline default
follow. All tracking types remain selectable even if no matching built-in exists. Availability
is explained, and selected intent is saved separately from actual follows. The no-matching-types
message does not incorrectly call that selection events only. New supported built-ins are
described as offers for review rather than automatic extra work for existing readers.
- **Archive story:** restores monthly/overall completion percentages, current/best completion
run, logged event check-ins and unfinished/near-miss counts, plus per-game outcome bars, above
the default per-game archive. Ignored events are excluded from completion-rate denominators.
Progress and statistics derive from illustrative saved archive records, and scope is labelled.
History timeline remains an alternate view.
- **Automatic backup history:** Backup shows seven daily, four weekly and six monthly snapshots,
with latest-per-period compression. The retained historical examples are labelled sample; today's
snapshot updates from the demo's own state. Restore previews game/routine/archive/progress counts
and preserves current data as a recovery copy first. Undo last restore is available. Manual
Back up now and demo JSON export/import live alongside the automatic history.
## Backup semantics used for this review
The most recent seven days retain one latest snapshot per day. Older snapshots fold into four
consecutive seven-day windows. Older records then fold into six calendar-month buckets, keeping
the latest snapshot in each. This is up to seventeen retained snapshots, plus a recovery copy from
the last restore. No snapshot is invented for a day the reader never opened the app.
The demo clock is fixed and its bucket calculations use UTC. Production needs to settle and
document the stable calendar basis independently of display-timezone changes, handle storage
capacity/failures, validate restore files, and use the real export contract. Backups are browser
local and are created while the app is used; they cannot be promised for days when it stays closed.
Export remains the way to keep a copy outside that browser or move to another device.
## How to inspect
1. Compare focus options in the review bar. Focus Genshin; tick EU and Asia dailies separately.
2. Compare Standard/Compact, especially rows without descriptions, and switch the daily layout.
3. Use the Data selector to inspect every freshness state, then expand Why the data is older.
4. Open About, Data & sources, Region & time and Appearance. Change timezone, scheme and game colour.
5. Open Archive to inspect richer stats and History timeline.
6. Open Backups, review a monthly sample, restore it, then Undo last restore. Demo export files are
separate from the production app's format and storage.
7. Open New user. Choose preferences and Genshin, then select Cycles despite no matching built-in.
8. Set Timeline as the default and use Returning user to check the introduction appears there.
## Verification and boundaries
Chrome journeys at 1440px and 390px exercised direct daily ticks, layout choices, focus alternatives,
freshness variants, credits, timezone persistence, light/dark/system and schemes, game-colour reset,
fresh server setup, Archive stats, backup restore/recovery, onboarding with an empty type,
returning-reader introduction and completion/Undo. Both passes had no JavaScript errors and no
page-level horizontal overflow in the checked screens. A follow-up checked latest-per-period
retention across 400 simulated days, all-games empty-type toggles, and desktop sticky toolbar/rail
separation.
The mechanical design detector reports 30 findings, chiefly differences from the shipped palette,
Barlow typography and size/radius ramp, plus inherited archive stripe treatments. These are review
artifacts in the owner-selected replacement visual direction; no production design reference or
detector suppression was changed to make the prototype appear to match the shipped app.
An inherited Round 4 Reset handler initially discarded the new state fields. Round 5 now owns
reset initialization and its browser check passes. The active prototype data namespace is
`event-clock:mockup:round5:v1`; production localStorage keys and progress are untouched.
The existing demo library/create form, independent pins, completion confirmation/fade/Undo,
Archive restore and code-newness/redemption distinctions remain. Some inherited own-event editors
are still placeholders. Codes, old Archive history, backup history and source health are sample
content. Real parsers, source refreshes, app storage migrations and production backup machinery are
not implemented by this artifact. The shipped specification and design documents remain about the
shipped app until the redesign's spec and implementation are reviewed.
@@ -0,0 +1,105 @@
# Routine redesign: three quieter layouts
Review artifacts, not a production release. This round responds to the owner's concern that
Round 5's focus toolbar and stale-data notice occupied too much space, and incorporates the
additional feedback received while building the alternatives.
Open [the comparison index](../../design/mockups/round6/index.html). The shared copy lives at
`~/gacha-mockups/round6/index.html`; earlier rounds stay available. Each design is a complete
standalone HTML prototype with the same sample event data and the shared features below.
## The alternatives
- **A — Inline desk:** retains the left routine/daily rail. Game focus is a small selector beside
the event heading, and freshness is a single understated line. Source reasons are one click away.
- **B — Game desk:** puts game focus, daily ticks and a short source-status report in the rail.
There is no full-width freshness or focus band above the event list. It reserves more attention
for the list; its tradeoff is a more populated rail that stacks before events on mobile.
- **C — Open ledger:** replaces the rail with a full-width event list, a brief next-deadline row,
direct daily ticks in a horizontal strip, and a disclosure for other pinned routines. Game focus
and a short freshness report sit with the event heading. It gives titles the most width; the
routine controls scroll with the list.
The chosen timetable appearance is preserved. These compare structure and interaction rather than
asking the owner to pick a new visual identity again.
## Shared decisions implemented for review
- **Changelog is its own Settings page.** The owner explicitly chose this over a main app tab,
and explicitly declined a footer link. Remove the awkward Changelog button from About, remove
it from the footer, and render release notes in the page rather than the old overlay. Existing
returning-reader news links lead to that Settings page.
- **Freshness remains honest and available.** Keep the full data timestamp and age in the footer,
scope warnings to selected games, and expose successful-check/content-change times and reasons
in Data & sources. Older source data is also explained in the event's detail. Unknown causes
remain unreported, not invented. The review bar's Data selector tests the illustrative scenarios.
- **Dailies on Checklist belongs in Routines as well.** Routines → Checklist display controls the
same preference as the daily panel shortcut and Appearance. Choose Separate panel or Grouped
with routines. Independent pins still govern whether a routine appears.
- **Checklist routine groups have three behaviours:** Collapsed by default, Expanded by default
(still collapsible), and Always expanded (no collapse buttons). Focusing a game expands its
routines. The wide layout also honours this for its enclosing routine section.
- **Numeric fields show the native stepper.** Browser increase/decrease controls remain visible
and use step one, as requested. They are not replaced by a separate custom spinner.
- **Appearance includes Custom.** Edit background, surface, text and interface accent separately
for light and dark. Stored choices are retained while preview text/surfaces are adjusted for
contrast. Game identities and deadline urgency remain separate from the custom interface accent.
Individual game colours retain their own control and reset.
- **Reset timing can be overridden per game/server.** Game Setup and Region & time expose Automatic
versus a custom server-local hour/minute, with Revert to Automatic. The prototype applies the
override consistently to daily/routine periods and reset summaries. Reader-authored routines
with their own explicit reset retain it. The automatic source clocks remain simplified here;
this is not a production reset migration.
- **A game's About names its sources.** Links come from that game's entries in the existing local
feed, including separate event/banner/pass sources. Use readable schedule labels and thank the
editors. This differs from the global About's full, unfiltered colophon.
- **Remove Done and ignored events from Lists.** That row only linked to Archive and was not a
setting. Archive keeps its own main navigation tab.
- **Backup rows say View backup.** They open a preview headed Backup from [date], showing the
snapshot's contents. Restore this backup is the explicit action there; the current state is
preserved as a recovery copy. The seven-day/four-week/six-month history is retained.
- **Returning readers get a guided introduction.** The review bar's Returning user control opens
a short welcome in whichever view is preferred, with Take the quick tour and Keep browsing.
Four steps visit Routines, Checklist, Archive and Settings. Back and Skip are available; finishing
returns to the preferred view. Games, follows and progress remain intact. Production will bind
this to a saved release marker, so it is offered once rather than every load.
- **Upcoming events moves to What do you want to track?** It is no longer in the initial preference
step. The initial preferences retain default server, appearance and timezone; all type choices
remain selectable and explained even when no matching built-in exists.
Standard/Compact density, richer Archive statistics and the history timeline, separate server
progress, code-newness versus redemption, completion confirmation/fade/Undo, timezone and the
existing routine library/create form remain available in every alternative.
## Boundaries
Each alternative uses its own `event-clock:mockup:round6:<letter>:v1` browser namespace. No production
keys, parsers or feed data are changed. Source-health states, codes, archived history and historical
backups are illustrative. Clock overrides affect the demo's period calculations, not any real stored
game-day keys. Production must review migration and the preservation of historical ticks before
introducing reader overrides. These are disposable prototypes, not code to transplant into the app.
## Verification record
The browser pass checks all three designs at 1440px and 390px: focus and daily controls, native
numeric stepping, all disclosure modes, source reasons in event detail, the Changelog page and its
absence from About/footer, custom-palette contrast, reset override/revert, per-game source credits,
Lists cleanup, backup labels, the moved upcoming option, and the returning-user tour preserving
progress. Screenshots cover each main layout plus the new shared pages.
The first mobile pass found a backup-preview smooth scroll continuing after navigation, allowing a
subsequent automated tap to hit the fixed review bar. The preview now scrolls instantly, avoiding a
pending animation against a different screen.
The confirming pass completed all six combinations of design and viewport with no JavaScript
errors or page-level horizontal overflow in the checked screens. Custom-palette checks included
equal chosen background/text colours and confirmed the rendered text was adjusted to at least
4.5:1. Per-server override checks exercised 05:30, verified the 90-minute change from 04:00, then
returned to Automatic. The tour returned a Timeline-default reader to Timeline with saved games,
follows and progress unchanged.
Design-hook triage: retain the owner-selected Barlow typography. Narrow value exceptions for that
font are scoped to `design/mockups/round6/index.html` only, with evidence in `.impeccable/config.json`.
No whole-file or whole-rule exclusions are added. The shipped DESIGN.md remains the production
reference; prototype differences in palette/size and inherited archive treatments remain review
findings rather than being suppressed broadly.
@@ -0,0 +1,80 @@
# Routine redesign: one airier design
**Later owner decision:** retain two interfaces after all: Round 7 and Round 6 **B**, selected
in Settings. The earlier single-interface direction below records the mockup review, not the
final implementation decision. Replace “Older schedules” with “Event data may be out of date”.
See the [production redesign specification](../superpowers/specs/2026-10-16-ui-redesign-design.md).
Review prototype, not shipped behaviour. Open
[the unified mockup](../../design/mockups/round7/converged.html), also copied to
`~/gacha-mockups/round7/converged.html` through the shared index.
## The owner's correction
Round 6's three alternatives were rejected. The owner asked for:
- Older-data notices in the same structure as New since you last looked, appearing only when
games have older data.
- The current production game's focus-pill structure, placed at the top of the left rail as in B.
- A more airy, easy-to-read design informed by the original
`~/gacha-mockups/c-one-due-list.html`.
The initial idea of a Simple mode or a spacing toggle was then explicitly reconsidered: the owner
does not want two distinct UIs. Carry the spirit of C into one unified interface instead.
## What this mockup does
**A shared updates area.** New code notices and Older schedules use two compact rows in one
component above the Checklist's event list and above Timeline. The older row lists affected games
and links to Details & reasons; no full-width health banner or rail status panel remains. It is
absent when the selected games have current sources. If there are no new codes either, the whole
updates area disappears. Source reasons remain available in Data & sources and event detail; the
full feed timestamp stays in the footer on every load. Scope is the games the reader has switched
on, rather than only the temporary focus. Repetitive older-data labels on individual list rows are
removed because the updates row already names those games.
**The existing focus behaviour.** `src/client/components/GameFocus.tsx` is the reference: All and
game pills, game hues, outstanding counts, and Next progressing through games and then back to All.
Tapping the focused pill returns to All. Pills wrap in their own panel at the top of the Checklist
rail; they are absent for a single-game setup. Timeline uses the same pills above its lanes. Focus
never changes which games are followed.
**One visual world.** The original C informs separated panels, quieter rules, softened surfaces,
more room around controls and a warmer reading rhythm. Bricolage Grotesque and Figtree replace the
compressed timetable typography consistently across the prototype, including settings and forms.
The rail and event list have a real gutter; focus, deadline, dailies and routines have separated
surfaces. Light mode uses warm paper and dark mode a softer charcoal. No Simple/Detailed mode or
global spacing toggle is added. Standard/Compact remain two row densities of this same appearance.
Rows with no description remain shorter; comfortable layout is not a fixed tall event height.
Direct daily ticks, routine disclosure choices, independent pins, custom palettes, per-game colours
and reset overrides, source links and thanks, Changelog in Settings, Archive statistics and history,
backup history/recovery, timezone and both onboarding flows remain available. The daily-panel layout
shortcut is removed from the Checklist rail to reduce controls there; its Routines and Appearance
settings continue to edit the same preference.
## Validation
Chrome journeys at 1440px and 390px passed with no JavaScript errors and no page-level horizontal
overflow in the checked views. They cover conditional/scope-aware notices, empty updates, focus
toggle/advance, spacing, routine expansion, reset override/revert, source credit links, custom
scheme controls, light-theme game-colour contrast on ground and panel, Changelog location, backup
history, Archive stats, Timeline, completion/fade/Undo and onboarding. Contrast adjustment now
considers both panel and page grounds, while retaining saved base game colours.
Screenshots were inspected for Checklist in both appearances, Routines, Data & sources, Archive
and Timeline. The first verification script used a nonexistent review-bar Settings shortcut; it
was corrected to use the actual Settings tab. No product change was needed for that script error.
The mechanical design check flags differences from the **shipped** DESIGN.md in palette, type
size, radius and inherited archive treatments. Those remain review findings, not broad suppressions.
The intentional Bricolage font exception is scoped to this one prototype file, with rationale in
`.impeccable/config.json`. The production visual reference is not rewritten before implementation.
## Boundaries
The namespace is `event-clock:mockup:round7:v1`. Earlier mockups and production progress remain
unchanged. Historical records, codes and source health are illustrative; clocks are fixed and
automatic reset defaults simplified. No source requests or production parser/storage changes run
here. This is a disposable visual review artifact; the redesign still needs a reviewed production
specification and migration plan.
@@ -0,0 +1,101 @@
# Redesign fidelity correction — 2026-10-17
> **Owner rejected this implementation after browsing it:** “This is still not even close to the
> mockup.” The completion claim is withdrawn. Screenshots and review verdicts below are historical
> evidence of the rejected attempt, not acceptance. Continue from
> [the second-rejection handoff](../handoffs/2026-10-17-redesign-second-rejection-handoff.md):
> launch the current server, then ask the owner for the biggest differences before further UI edits.
The owner asked to repair the rejected implementation against Round 7 Relaxed and Round 6 B
Structured, then explicitly made **Structured the default**. Existing saved Relaxed choices remain
valid. This correction is uncommitted in the existing checkout; no branch, publication or data-source
refresh was performed.
## What changed
- Checklist uses dedicated reference-shaped event rows and next-deadline panel, with separate copy,
countdown, supporting date, segmented meter and detail chevron. Header counts and Settings placement
follow the reference. Both interfaces retain the owner-approved focus pills.
- Routines has By reset/By game bands, aligned game/routine/progress/view-pin/menu columns, live totals,
direct daily status, amount bars/inputs, library and a native custom-routine drawer. Bulk pins combine
writes per account. Multi-server row identity includes both account and routine.
- Timeline plots event spans and routine periods on one axis with frozen labels, progress fill and
reset markers. In-progress bar labels remain visible after scrolling. Deadline ordering includes
routine resets, and separate reset strips remain available.
- Settings restores separate Default view and Your own events and games pages, game profiles, tracking
summaries and Other games. Profiles follow tracking → servers → colour → reset timing. A server
selector above tracking keeps choices independent without duplicating tall account panels.
- Archive restores the story, interactive outcome bars, game sidebar, saved rows and history timeline.
Its detail dialog reads saved metadata, original clock region, notes, days and amounts. It does not
open the live progress editor or replace historical state with the current clock.
- Structured defaults agree across prepaint, normalization, setup and applied appearance. No IDs,
localStorage key spaces, migrations or ingestion rules were changed by this correction.
## Reference comparisons
Images are in [the evidence directory](assets/2026-10-17-fidelity/). Representative matched views:
| View | App | Approved reference |
| --- | --- | --- |
| Structured Checklist | [Desktop](assets/2026-10-17-fidelity/app-structured-dark-1440.png) | [Desktop](assets/2026-10-17-fidelity/reference-structured-dark-1440.png) |
| Relaxed Checklist | [Desktop](assets/2026-10-17-fidelity/app-relaxed-dark-1440.png) | [Desktop](assets/2026-10-17-fidelity/reference-relaxed-dark-1440.png) |
| Relaxed mobile | [390px](assets/2026-10-17-fidelity/app-relaxed-dark-390.png) | [390px](assets/2026-10-17-fidelity/reference-relaxed-dark-390.png) |
| Routines | [Structured](assets/2026-10-17-fidelity/app-structured-routines.png) | [Structured](assets/2026-10-17-fidelity/reference-structured-routines.png) |
| Timeline | [Structured](assets/2026-10-17-fidelity/app-structured-timeline.png) | [Structured](assets/2026-10-17-fidelity/reference-structured-timeline.png) |
| Game Setup | [Relaxed](assets/2026-10-17-fidelity/app-relaxed-settings.png) | [Relaxed](assets/2026-10-17-fidelity/reference-relaxed-settings.png) |
| Archive | [Structured](assets/2026-10-17-fidelity/app-structured-archive.png) | [Structured](assets/2026-10-17-fidelity/reference-structured-archive.png) |
Additional captures cover both themes at 1440px/390px, populated mobile utility pages, compact rows,
event/archive details, custom-routine drawers, archive history and the Settings pages.
The comparison harness freezes time at 2026-10-16 12:00 UTC and intercepts the event-feed request
with the prototype's **synthetic demonstration events**. It seeds disposable browser storage with
matching routine choices and 38 archive snapshots. These fixtures are review evidence only: they
are not imported by production, copied into public, or written into source snapshots. Actual setup,
HTTP migration and offline checks use the built app and its real feed separately.
Expected semantic differences: real account rows include Asia events, so totals differ from the
prototype's game-only event list. One prototype custom task was not seeded. Production has no
version metadata for archive events, so it groups saved records without inventing version names.
Real reset clocks and available routines govern progress. Code/release notices reflect actual data;
Checklist comparisons hide these notices to align the first viewport. The Structured prototype's
filter dropdown is intentionally superseded by the owner's focus-pill decision.
Reproduce with [fidelity-capture.mjs](fidelity-capture.mjs), from the repository root, setting
`PUPPETEER_MODULE` to the scratch installation's ESM entry and `APP_URL` / `MOCKUP_URL` to fresh
running origins. Chrome is `/usr/bin/google-chrome-stable`; the harness maps event-clock.test to
loopback. Rebuild CSS/JS/static without fetching the feed, and use a new app port after each rebuild.
## Independent review
The first reviewer invocation hit a session usage limit; a retry succeeded. Its initial material
findings were invisible scrolled timeline labels, substituted Settings navigation, routine column/
status hierarchy, multi-account row-key collisions, and insufficient populated Archive evidence.
One follow-up also required correcting the game-profile hierarchy. Each finding was fixed and the
same screenshot paths recaptured. Final independent verdict: **“ship the reviewed corrections.”**
All reported material findings were resolved. This verdict is scoped to the compared screens and
reported fixes; it is not owner acceptance or blanket proof of every interaction.
The Impeccable detector ran once and returned `[]` (no findings).
## Verification
Final browser build: `a28021bbe1a8`, production React/JSX transform, 117 bundled modules.
- TypeScript passes. Full offline suite: **1,594 passed, 0 failed**, 5,410 assertions.
- Docker-equivalent copied file layout, without docs/design/public: 1,593 passed, one intentional
docs-dependent skip, no failures. This validates the COPY-layout assumption; no Docker image was built.
- Eight interface/theme/width combinations: no document overflow or page exceptions. Both densities,
reduced motion/effective 200% viewport, custom palette and timezone selection checked.
- Ordinary insecure HTTP hostname: new setup, existing v1 migration, daily writes, second server,
reset override/revert, backup and reload pass; original legacy bytes retained. Structured is the
missing-choice default and saved Relaxed survives reload.
- Real two-tab simultaneous writes with Web Locks disabled pass using the existing IndexedDB lock.
Offline reload passes on localhost's service-worker-capable origin.
- Final-build interaction checks: multi-server bulk pins preserve the other view's placement; numeric
edits and custom-routine creation persist; completion, Undo, archive detail, Restore and Escape pass.
Final profile's server/reset controls were rechecked on insecure HTTP after layout changes.
- `git diff --check` passes. No new runtime dependency, source fetch, commit or publication.
The earlier rejection is preserved in the 2026-10-16 report. Passing tests did not establish its
visual fidelity; the side-by-side evidence and scoped review above are the correction's design record.
Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

@@ -0,0 +1 @@
[{"id":"e0","game":"genshin","title":"Anniversary Memories Album","desc":"Look back through the year's moments in an album and claim Primogems for each page you fill.","start":1790564400000,"end":1792292400000,"src":"https://genshin-impact.fandom.com/wiki/Anniversary_Memories_Album"},{"id":"e1","game":"zzz","title":"Clink, Clank, Pinball Knight!","desc":"A pinball minigame with daily stages; clear them for Polychromes and upgrade materials.","start":1789009200000,"end":1792378800000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e2","game":"zzz","title":"Diary of an Orbie Parent","desc":"Raise an Orbie through daily check-ins and small tasks.","start":1790737200000,"end":1792378800000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e3","game":"nte","title":"Stamina Recharge","desc":"Spend City Stamina twice as fast in select Hethereau Hobby activities.","start":1791169200000,"end":1792378800000,"src":"https://game8.co/games/Neverness-to-Everness/archives/592073"},{"id":"e4","game":"zzz","title":"Data Bounty - Combat Simulation","desc":"Short combat simulations with bonus rewards for the first clear of each.","start":1791946800000,"end":1792378800000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e5","game":"zzz","title":"All-New Program","desc":"","start":1788922800000,"end":1792465200000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e6","game":"genshin","title":"Predictive Victory Dynamics","desc":"A combat event in stages; each unlocks a day after the last.","start":1791774000000,"end":1792465200000,"src":"https://game8.co/games/Genshin-Impact/archives/301601"},{"id":"e7","game":"wuwa","title":"Bountiful Crescendo","desc":"","start":1792033200000,"end":1792638000000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e8","game":"hsr","title":"4.6 Planar Fissure","desc":"Double rewards from Planar Ornament Extraction, a few times a day.","start":1791774000000,"end":1792897200000,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"},{"id":"e9","game":"wuwa","title":"Artisan's Search","desc":"","start":1791428400000,"end":1792983600000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e10","game":"wuwa","title":"Waking Moon Fishing","desc":"","start":1792033200000,"end":1793502000000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e11","game":"genshin","title":"Battle Pass - Moontrace","desc":"","start":1790132400000,"end":1793588400000,"src":"https://game8.co/games/Genshin-Impact/archives/301601"},{"id":"e12","game":"genshin","title":"To Temper Thyself and Journey Far","desc":"","start":1786330800000,"end":1793588400000,"src":"https://game8.co/games/Genshin-Impact/archives/301601"},{"id":"e13","game":"hsr","title":"Love, Ghosts & Robots","desc":"","start":1790564400000,"end":1794366000000,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"},{"id":"e14","game":"hsr","title":"Astral Imagea","desc":"","start":1790564400000,"end":1794366000000,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"},{"id":"e15","game":"nte","title":"Pukaland Travelogue","desc":"","start":1790737200000,"end":1794366000000,"src":"https://game8.co/games/Neverness-to-Everness/archives/592073"},{"id":"e16","game":"wuwa","title":"3.7 Moonlit Path","desc":"","start":1790737200000,"end":1794366000000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e17","game":"nte","title":"Coal Lumps Treasure","desc":"","start":1791428400000,"end":1794366000000,"src":"https://game8.co/games/Neverness-to-Everness/archives/592073"},{"id":"e18","game":"endfield","title":"Originium Supply Run","desc":"Mockup event so Endfield has a row.","start":1791514800000,"end":1792724400000,"src":null},{"id":"e19","game":"hsr","title":"Free Gilgamesh or Archer","desc":"","start":1784862000000,"end":null,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"}]
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Some files were not shown because too many files have changed in this diff Show More