# Shift Planning

## Overview

Shift Planning covers defining reusable **シフトパターン** (`ShiftPattern`) and creating concrete **シフト** (`Shift`) on dates — including 中抜け (split), 通し (through), 宿直 (on-call), and unassigned **open shifts**. The planned working time of both patterns and shifts is carried by one or more ordered **segments** (`ShiftPattern.segments` / `Shift.segments`): a 通常 / 通し / 宿直 shift is a single segment, and a 中抜け shift is two or more segments separated by a real, unpaid gap. It reads the time-tracking `CompanyHoliday` calendar to determine 所定日 / 休日 for a date. Shifts are planned inside a **`ShiftSchedule`** period: while the shift schedule is 下書き (DRAFT) the slots are freely editable, and 確定 (`confirmShiftSchedule`) commits the whole period to workers in one act instead of one slot at a time. Shift-creation-time compliance pre-checks (勤務間インターバル, 36協定) are designed as future room only — the container, not the engine (issue #11, ADR-017).

## Business Purpose

- Let planners compose shifts from named patterns (キンタイミライ pattern-型シフト) instead of re-entering times
- Represent Japanese shift shapes — 中抜け / 通し / 宿直 — and open (unassigned) demand slots as first-class
- Model 中抜け as genuine multiple work segments (not one span with a big break), so the plan is symmetric with actuals (`ReportedTimeBlock` WORK / BREAK / STEP_OUT) and variance is derivable per segment
- Determine 所定日 / 休日 by reading the time-tracking holiday calendar rather than owning holidays in shiftSchedule
- Provide the planned slot that placement fills and that variance measures actuals against
- Leave structural room for shift-time interval (勤務間インターバル) and 36協定 pre-checks without building the engine now (ADR-017)

## Process Flow

```mermaid
flowchart TD
    A[Define ShiftPattern templates with segments] --> B[Create Shift for a date]
    B --> C{From a pattern?}
    C -- Yes --> D[Copy pattern segments and midnight bounds]
    C -- No --> E[Enter one or more segments ad-hoc]
    D --> F[Read time-tracking CompanyHoliday for date]
    E --> F
    F --> G{所定日 or 休日?}
    G --> H[Set shiftType from segment count and open-shift flag]
    H --> I{Future room: interval and 36協定 pre-check}
    I -- Warn only --> J[Shift stays in the DRAFT shiftSchedule]
    J --> K[confirmShiftSchedule commits the whole period]
    K --> L{Withdraw?}
    L -- Yes --> M[cancelShift to CANCELLED]
```

## Scenario Patterns

- **Pattern-based shift**: instantiate a ShiftPattern onto a date, copying each of its `segments` into the Shift's `segments`
- **Ad-hoc shift**: enter one or more segments directly without a pattern
- **中抜け (SPLIT)**: a shift with two or more segments separated by a real gap (e.g. 10:00–14:00 then 17:00–22:00) — the worker is off-site during the gap; each segment may carry its own intra-segment break
- **通し (THROUGH)**: a single long continuous segment, possibly crossing midnight
- **宿直 (ON_CALL)**: a single on-call night segment that spans midnight (plannedEndAt on the next day)
- **Open shift**: a slot with no ACTIVE placement, advertised for fill once its shift schedule is confirmed
- **休日 determination**: a date flagged in time-tracking `CompanyHoliday` is treated as 休日 rather than 所定日
- **Lifecycle**: confirming the ShiftSchedule commits every slot in the period; `cancelShift` withdraws a single slot, even out of a confirmed period
- **Future compliance pre-check (container)**: interval and 36協定 warnings are surfaced at planning time in a later phase (ADR-017)

## Test Cases

- creating a shift from a single-segment pattern copies its segment (start, end, break) onto the date
- creating a shift from a 中抜け pattern (two segments) yields two segments on the Shift with the gap preserved
- a shift with two or more segments is classified SPLIT; a single-segment shift is NORMAL / THROUGH / ON_CALL
- consecutive segments must leave a positive gap; a zero-gap pair is rejected (should be one segment)
- the Shift envelope plannedStartAt / plannedEndAt equals the earliest segment start and latest segment end
- a spansMidnight pattern segment yields a Shift segment whose plannedEndAt is on the following day
- a slot with no placement is still part of the period and is committed with it
- confirmShiftSchedule commits the period; a shift in a CONFIRMED shift schedule and its segments are not freely edited
- cancelShift stamps cancelledAt on a slot in a CONFIRMED shift schedule (terminal)
- a date present in the time-tracking holiday calendar is determined as 休日
- a segment's breakMinutes must be less than that segment's gross span

## Reference Links

- Shift methods, pattern-型シフト, shift-time compliance check (キンタイミライ): https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/11
- Variable working hours and holiday determination gap in current kintai: https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/9
- ROI threshold for AI / optimization scope deferral: https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/6
