A punch can be voided without being deleted

Featurekamo-shared-library
Shipped
August 13, 2026 at 2:33 AM UTC
Author
Kamo
Commit
c9f9a8f

Four nullable columns and a second write-once transition on TimecardPunch. A void says the punch should never have existed — a double tap on the clock, an abandoned break, or the seam between two spans the timecard editor has just merged into one. That is neither a correction ("this happened, at a different time", which leaves a replacement row) nor a delete: supersededByPunchId means "read this other row instead", and here there is no other row. The row survives, voided, with its author and its reason beside it. A wage record that can be made to forget a punch is exactly the inadequacy that shifts the burden to the employer under Anderson v. Mt. Clemens Pottery, so the only thing a void removes is the punch's effect on the calculation. enforceWorm() permits voidedAt on the same terms as supersededByPunchId — null to a value, once — and refuses a void with no reason. Un-voiding in place is deliberately impossible: re-instating a punch means inserting one, which leaves its own row and its own explanation. **************** is not a convenience. The unfiltered variant is wrong for deciding clock state: a superseded row keeps its ORIGINAL instant, so a clock-out corrected backwards leaves the dead row latest by occurredAtUtc, and a voided row stays latest until something newer is punched. Either one answers "are you on the clock?" from a record that no longer exists. Sequenced per the entity-change rule: shared-lib installed locally, KI migration written and RUN against 192.168.4.22:5433 with all four ALTERs applied, and only then is this pushed — so no service can build against an unmigrated schema.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing