# GetShift

## Overview

getShift retrieves a single Shift by id, including its embedded planned `segments`. It is the lookup behind shift detail/edit views, shift-schedule-placement flows, and variance calculation, which all need the full planned slot.

## Business Rules

- Accepts a `shiftId` as input
- Returns the Shift record — `shiftScheduleId`, `date`, `shiftType`, `cancelledAt`, `siteId`, `shiftPatternId`, `plannedStartAt`, `plannedEndAt` (the span envelope) — whether the slot is live or withdrawn
- Returns the shift's ordered embedded `segments` (by `sequence`)
- Carries no staffing field: whether the slot is open is an anti-join against ACTIVE placements (`listOpenShifts`), not a column here
- Does not filter — callers needing only committed or only open shifts use `listPublishedShifts` / `listOpenShifts`

## Process Flow

```mermaid
flowchart TD
    A[Receive shiftId] --> B[Look up Shift by id]
    B --> C{Found?}
    C -->|No| D[Return error: SHIFT_NOT_FOUND]
    C -->|Yes| E[Return embedded segments ordered by sequence]
    E --> F[Return shift with segments]
```

## External Dependencies

None — segments are embedded directly in the Shift (ADR-022).

## Error Scenarios

- **SHIFT_NOT_FOUND**: no Shift exists for the given id

## Test Cases

- returns the Shift for a given id regardless of status (PLANNED / PUBLISHED / CANCELLED)
- returns segments ordered by sequence
- returns plannedStartAt / plannedEndAt equal to the earliest segment start and latest segment end
- carries no staffing field at all — openness is not readable off the shift
- throws SHIFT_NOT_FOUND for an unknown id
