Skip to content

feat: scrub and play thirty days on the control room - #24

Merged
iamsiddhesh-dev merged 4 commits into
mainfrom
clock-scrubber
Sep 4, 2026
Merged

iamsiddhesh-dev merged 4 commits into
mainfrom
clock-scrubber

Conversation

@iamsiddhesh-dev

Copy link
Copy Markdown
Owner

The control room showed a finished run. It now shows the run happening — a
slider across thirty simulated days, with a play control that replays the whole
thing in twelve seconds while the money accumulates on a curve beneath it.

The ledger already made this possible: it holds every intermediate state rather
than a final summary, and replay.state_at folds the stream up to a timestamp.
That capability had been sitting there since the ledger was built, unused.

Precomputed, because scrubbing cannot afford a request

state_at is O(events) per call. Fine for one moment; wrong for a slider that
wants two hundred of them faster than a hand can move — that would be 200 full
scans, or 200 round trips, and either makes the control feel like a form rather
than an instrument.

So all 200 frames are folded in one pass at demo time, written to
data/frames.json, and embedded in the page. Scrubbing is an array lookup with no
network in it: no request per frame, no loading state, no way for it to feel laggy.

Safe because the run is frozen and deterministic — the same property that lets
every other screen read a precomputed artifact.

Frames are spaced evenly in simulated time, not event volume, so a quiet
stretch still produces frames. Otherwise the slider races through busy periods and
stalls through calm ones, which misrepresents when the money actually came back.
There is a test for that.

56KB → 16KB

The first payload sent thirteen JSON key names per frame — about 45KB of repeated
strings carrying no information, landing on first paint since the data is embedded
rather than fetched. The payload is now a columns header and bare rows.

Page-load cost also went from ~400ms to zero: folding happens in recoup demo
alongside the summary and the explanations. If frames.json is absent — an older
run — the route rebuilds from the ledger, so it degrades to slow rather than to
missing.

Details

The card ships hidden and the script reveals it. If JavaScript fails there is
no half-working slider, and every other figure on the page is server-rendered
anyway.

Indian grouping is reimplemented in JS to match recoup/money.py. The scrubber
sits directly beneath the scoreboard; ₹5,41,724 above and ₹541,724 below would be
worse than no scrubber.

A test asserts the last frame equals the scored arm — recovered, count,
observed, at-risk, vetoes, contacts, cost. The replay and the headline cannot drift
apart.

No cumulative total may go backwards. A number that dips as you drag is a
folding bug; there is a test over every cumulative field. open_count is exempt and
tested separately — it is a level, not a total, and legitimately falls as payments
resolve.

prefers-reduced-motion hides the play button but keeps the slider. Dragging is
the reader's own choice; autoplay is ours.

Verification

pytest — 502 passed, 15 new. ruff check . clean.

Driven in the browser at 1150px: no console errors, no horizontal scroll, 16KB
payload confirmed in the DOM. Scrubbed to frames 0/50/100/150/199 and checked every
figure accumulates correctly with the marker tracking from x=5 to x=1000. Play
started from the end, reset to zero, and reached frame 50 of 200 after 3 seconds —
on pace for the intended 12. Every new colour resolves from tokens in both themes,
read from computed styles rather than judged by eye.

@iamsiddhesh-dev
iamsiddhesh-dev merged commit 60c73fd into main Sep 4, 2026
@iamsiddhesh-dev
iamsiddhesh-dev deleted the clock-scrubber branch September 4, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant