# TimeTracking — Domain Glossary

Canonical terms for the `timeTracking` module. Implementation details live elsewhere (`AGENTS.md`, `docs/`); this file is a glossary only.

## Shift Management terms

### Shift
A reusable shift definition: name, color, one or more time slots, and metadata flags. Versioned by `(code, version)` with `lastVersion=true` marking the current row. Created via `POST /shifts` (plain) or `POST /shifts/templates` (with `isTemplate=true`).

The active flag is `isTemplate` (see "Shift Template" below). Two other columns exist on the table but are **vestigial** and should be removed in a follow-up cleanup PR:

- **`isCustom`** — column + index exist on the table, but no DAO/service/VC reads or writes it. Pure dead schema. Safe to drop (column + `idx_shifts_is_custom` index).
- **`isRest`** — code reads and writes exist (`shiftService.createShift`, `shiftAssignment.ts:122`, etc.), but **zero rows in production have `isRest=true`**. The actual rest mechanism is direct `ShiftAssignedShift.type='REST'` rows with `shiftCode=NULL` (see "Shift Assignment" below). Dropping `isRest` requires removing the dead read sites too.

Spanish (PRD/UI): **Turno**.

### Shift Template
A `Shift` with `isTemplate=true`. The canonical code term — used throughout the codebase (`POST /shifts/templates`, `createShiftTemplate`, `ShiftTemplateVC`, `PublicApiShiftTemplateSC`, `listShiftTemplates`, `removeFromTemplates`, etc.). Represents a shift the planner keeps in the **shift repository** for reuse.

In the frontend, Shift Templates are surfaced under a window labeled **"Repositorio de turnos"** (shift repository). The PRD-level terminology for the same concept is `turnos del repositorio` (PRD definition #1: "*Se pueden agregar turnos del repositorio…*"). Code identifier remains `shift template` / `ShiftTemplate`.

Spanish (UI/PRD): **Repositorio de turnos**.

### Rest Day
A non-working day on a user's shift calendar. Modeled at the **assignment** layer as a `ShiftAssignedShift` row with `type='REST'` (and `shiftCode=NULL` in the current production pattern) — not via a parent `Shift`. The day-summary computation treats this as a non-working day; overlap rules in `move` block placing other shifts on top of it.

This is the canonical mechanism. The `Shift.isRest=true` path exists in code but is unused in production data — see the `Shift` definition above.

Spanish (PRD/UI): **Turno de descanso** / **día de descanso**.

### Shift Rotation
A reusable, named, ordered sequence of up to 4 weeks (28 days) of shifts. Assigning a rotation materializes its content into the existing shift-assignment grid (`ShiftAssignedDay` / `ShiftAssignedShift` / `ShiftAssignedTimeSlot`). Rotation edits are not retroactive in the current iteration.

Each `ShiftRotationDays` row has a `type` (`WORK` | `REST`):
- **WORK** day → carries 1..3 shift entries defined **inline** on the rotation (name, description, time slots, color). Entries on the same day cannot overlap in time. Rotation shifts are not Shift Templates and do not reference the shift repository.
- **REST** day → carries **zero** shift entries. Rest is intrinsic to the day; no shift row is consumed.

Invariants:
- `dayPosition` values are contiguous from 1 to the rotation's cycle length. No gaps. Cycle length = `MAX(dayPosition)`.
- A day is in exactly one state — WORK or REST — never mixed.
- WORK shifts on the same day cannot overlap in time; display order = `ORDER BY start_time`.

The canonical code identifier is `ShiftRotation` (tables: `ShiftRotations`, `ShiftRotationDays`, `ShiftRotationDayShifts`). The word "template" is intentionally avoided — see "Why not 'Rotating Shift Template'" below.

Spanish (PRD/UI): **Plantilla Rotativa**.

### Why not "Rotating Shift Template"

"Shift Template" already names a different live concept in this codebase: a `Shift` with `isTemplate=true` (see above). Reusing "template" for the new rotation entity would create a constant disambiguation cost in every doc, controller name, and code comment. **`ShiftRotation`** is unambiguous and matches what the entity actually models — an ordered, rotating sequence. The Spanish PRD term "Plantilla Rotativa" still translates cleanly; English code ⟷ Spanish PRD divergence is acceptable here (precedent: `TimeOff` ⟷ `Vacaciones`).

### Rotation Assignment
The act of stamping a **Shift Rotation**'s pattern onto a user's calendar starting at a chosen date. Fire-and-forget: it persists no owning entity — only `ShiftAssignedShift` rows that each carry rotation **lineage** (`shiftRotationId + shiftRotationDayPosition`). After stamping, cells are edited individually on the grid like any other shift; there is no "assignment" object to reopen. With **Recurrence** off, exactly one cycle is stamped (cycle once from the start date). WORK days stamp their shift(s); REST days stamp a `type='REST'`, `shiftCode=NULL` row with the same lineage.

Spanish (UI/PRD): **Asignar plantilla rotativa**.

### Recurrence
Repeating a **Rotation Assignment** beyond a single cycle ("repeat rotation" toggle: repeat N times, or until an end date). **Out of scope** for the initial assignment work — only the single-cycle (no-recurrence) case is built. Future recurrence editing is expected to start from a stamped shift's lineage, not from an assignment entity. The materialization strategy for unbounded/far-future ranges is left open for the recurrence ticket — options include a rolling materialized horizon and on-demand materialization at fetch time (since most downstream consumers reconcile against entries, which only exist in the past/near-present, far-future fetches may be safe to materialize lazily via the recurrence engine).

Spanish (UI): **Repetir rotación**.

### Rotation Occurrence
A calendar cell that originated from a **Shift Rotation** — a stamped shift (or REST day) carrying that rotation's lineage. The set of a rotation's occurrences within a date range is recoverable from one or more **sources**: today only the materialized assignment grid (`ShiftAssignedShift.shiftRotationId`); in the future, additionally **derived** from the **Recurrence** config for far-future ranges that were never materialized. A REST day counts as an occurrence (lineage is carried regardless of WORK/REST).

"Filtering the calendar by rotation" is defined in terms of this: it selects the **users** who have at least one occurrence of the given rotation(s) in the requested range — it does not hide the matched users' other shifts.

Spanish (no distinct PRD term): ocurrencias de una **Plantilla rotativa** en el calendario.

### Shift Assignment
The materialization of shifts onto a calendar grid. Stored as `ShiftAssignedDay` (cell: user + date + region) → `ShiftAssignedShift` (one row per shift in the cell) → `ShiftAssignedTimeSlot` (time slot snapshot at the moment of assignment).

References to source shifts use `shiftCode + shiftVersion` (soft FK, `constraints: false`). Rotation-sourced assigned shifts additionally carry `shiftRotationId (UUID) + shiftRotationDayPosition (INTEGER)` (introduced by SQCC-11) so the originating rotation/day is recoverable per shift.

Status columns:
- **`ShiftAssignedShifts.status`** (`DRAFT | PUBLISHED`) — scaffolding for an upcoming Draft/Publish feature. Currently all rows are written as `PUBLISHED` and read sites check for `PUBLISHED`; the DRAFT branch is dormant until the feature ships. **Keep**.
- **`ShiftAssignedDays.status`** (`DRAFT | PUBLISHED`) — **vestigial** by design. The Draft/Publish workflow lives at the assignment level (the child `ShiftAssignedShifts.status`), not at the day cell. This column always holds `PUBLISHED` and is only structurally referenced by the unique index `(userId, dateString, status)`. Cleanup PR should drop the column and recreate the unique index as `(userId, dateString) WHERE deletedAt IS NULL`. No code refactor needed — no logic discriminates by its value.

Spanish: **Asignación de turno**.

### Work Schedule (distinct from Shift Rotation)
A `WorkSchedule` is a **fixed weekly schedule** (Mon–Sun) attached to a user via `WorkScheduleUsers`. The schedule defines per-weekday time slots (`ScheduledDays`) and feeds day-summary computation as `DaySummaryScheduleSource.WORK_SCHEDULE`.

A **Shift Rotation** is a separate concept: a freeform N-day (1..28) cycle stamped onto the calendar as `ShiftAssignedShift` rows (`DaySummaryScheduleSource.SHIFT`). Rotations are not bound to weekday alignment; their cycle length is independent of the week.

Both can coexist on a user; the day-summary computation picks one source per day per the existing priority rules.

Spanish: `WorkSchedule` ≈ **Horario fijo**; `ShiftRotation` ≈ **Plantilla rotativa**.
