- Shipped
- August 11, 2026 at 11:47 PM UTC
- Author
- Kamo
- Commit
- abedbab
Punches become resolved spans. Pure, total, and over value types rather than entities so every stage is fixture-testable with no persistence context. Durations come from Duration.between on instants, never wall-clock subtraction. That is the entire DST story and the tests pin it: a 22:00-06:00 shift is SEVEN hours across spring-forward and NINE across fall-back. Subtracting wall clocks computes 8 for both -- one underpaying, one overpaying, and in a daily-overtime jurisdiction the 9-hour night silently loses its overtime hour. Anything unpairable comes back as unresolved rather than guessed. An unclosed shift is an EXCEPTION: leaving it open accrues absurd hours, auto-closing at a scheduled end under-reports, and a fixed cap invents a number nobody witnessed. It must never finalize as zero -- that pays nothing for a day that was worked. Meals are punched, never auto-deducted. TRANSFER closes one span and opens the next without ending the shift, so meal timing and shift-length eligibility continue across a mid-shift move between cost centres.