- Shipped
- August 12, 2026 at 6:35 AM UTC
- Author
- kamo
- Commit
- b764d11
DayTimeline draws a worked day as a 24-hour track with a draggable handle on every punch. A graph rather than a table because a wrong punch is almost never wrong in a way a number reveals: a clock-out at 17:00 looks fine in a list and is obviously wrong on a track where the shift before it ended at 13:00 and nothing follows. Shape is what makes a bad day visible to a reviewer holding fifty timecards. Dragging PROPOSES. It never writes. The handle's release opens the mandatory reason dialog and the proposal is held until the reason is written, because a slider that saved on release would put an unexplained wage adjustment one stray reason for dragging rather than typing a time. An unclosed shift is drawn as an open-ended bar rather than closed at "now". Guessing an end is exactly what the engine refuses to do, and the picture must not imply an answer the calculation will not give. The new instant is derived by SHIFTING the original rather than rebuilding a local wall-clock time, which is what keeps it correct across a DST boundary: "09:00 on this date in this zone" is ambiguous twice a year and simply wrong once. PayPeriodsSubTab makes both period rules visible — only one period in progress at a time, and closing freezes the rule versions. PayrollExportSubTab leads with the dry run: same numbers, commits nothing, so the variance can be read before anyone is paid. Commit is confirmed separately and disabled once a period has one, and a reversal is offered instead of a re-issue. RestrictedMembershipGuard confines a member whose employment has ended to their own records via a prefix allowlist — an allowlist so a page added later is denied by default, where a blocklist would silently admit it. It is a UX boundary, not the security one: every endpoint behind it is independently authorized in SecurityService, because a client-side redirect stops nobody who can open devtools. `chat` now maps to canSendTextMessages in TOOL_CAPABILITY, the one floor capability the restricted tier takes away. The payroll proxy passes text/csv through untouched — parsing it as JSON would fail the download as a 500 while the file was perfectly fine on the other side.