feat(web): a usage window shorter than a day, and an axis fine enough to draw it (AGT-4296) - #615
Merged
Merged
Conversation
… to draw it (AGT-4296) The dashboard's shortest window was 24h, and a 24h total is dominated by whatever ran before the last deploy. Measured on vela the morning after the durable draft cache shipped: the draft stage read 34.1% prompt-cache over 24h and 66.8% over the last two hours. Only the second number described the code that was actually running — the first said the fix had not landed. `parseUsageSince` already accepted durations like `2h`; the selector just never offered one. What was missing was somewhere to draw it. `groupKeyOf` bottomed out at `day`, so a one-hour window rendered on the time axis is a single bar, which is not a series. So: a `by=hour` axis (`record.ts.slice(0, 13)`), 1h/2h/6h in the selector, and the axis chosen from the window — hour up to 48h, day beyond, because thirty days by hour is 720 bars, which is a texture rather than a reading. Hour keys stay UTC on the wire, like day keys, and are rendered on the reader's clock. Day keys are deliberately NOT converted: shifting one by the UTC offset relabels the day, and that alignment is AGT-4293's business. The card heading follows the axis that was actually drawn — left fixed it read 일별 over labels like `9. 10. 23시`, on first load for every reader, because the default window is 24h. Two test properties worth naming, both learned the hard way here: `process.env.TZ` is pinned in the client test file. Local-time rendering is invisible where UTC and local coincide, and CI runners are UTC — the mutant that skips the conversion entirely passed there while failing on a laptop in KST. Only the non-zero offset is load-bearing, not Seoul. `usage.css` now has a tripwire asserting every `var(--…)` it references is defined in `tokens.css`. Three undefined names shipped into that file during this work (`--surface-2`, `--text-1`, `--text-2`; the real names are `--surface2`, `--fg-primary`, `--fg-secondary`). An undefined custom property is not an error — the declaration is dropped and the rule renders with an inherited colour, so it looks plausible, and neither the build, nor oxlint, nor jsdom sees it. Cross-axis filters and the drill-down panel were split out to AGT-4297. Three review rounds all returned REVISE with the same two classes recurring — a fix trading one defect for another, and a fix re-breakable with a green suite — and every finding in the last round was in the drill-down UI while this half drew none. Per the commit gate, a repeating finding means the change is too large. Review: layer 2 (independent subagent), three rounds on the combined change; this half accumulated no findings across all three. Mutants: always-day axis kills 4 tests, no local-time conversion 2, fixed heading 2, hour key equal to day key 1, short windows removed 1. tsc --noEmit exit 0 · build exit 0 · full suite 5984 passed / 10 skipped, and the same under TZ=UTC · oxlint 0 warnings on the changed files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
The dashboard's shortest window was 24h, and a 24h total is dominated by whatever ran before the last deploy. Measured on vela the morning after the durable draft cache shipped:
Only the second number described the code actually running. The first said the fix had not landed.
What was missing
parseUsageSincealready accepted durations like2h— the selector just never offered one. The real gap was somewhere to draw it:groupKeyOfbottomed out atday, so a one-hour window on the time axis is a single bar, which is not a series.by=hour(record.ts.slice(0, 13)), advertised in--helpand the README alongside the other axes9. 10. 23시— on first load for every reader, since the default window is 24hTwo test properties worth naming
process.env.TZis pinned in the client test file. Local-time rendering is invisible where UTC and local coincide, and CI runners are UTC — the mutant that skips the conversion entirely passed there while failing on a laptop in KST. Only the non-zero offset is load-bearing, not Seoul specifically. The suite is run both ways below.usage.csshas a tripwire asserting everyvar(--…)it references exists intokens.css. Three undefined names shipped into that file during this work (--surface-2,--text-1,--text-2; the real names are--surface2,--fg-primary,--fg-secondary). An undefined custom property is not an error — the declaration is dropped and the rule renders with an inherited colour, so it looks plausible, and neither the build, nor oxlint, nor jsdom sees it.Why this is half a change
Cross-axis filters and the drill-down panel were split out to AGT-4297. Three review rounds on the combined change all returned REVISE, with the same two classes recurring — a fix trading one defect for another, and a fix re-breakable with a green suite. Every finding in round 3 was in the drill-down UI; this half drew none across all three rounds. The commit gate treats a repeating finding as a signal the change is too large.
The split line is drawn so each PR is a vertical slice: filters ship with their consumer in AGT-4297, rather than landing as an API nobody calls.
Review
Layer 2 (independent subagent), three rounds. Layer 1 (
openswarm review) is not run on my own commits per standing policy.Mutants against this half:
dayGate
tsc --noEmitexit 0 ·npm run buildexit 0 · full suite 5984 passed / 10 skipped, and the same underTZ=UTC·oxlint0 warnings on the changed files.Closes AGT-4296.
🤖 Generated with Claude Code