# WorkRuleAssignment

## Description

WorkRuleAssignment is the **effective-dated binding between a WorkRule and a target population** — the persisted record of "who is on which rule, from when". It is what the time-tracking engine reads when converting Reported → Calculated for a person on a date: the engine resolves the worker's workforce context (Assignment → WorkerEmployment / Position) and picks the assignment in force on the `workDate`, most specific target first.

A target is expressed the same way as `EligibilityRule` targets: every target type is referenced by id — a workforce identity (`WORKER` / `POSITION` / `JOB_PROFILE`) or a catalog population (`EMPLOYMENT_TYPE` / `WORK_REGIME`, an EmploymentType / WorkRegime catalog id). When multiple tiers have an assignment in force on the same date, resolution is **most-specific-wins**: `WORKER` > `POSITION` > `JOB_PROFILE` > `EMPLOYMENT_TYPE` > `WORK_REGIME`. This keeps broad defaults (e.g. all full-time workers on the standard rule) coexisting with narrow overrides (one worker on a store-specific rule).

WorkRuleAssignment is effective-dated (ADR-013): re-assigning a target closes the current generation (`effectiveEnd` = day before the change) and inserts a new one, so "which rule was in force on any past date" is always reconstructable. Creation goes through `assignWorkRule`, which validates the WorkRule exists and an `EligibilityRule` in force grants the target that rule (ADR-022 made WorkRule the sole assignable rule).

## Domain Model Definitions

### Model type

Standard

### Command Definitions

- assignWorkRule — bind a WorkRule to a target on an effective date (subject to EligibilityRule), closing the target's prior assignment

### Query Definitions

- getWorkRuleAssignment — retrieve a single assignment generation by id
- listWorkRuleAssignments — paginated list of assignment generations (history included), filterable by WorkRule
### Models

- WorkRuleAssignment

### Invariants

- Effective-dated per ADR-013: each record carries `effectiveStart`, nullable `effectiveEnd`, and `versionOf`; generations sharing a `versionOf` must not overlap in their effective ranges
- The current generation has `effectiveEnd IS NULL`; re-assigning a target never overwrites a generation — it closes the prior one (`effectiveEnd` = day before the change) and inserts a new one
- A future-dated generation (`effectiveStart` in the future) is a scheduled change and must not affect calculation before its start date
- For any single target and date, at most one assignment generation is in force; overlap within the same target is rejected (`ASSIGNMENT_OVERLAP`)
- `targetType` is a normalized enum discriminator (`WORKER` / `POSITION` / `JOB_PROFILE` / `EMPLOYMENT_TYPE` / `WORK_REGIME`); every type references its target by `targetId` (a Worker/Position/JobProfile identity or an EmploymentType/WorkRegime catalog id)
- The assignment references the WorkRule by `workRuleId` and the target by stable id, never by display name (ADR-015)
- Cross-tier resolution at calculation time is most-specific-wins: `WORKER` > `POSITION` > `JOB_PROFILE` > `EMPLOYMENT_TYPE` > `WORK_REGIME`

### Relationships

- **References WorkRule**: `workRuleId` references the rule generation series being assigned; calculation re-resolves the generation effective on the calculated date via the rule's `versionOf`
- **References workforce targets (cross-module)**: `targetId` identifies a Worker / Position / JobProfile, or an EmploymentType / WorkRegime catalog entry, per `targetType` — read from the WorkerEmployment / Position effective on the calculated date
- **Guarded by EligibilityRule**: `assignWorkRule` permits the binding only when an eligibility generation in force grants the target this WorkRule
- **Consumed by time-tracking calculation**: `calculateTimeBlocks` / `recalculateRange` resolve the assignment in force on `workDate` to pick the WorkRule to apply
