Skip to content

dash: Workflow panel and Spend punchcard - #783

Open
iamtoruk wants to merge 1 commit into
mainfrom
feat/dash-workflow-punchcard
Open

dash: Workflow panel and Spend punchcard#783
iamtoruk wants to merge 1 commit into
mainfrom
feat/dash-workflow-punchcard

Conversation

@iamtoruk

Copy link
Copy Markdown
Member

Phase 1 and 2 of the surfacing plan for the web dashboard.

Workflow panel beside Model efficiency: correction rate with count, median time-to-first-edit, pricing coverage (renders only when computable, null is absent and never shown as 100%), and the top reworked files (basenames, edits x sessions). The whole panel gates on hasWorkflowContent, so payloads from older CLIs render today's dashboard unchanged and Model efficiency keeps full width.

Spend punchcard below it: hour-of-day by weekday matrix computed client-side from history.timeline, dot size and intensity encoding spend, per-cell exact-USD tooltips. Honesty rules: empty cells stay empty, the panel hides when the timeline is absent, and coarser-than-hourly periods (month and up produce daily buckets) show a notice pointing at Today or 7 days instead of faking a midnight column.

Verified on a real cache copy: month workflow block corrections 6 / TTFE 6m24s / coverage 95.5% with 8 reworked files; week punchcard covers all 168 cells, peak Fri 02:00 at 96.80 USD. Typecheck and vite build clean; the dash has no test harness, so verification is typecheck, build, and a rendered-DOM smoke pass.

Part of the phased surfacing plan; not for release until the wave is reviewed together.

Workflow panel sits beside Model efficiency and surfaces correction rate
(with count), median time to first edit, pricing coverage (rendered only
when a real number, never as 100% for unknown), and the top reworked files
(basenames with session and edit counts). The whole panel hides when the
payload carries no workflow block, so an older peer renders the dashboard
unchanged.

Spend punchcard is an hour-of-day by weekday matrix computed client-side
from history.timeline, dot size and intensity encoding spend, with a
per-cell tooltip. Cells with no bucket render empty, the panel hides when
the timeline is absent, and daily-bucket periods show an honest notice
instead of faking hour-of-day resolution.

@ozymandiashh ozymandiashh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed both components against the payload contract and, since #888 is open, against the dark token block it introduces. The token discipline here is exactly right: everything reads from var(--color-primary) / var(--color-interactive-secondary) and the semantic classes (text-tertiary-foreground, border-border, bg-popover), so both panels restyle correctly under #888's .dark overrides with zero changes. The honesty gates are also the right calls: absent workflow block hides the panel, null coverage never renders as 100%, and daily buckets refuse to fake a midnight column.

Four findings, first one is a real rendering bug:

1. The tooltip clips inside the scroll container

overflow-x-auto on the punchcard wrapper computes overflow-y to auto as well (CSS spec: once one axis is non-visible, the other cannot stay visible). The tooltip is absolutely positioned inside that container with -translate-y-full at top: hover.y - 8, so for the top row (Mon) it extends above the container's content box: it gets clipped and, because the axis is auto, can flash a vertical scrollbar that shifts the grid. Hovering Mon cells on a narrow window shows it. Fix options: track the hover position relative to the outer non-scrolling div and render the tooltip there, or give the scroll area enough top padding to contain the tooltip.

2. The hour-resolution gate admits future sub-daily-but-coarser buckets

hourResolved = timeline.bucketMinutes < 1440 is honest today because buckets are either <=60min or daily. But any future coarser-than-hour bucket (say 240min) passes the gate and piles its whole spend onto the bucket-start hour, which is precisely the misattribution the gate exists to prevent. bucketMinutes <= 60 is honest by construction and costs nothing now. Same for bucketNote: the >= 60 ? 'Hourly buckets' ternary would label a 1440 bucket "Hourly buckets" if the gate ever moved.

3. "local time" is the viewer's, not necessarily the device's

For a remote peer, points were bucketed on the peer's local hour boundaries and are re-interpreted here through the viewer's getHours(). With a whole-hour offset the punchcard is merely shifted (defensible); with a half-hour offset (IST) every remote bucket straddles two viewer cells and lands on the earlier one. Suggest labeling it "viewer local time" for remote devices, or carrying the peer tz in the payload later. Low priority, but the current label overpromises in exactly the remote case DeviceView supports.

4. Two nits

  • Moving from a covered cell onto an uncovered one keeps the previous tooltip alive (uncovered cells never call setHover), so the tooltip lingers while pointing at a cell the cursor left. setHover(null) on uncovered cells closes it.
  • ring-black/5 (punchcard dot and tooltip) is the one non-token color in the diff; on #888's dark surfaces a black ring vanishes. ring-border or ring-foreground/10 keeps the edge visible in both themes.

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.

2 participants