# TimeClockEvent

## Description

TimeClockEvent is the **raw punch event** — the immutable, append-only bottom layer of the time-data pipeline. Each row records a single physical act of punching: a clock-in, clock-out, break start/end, or step-out/step-in, captured from whatever source the worker used. It holds `assignmentId` (whose punch — resolved against workforce), `eventType`, `occurredAt` (the real-world instant), and `source` (punch method).

This is the layer ADR-014 mandates be preserved without destruction: it is never edited or deleted. A mistaken or duplicate punch is retracted not by touching this row but by appending a record to the separate append-only `TimeClockEventVoid` table (ADR-023), so the full punch trail — including the error and its retraction — survives for audit and labor-dispute defense. The `source` is a free-form key (catalog data, not a fixed enum) — deliberately open to cover Japan's IC-card and biometric punch culture and ambient-punch trends (issues #10, #11) as well as any channel an app defines; "which device/channel emitted the punch" is treated as part of the immutable fact. Overnight shifts are not resolved here — the workday a punch belongs to is decided during calculation via a day-breaker, so an event's `occurredAt` date may differ from the workday of the ReportedTimeBlock and CalculatedTimeBlock it feeds (issue #7).

## Domain Model Definitions

### Model type

AppendOnly

### Command Definitions

- recordPunch — append a raw punch event (clock-in/out, break, step-out) from a given source
- voidPunch — append a VOID record to TimeClockEventVoid retracting a prior punch (the event row is never mutated)
- unvoidPunch — append an UNVOID record to TimeClockEventVoid restoring a previously voided punch
- importPunches — append events in bulk from an external time-clock device or file (source = IMPORT / IC_CARD)

### Query Definitions

- getPunch — retrieve a single punch event by id
- listPunchesByAssignment — an Assignment's punch events over a date range, in occurrence order, paginated
- listPunchesByOccurredDate — all punches on a given occurred date (raw, pre-day-breaker), paginated
### Models

- TimeClockEvent

### Invariants

- Append-only: an event is never modified or deleted; a retraction is a record in TimeClockEventVoid, so the raw punch trail is never destroyed (ADR-014, ADR-023)
- `eventType` is one of CLOCK_IN, CLOCK_OUT, BREAK_START, BREAK_END, STEP_OUT, STEP_IN, stored with normalized enum naming
- `source` is a free-form punch-source key (catalog data, not a fixed enum), stored as provided; the app defines the set of sources it recognizes (e.g. WEB / MOBILE / KIOSK / IC_CARD / BIOMETRIC / SLACK / IMPORT)
- **Device-integration seam**: `source` is an open key, so an app can introduce new punch channels (a new device, an SFTP import feed, …) without a core code change
- Every event references exactly one workforce `Assignment` (via `assignmentId`) effective on its occurred date, establishing whose punch it is
- `occurredAt` is a required instant; the event carries no computed workday — the workday is derived downstream by the day-breaker, so `occurredAt`'s date may differ from the workday of blocks derived from it
- Effective void state is external to this row: it is decided by the highest-`sequence` TimeClockEventVoid record for the event (VOID = voided, UNVOID or none = active)
- Raw events are inputs to derivation only; they are never read directly by payroll, costing, or Article-36 agreement (those read CalculatedTimeBlock)

### Relationships

- **References Assignment** (workforce): `assignmentId` references the Assignment effective on the occurred date, resolving employment / post / legal entity ("whose")
- **Voided by TimeClockEventVoid**: void/unvoid records in `TimeClockEventVoid` reference this event via `targetEventId` (append-only negation, ADR-023); this row is never mutated
- **Feeds ReportedTimeBlock**: matched and paired into declared work/break intervals during block formation; a ReportedTimeBlock's `sourceKind = PUNCH_DERIVED` traces back to these events
- **Audit source**: together with effective-dated generations and the approval decision log, the immutable punch trail is part of the "who/when/what" record that replaces the deprecated audit module (ADR-013)
