- Shipped
- August 12, 2026 at 10:40 PM UTC
- Author
- kamo
- Commit
- 8775e47
Replaces the one-day timeline on **************** with a whole pay period, and changes what editing one feels like. The pills carry no text. They used to carry their own times, and a label is 60px wide however long the thing it describes was — so a ten-minute break and a four-hour shift drew the same pill, and the one property a timeline has that a table does not (width means duration) was the property it threw away. Both times now live in a tooltip that names the start, the end and the span between them, and follows the edge being dragged with a readout that updates as it moves. Corners are 2.5px: enough to take the mechanical hardness off, not enough to round a five-minute pill into a lozenge. The hour header labels every hour, each turned a quarter turn counter-clockwise so all twenty-four fit. Four labels six hours apart cannot tell anyone whether a pill starts at 9 or at 10, which is the only question the header is there to answer now that the pills are unlabelled. Every day in the period gets a row, INCLUDING days that have not happened yet. A graph that only draws days with data cannot show a missing punch, and the missing punch is the most common thing wrong with a timecard: the gap is the finding, and a finding needs a row to appear on. Future days are hatched rather than merely dimmed — "not yet" and "nobody recorded anything" are different facts. Drags snap to the MINUTE. Five-minute snapping could not express 9:02, so a clock that was two minutes out could not be corrected and every corrected time acquired a roundness that read as an estimate. Rounding is a payroll policy the engine applies with neutrality reporting behind it; a slider must not apply a second, invisible one. Arrow keys nudge by one minute, Shift by fifteen, Alt takes one end on its own — a pointer cannot express a minute on a track where a minute is under a pixel. And editing is now a draft. Dragging used to open the mandatory-reason dialog on every release: fixing a day with three wrong punches meant writing three explanations, each a variant of "the clock was wrong", and a real protection under Anderson v. Mt. Clemens Pottery turned into a box people typed "correction" into to make it go away. A protection everyone learns to defeat protects nobody. Edits now stage freely and one explanation is collected on save — still mandatory, still recorded on every punch the save touches, but asked once, when somebody actually knows what they did. Nothing is written until then. Supporting changes: - app/lib/timecardGraph.ts holds every decision the graph makes — pairing, clamping, snapping, the DST-safe instant shift — as pure functions, because a decision about a wage record that can only be checked by dragging something with a mouse is a decision nobody checks. 34 tests. - fetchRange falls back to per-day reads on a 404 so the tab is not broken for whoever loads it between the SecurityService deploy and this one. - the add-a-time field round-trips through local wall-clock. It rendered a UTC slice into a datetime-local input and parsed the result back as local, so every visit to the field moved the time by the zone offset in silence.