# ReportedTimeBlock

## Description

ReportedTimeBlock is the **declared time block** — the middle layer of the pipeline. It is a bounded work, break, or step-out interval on a specific workday, formed either by matching a pair of `TimeClockEvent`s (`sourceKind = PUNCH_DERIVED`), by manual declaration where no punch device exists (`sourceKind = MANUAL`), or by bulk import (`sourceKind = IMPORT`). It holds `assignmentId`, `workDate` (the workday assigned by the day-breaker, which may differ from a punch's occurred date), `blockType`, `startAt`, `endAt`, and an optional `correctionReason`.

This layer is where corrections live, and it is designed so **corrections never destroy the original declaration**. While the covering Timecard is OPEN, a correction is expressed as a *new* ReportedTimeBlock plus setting `supersededByBlockId` on the prior one — the superseded block remains readable as history. Once the Timecard is SUBMITTED or APPROVED, a correction attempt is rejected (`TIMECARD_NOT_OPEN`) and `reopenTimecard` must be called first. Once LOCKED, corrections no longer go through supersede at all — they are recorded as historical corrections via `TimeCorrectionLog` instead. This is the structural fix for the recurring Japanese-market complaint that manual edits erase the original data in SMB tools (issue #6). ReportedTimeBlock is the declared truth; the immutable raw beneath it is `TimeClockEvent`, and the rule-driven derivation above it is `CalculatedTimeBlock`, which reads only the current (non-superseded) reported blocks.

## Domain Model Definitions

### Model type

Standard

### Command Definitions

Command docs are out of scope for this design phase (ADR-011). Anticipated commands:

- formReportedBlocks — form/refresh punch-derived blocks for an Assignment and workday by pairing TimeClockEvents through the day-breaker
- declareReportedBlock — manually declare a block where no punch exists (sourceKind = MANUAL)
- correctReportedBlock — supersede a prior block with a new one, preserving the original as history (only while the covering Timecard is OPEN; rejected with `TIMECARD_NOT_OPEN` otherwise), recording correctionReason
- importReportedBlocks — bulk-create declared blocks from an external source (sourceKind = IMPORT)

### Query Definitions

- getReportedBlock — retrieve a single reported block by id
- listCurrentReportedBlocks — current (non-superseded) blocks for an Assignment over a date range, paginated
- listReportedBlocksByWorkDate — all blocks (including superseded history) for an Assignment on a workday, paginated
### Models

- ReportedTimeBlock

### Invariants

- `blockType` is one of WORK, BREAK, STEP_OUT, stored with normalized enum naming
- `sourceKind` is one of PUNCH_DERIVED, MANUAL, IMPORT, stored with normalized enum naming
- `startAt` and `endAt` bound a non-negative interval on `workDate`; duration is measured in whole minutes (no float hours), and an overnight block may end after midnight while still belonging to its `workDate` (day-breaker)
- Every block references exactly one workforce `Assignment` (via `assignmentId`) effective on its `workDate`
- A correction preserves history: the prior block is marked superseded via `supersededByBlockId` and is never deleted or mutated; the replacement is a new block (ADR-014)
- A superseded block is terminal: it is never itself superseded again and never re-opened; correction chains move forward to the newest block
- Among blocks sharing an Assignment and workday, current (non-superseded) WORK blocks do not overlap; BREAK and STEP_OUT blocks fall within a covering WORK span
- `correctionReason` is required on any block created as a correction of a prior block, and absent on an original first declaration
- Corrections by supersede are only allowed while the covering Timecard is OPEN; while SUBMITTED or APPROVED a correction attempt is rejected with `TIMECARD_NOT_OPEN` and `reopenTimecard` is required first; once LOCKED, changes are historical corrections handled by Timecard / TimeCorrectionLog, not by superseding here
- A punch-derived block is re-derivable from its source TimeClockEvents; manual/imported blocks are the declared source of truth for their interval

### Relationships

- **References Assignment** (workforce): `assignmentId` references the Assignment effective on `workDate`, establishing "whose" declared time this is
- **Derived from TimeClockEvent**: a `PUNCH_DERIVED` block traces to the matched raw punch events that formed it
- **Supersedes a prior block**: `supersededByBlockId` links a superseded block to its replacement, keeping the correction history intact
- **Feeds CalculatedTimeBlock**: the current (non-superseded) blocks are the input set for rule-driven calculation; a CalculatedTimeBlock records the reported block ids it was derived from
- **Covered by a Timecard**: the Timecard whose period contains `workDate` governs correction mode — supersede while OPEN, blocked (`TIMECARD_NOT_OPEN`, reopen required) while SUBMITTED or APPROVED, and historical correction once LOCKED
