# TOKIWA — 要件定義書 兼 Claude Code 実装指示書

**版数** 1.0（DBレス構成 / 記事埋め込み型デモ）／ **作成日** 2026-08-24 ／ **発行** ソウゾウ合同会社
**用途** SEO記事「【デモで試せる】Claude Codeで勤怠・シフト管理システムを自作する方法と費用｜動画つき資料【2026年】」に組み込む実演デモ

---

## このドキュメントの使い方（Claude Code へ）

これ1枚が、規約・仕様・タスクのすべて。リポジトリ直下に `CLAUDE.md` として置き、毎回読み込むこと。

- **実装は「12. 実装タスク」の順に進める。フェーズを飛ばさない。**
- 各タスクの括弧内は要件ID。着手前に該当章を読むこと。
- **仕様がここに書かれていない場合は、推測で実装せず質問する。**
- スコープ外（3章 Won't have）は、思いついても実装しない。
- 「デモだから」を理由に品質を落とす判断はしない。
- **この領域だけの特別ルール：計算ロジックを推測で書かない。** 割増賃金・休憩・36協定の判定は法令に基づく。仕様が曖昧なまま実装すると、**動くが違法な計算**が生まれる。曖昧なら必ず質問する（1章）。

> **姉妹プロジェクトとの関係**
> HIREBASE（求人）／ RELATE（顧客管理）／ CASTA（動画配信）／ FIELDPIN（位置情報）／ KANADE（音楽）／ MIWAKE（画像認識）／ KAMIWAZA（美容室予約）／ KURA（在庫管理）に続くシリーズ。**並べて「同じテンプレ」と思われた時点で失敗。**
> **特に RELATE / KURA との差別化に注意。**3本とも「白背景・高密度・業務システム」だが、
> RELATE＝罫線と等幅数字で表を読ませる（藍）／ KURA＝各行に在庫の水位バーを描く（ティール＋アンバー・縦長数字）／
> **TOKIWA＝1日を24時間の帯として横に描く。時間の重なりが図で見える（インディゴ＋コーラル・等幅数字＋大きな時刻表示）。**
> 8章のデザイン要件は意図的に別方向に振っている。トークンを流用しないこと。

---

## 目次

1. [プロジェクト概要](#1-プロジェクト概要)
2. [アーキテクチャ方針](#2-アーキテクチャ方針)
3. [スコープ定義](#3-スコープ定義)
4. [ロールとデモ切替](#4-ロールとデモ切替)
5. [画面一覧とユーザーフロー](#5-画面一覧とユーザーフロー)
6. [機能要件](#6-機能要件)
7. [データ設計](#7-データ設計)
8. [デザイン要件](#8-デザイン要件)
9. [技術要件・ディレクトリ構成](#9-技術要件ディレクトリ構成)
10. [非機能要件・法令](#10-非機能要件法令)
11. [費用設計](#11-費用設計)
12. [実装タスク](#12-実装タスク)
13. [受入基準](#13-受入基準)

---

# 1. プロジェクト概要

## 背景と目的

「勤怠・シフト管理を作りたい」という相談の背景は、ほぼ次のいずれか。

- タイムカードとExcelで運用しており、**月末の集計に何日もかかる**
- シフトをLINEで調整していて、**希望の取りまとめと確定の連絡が毎月地獄**
- 残業時間の把握が月末までできず、**気づいたら上限を超えていた**

そして、この領域には**他のどのテーマにもない特殊性が3つある。**

### 特殊性①：**間違えると法令違反になる**

在庫が1個ズレても事故だが、労働時間の計算がズレると**未払賃金・36協定違反**になる。しかも従業員全員に影響し、遡って修正が必要になる。

> **勤怠システムは「動けばいい」が最も通用しない領域。**
> 割増賃金の計算式、休憩の付与義務、時間外の上限——これらは**設計者の裁量ではなく法令で決まっている**。

そして厄介なのは、**画面上は正しく動いているように見えること**。だから計算ロジックは純粋関数として切り出し、テストで固める。それがこのプロジェクトの中核（2章）。

### 特殊性②：**「打刻」は生データ。「勤務時間」は解釈**

打刻を集計すれば勤務時間になる——ならない。間に**大量の解釈**が挟まる。

```
打刻（生データ）  9:03 出勤 / 12:01 休憩開始 / 12:58 休憩終了 / 19:47 退勤
        ↓ ①丸め（15分単位？切り捨て？切り上げ？→ 切り捨ては違法になりうる）
        ↓ ②所定労働時間との突き合わせ（9:00-18:00 / 休憩60分）
        ↓ ③休憩の実績と法定休憩の充足チェック
        ↓ ④遅刻・早退の判定
        ↓ ⑤法定内残業と法定外残業の切り分け
        ↓ ⑥深夜（22:00-5:00）の抽出
        ↓ ⑦休日労働（法定休日／所定休日）の判定
        ↓ ⑧割増率の適用と重複（深夜×休日 等）
勤務時間（解釈済み）  所定8h / 法定外残業1.75h / 深夜0h / 休憩57分（法定60分に3分不足）
```

**この①〜⑧のどれか1つを間違えると、給与が変わる。** そして各社で①②⑦のルールが違う。**だから設定で切り替えられる構造にしなければならない。**

### 特殊性③：**シフトと勤怠は別物だが、繋がっている**

| | シフト | 勤怠 |
|---|---|---|
| 何を扱うか | **予定**（これから働く予定） | **実績**（実際に働いた記録） |
| 誰が作るか | 管理者（希望を集めて組む） | 従業員（打刻する） |
| 目的 | 人員配置・人件費の予算管理 | 給与計算・法令遵守 |

**多くの失敗は「シフト＝勤怠」と考えて作ること。** シフト通りに働くことは稀で、**予定と実績の差分こそが管理対象**。TOKIWA はこの2つを別のデータとして持ち、**常に並べて見せる**。

TOKIWA はこの3つの特殊性を前提に設計する。

| 目的 | 内容 |
|---|---|
| 実装力の証明 | 打刻・丸め・残業区分・深夜・休日・36協定・シフト希望収集・自動シフト生成・有給まで、実運用に耐える勤怠エンジンを見せる |
| **期待値の正常化** | **「打刻は生データ、勤務時間は解釈」**を実物で示す。設定を変えると計算結果が変わることを体験させる |
| **法令リスクの可視化** | 36協定の残り時間、法定休憩の不足、割増の内訳を画面上で示す |
| 費用判断の材料 | **人数課金の既製サービスと自作の損益分岐**、打刻ハードウェアの選択肢を示す |

## プロダクト定義

| 項目 | 内容 |
|---|---|
| プロダクト名 | TOKIWA（時輪）※仮称 |
| 一言定義 | 打刻の生データから勤務時間を組み立て、シフトの予定と実績を並べて管理するシステム |
| 想定業態 | 飲食・小売（シフト制）、介護・医療（夜勤あり）、製造（交替勤務）、士業・IT（固定時間＋残業） |
| 提供形態 | レスポンシブWebアプリ（PWA）。**フロントエンド完結（サーバー側の永続化なし）** |
| 主戦場 | **従業員はスマホ**（打刻・シフト希望提出）。**管理者はPC**（シフト作成・集計・承認） |
| 想定利用者 | 記事の読者。登録なしで、従業員側と管理者側の両方を体験できる |

## プロダクトコンセプト

> **「打刻を書き換えない。解釈を見せる。」**
>
> TOKIWA は打刻の生データを一切変更しない。丸めも、残業区分も、割増も、**すべて生データからの算出結果として提示する**。だから「なぜこの時間になったか」が常に遡れ、設定を変えれば即座に再計算される。

## デモとしての成功条件

1. **打刻と解釈が分かれて見える** — 生の打刻と、丸め後・区分後の時間が同じ画面に並んでいる
2. **設定を変えると計算が変わる** — 丸め単位を「1分」から「15分切り捨て」に変えると、残業時間と割増額が変わる
3. **法令の警告が出る** — 法定休憩の不足、36協定の上限接近が具体的な数字で警告される
4. **シフトと実績が並ぶ** — 予定と実績の差分が1画面で分かる
5. **ロールで見え方が変わる** — 従業員は自分だけ、管理者はチーム全体。給与額は管理者のみ
6. **リセットできる** — 誰が触った後でも初期状態に戻せる

---

# 2. アーキテクチャ方針

## 基本方針

**バックエンドとデータベースを持たない。すべてブラウザ内で完結させる。**

| 一般的な構成 | 本プロジェクト |
|---|---|
| PostgreSQL | シードデータ + ブラウザ内ストア |
| 認証・SSO | デモ用ロール切替（ワンクリック） |
| REST / GraphQL API | ストア上の同期的な操作 |
| ICカード／生体認証の打刻機 | **Web打刻（ボタン）+ 位置情報の任意付与** |
| 給与計算システム連携 | CSVエクスポートで代替 |
| メール・LINE通知 | **送信内容のプレビュー表示**で再現 |
| **労働時間の計算** | **これは本物。** 生打刻からの算出をすべて実装する |

## なぜこの構成にするか

- **LPデモとして最適** — 訪問者は登録もログインもせずに、全機能を即座に触れる
- **打刻データが外に出ない** — 労働時間は従業員の個人情報。データを預からないことが訴求になる
- **開発速度** — 認証・APIの実装が不要になり、**計算ロジックとUIの作り込みに全時間を投下できる**
- **運用コストゼロ** — Vercel の静的配信のみ

## 計算エンジンの原則（**最重要の設計判断**）

**打刻を書き換えない。すべて生データからの算出結果として持つ。**

```
[ 生打刻 ]  ← 絶対に変更しない（訂正は「訂正申請＋承認」の別レコード）
    ↓
[ 丸め ]           丸め単位・方向を設定で切替
    ↓
[ 休憩の突き合わせ ]  実績休憩 vs 法定休憩の充足判定
    ↓
[ 所定との比較 ]     遅刻・早退・所定内労働
    ↓
[ 残業の区分 ]      法定内残業 / 法定外残業
    ↓
[ 時間帯の抽出 ]    深夜（22:00-5:00）／休日（法定／所定）
    ↓
[ 割増の適用 ]      率の重複を正しく合成
    ↓
[ 日次サマリ ] → [ 月次集計 ] → [ 36協定チェック ]
```

| ID | 規約 |
|---|---|
| CAL-01 | **打刻レコード（`Punch`）を編集・削除しないこと。** 訂正は `PunchCorrection`（申請＋承認）として別レコードで持ち、算出時に適用する |
| CAL-02 | **労働時間を保存しないこと。** `lib/attendance/` で常に生打刻と設定から算出する |
| CAL-03 | **`lib/attendance/` は純粋関数のみ。** ストア・DOM に依存させず、**現在時刻・就業規則設定を引数で受け取ること** |
| CAL-04 | 算出結果はメモ化してよいが、**メモ化のキーに「打刻の世代番号」と「就業規則設定のバージョン」の両方を含めること**。設定変更で必ず無効化されること |
| CAL-05 | **時刻は「その日の0時からの分数」で内部処理すること。** 日跨ぎ勤務は 24:00 超（1500分 = 翌1:00）で表現し、文字列の切り貼りをしないこと |
| CAL-06 | **「なぜこの時間になったか」の内訳を必ず返すこと**（丸め前後、区分ごとの時間、適用した割増率、根拠となった設定） |
| CAL-07 | **法令に関わる判定（法定休憩・36協定・割増率）を、UIコンポーネント側に書かないこと。** 必ず `lib/attendance/` から返す |

**この設計が、記事の技術パートの中核になる。** 「打刻を丸めて保存する」設計との違いを説明できることが専門性になる。

## 差し替え可能性の担保

**データアクセスは必ず `lib/repo/` のリポジトリ層を経由すること。** コンポーネントからストアを直接触らない。

```
[ コンポーネント ]
        ↓  呼ぶのはこの層だけ
[ lib/repo/*.ts ]     ← 全メソッドを async にしておく
        ↓
[ lib/attendance/ ]   ← 労働時間の算出（純粋関数）
[ lib/shift/ ]        ← シフト生成・充足判定（純粋関数）
[ lib/store/*.ts ]    ← Zustand + persist（localStorage）
        ↓
[ lib/seed/*.ts ]     ← 初期データ
```

リポジトリの各メソッドは、中身が同期処理でも **`async` で定義し、`await` で呼ぶ**。実案件へ転用する際、リポジトリの実装だけをAPI呼び出しに差し替えれば、UI層は一行も変更せずに済む。

## 記事への組み込み

| 段 | 配置 | 中身 |
|---|---|---|
| ① 記事内インライン | 記事の冒頭〜中盤、`<iframe>` | **1日の打刻タイムライン + 丸め設定トグル。** 設定を変えると勤務時間と割増額が変わる |
| ② 全画面デモ | 「全画面で試す」カード → **別タブ** | アプリ全体（シフト作成・36協定・有給・月次集計・承認） |

**記事内で見せるのは「設定を変えると計算結果が変わること」だけに絞る。** これが1章の特殊性②そのものであり、スクロール中に一目で伝わる唯一の絵になる。

| ID | 要件 |
|---|---|
| EMB-01 | 記事内 iframe は `loading="lazy"` とし、ビューポート接近まで読み込まないこと |
| EMB-02 | 埋め込みモードのJS初期バンドルは 60KB以下（gzip後）とすること |
| EMB-03 | 埋め込み iframe が記事の LCP 要素にならないこと |
| EMB-04 | **埋め込みモードで位置情報（Geolocation）を呼ばないこと。** 打刻ボタンも押させないこと（表示のみ） |
| EMB-05 | 埋め込みモードでは、ヘッダー・ナビ・ロール切替バーを非表示とし、**アプリ内遷移を禁止すること**。「全画面で試す」は `target="_blank"` |
| EMB-06 | **設定トグル（丸め単位・丸め方向）の切替による再計算は 50ms以下**で完了すること |
| EMB-07 | GA4 で計測すること：`demo_open` / `rounding_changed`（**丸め設定を変えた**）／`punch_posted` / `shift_generated` / `overtime_alert_viewed` / `role_switch` / `doc_download` / `form_click` / `line_click` |
| EMB-08 | **`rounding_changed` は最重要指標。** 「読者が設定を変えて計算差を自分で確かめたか」が、この記事の主張が届いたかを表す |
| EMB-09 | **氏名・打刻データ・給与額を GA4 に送らないこと** |
| EMB-10 | `?embed=1` のURLは `noindex` とすること。デモ本体は固有の title / description を持つこと |

## 永続化の範囲

| 対象 | 挙動 |
|---|---|
| シードデータ（従業員・部署・就業規則・シフト・過去3ヶ月の打刻） | 初期投入。マスタは編集可能 |
| **打刻・訂正申請・シフト希望・シフト・休暇申請・承認** | localStorage に保存。**リロードしても残る** |
| **就業規則の設定変更** | localStorage に保存。**変更すると過去分も再計算される**（バージョン管理付き） |
| リセット | 「デモをリセット」で localStorage をクリア |

**擬似ディレイ** は 150〜400ms。ただし**打刻・労働時間の再算出・一覧の絞り込みは 100ms以下**とし、ディレイを入れない（打刻でもたつくのは致命的な体験）。

---

# 3. スコープ定義

## 打刻・勤怠（従業員）

| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-P01 | **打刻** | 出勤／退勤／休憩開始／休憩終了。**現在の勤務状態に応じてボタンが変わる** | Must |
| F-P02 | 位置情報の任意付与 | 打刻時に位置を記録（設定でON/OFF、拒否しても打刻可） | Should |
| F-P03 | **本日の勤務状況** | 現在の勤務時間、休憩残り、退勤予定時刻、残業見込み | Must |
| F-P04 | **月次勤務一覧** | 日別の打刻・勤務時間・残業・区分。**丸め前後の両方を表示** | Must |
| F-P05 | **勤務日詳細** | 打刻タイムライン、丸めの適用、区分ごとの内訳、**計算の根拠** | Must |
| F-P06 | **打刻訂正申請** | 打刻漏れ・誤打刻の訂正を申請。**生打刻は変更しない** | Must |
| F-P07 | **残業時間の見える化** | 当月の累計残業、36協定の上限までの残り時間 | Must |
| F-P08 | 休暇申請 | 有給・特別休暇・欠勤。半休・時間単位有給に対応 | Must |
| F-P09 | 有給残日数の確認 | 付与日、残日数、失効予定日、取得義務（年5日）の進捗 | Must |
| F-P10 | オフライン打刻 | 圏外でも打刻でき、復帰時に同期 | Should |

## シフト（従業員）

| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-S01 | **シフト希望の提出** | 期間内の希望を入力（勤務可／不可／時間指定）。締切管理 | Must |
| F-S02 | **確定シフトの確認** | 自分のシフト（月／週表示）、変更通知 | Must |
| F-S03 | シフト交代の申請 | 他メンバーへの交代依頼、相手の承諾、管理者承認 | Should |
| F-S04 | シフトのics出力 | 自分のシフトをカレンダーに取り込む | Could |

## シフト作成・勤怠管理（管理者）

| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-M01 | **シフト表（作成・編集）** | 従業員×日付のグリッド。**ドラッグで割当、必要人数の充足を色で表示** | Must |
| F-M02 | **必要人数の設定** | 曜日別・時間帯別の必要人数。**過不足をリアルタイム表示** | Must |
| F-M03 | **シフト自動生成** | 希望・スキル・必要人数・法令制約から自動割当。**手修正前提の下書き** | Must |
| F-M04 | **人件費の見込み** | シフト確定前に人件費を試算。予算との差分 | Must |
| F-M05 | シフトの公開 | 確定して従業員に通知。公開後の変更履歴 | Must |
| F-M06 | **日次の勤務状況** | 本日の出勤状況、未打刻者、遅刻、退勤忘れの検出 | Must |
| F-M07 | **予実比較** | シフト（予定）と打刻（実績）の差分。人件費の予実 | Must |
| F-M08 | **打刻訂正の承認** | 申請一覧、訂正内容の差分、承認／差戻し | Must |
| F-M09 | 休暇申請の承認 | 申請一覧、残日数の確認、承認／差戻し | Must |
| F-M10 | **月次集計・確定** | 従業員別の労働時間集計、月次締め、確定後のロック | Must |
| F-M11 | **給与計算用CSV出力** | 基本・法定内残業・法定外残業・深夜・休日・遅刻早退・有給の集計 | Must |
| F-M12 | **36協定モニタリング** | 月45h／年360h、複数月平均80h、単月100h の各上限に対する進捗 | Must |
| F-M13 | **有給の取得義務管理** | 年5日取得義務の進捗、未達者の抽出、失効予定の警告 | Must |
| F-M14 | 従業員マスタ | 雇用形態、所定労働時間、時給／月給、スキル、部署 | Must |
| F-M15 | **就業規則の設定** | 丸め、所定時間、休憩ルール、割増率、法定休日、変形労働 | Must |
| F-M16 | 部署・拠点管理 | 部署、拠点、必要人数のテンプレート | Should |
| F-M17 | 操作ログ | 誰がいつ何を承認・訂正・確定したか | Should |

## 共通・基盤

| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-C01 | ロール切替 | 従業員／管理者（店長）／管理部 をワンクリック切替 | Must |
| F-C02 | 埋め込みモード | `?embed=1` で打刻タイムライン + 丸め設定（2章） | Must |
| F-C03 | 通知 | シフト公開、承認結果、打刻忘れ、上限接近（**プレビュー表示で再現**） | Must |
| F-C04 | 保存ビュー | 絞り込み + 表示列 + 並び順を保存 | Should |
| F-C05 | 記事への導線 | 元記事へのリンク、資料ダウンロード、問い合わせ | Must |
| F-C06 | デモリセット | localStorage をクリア | Must |
| F-C07 | ガイドツアー | 初回訪問時に「何を試せるか」を3ステップで案内 | Should |

## 対象外（Won't have）

| 項目 | 理由 |
|---|---|
| データベース・バックエンドAPI | 2章の方針に基づく |
| 本物の認証・SSO | ロール切替で代替 |
| **給与計算そのもの**（所得税・社会保険・年末調整） | **労働時間の集計とCSV出力までが対象。** 給与計算は専門領域であり別システム |
| ICカード・生体認証・打刻機との連携 | Web打刻で代替。**ハード比較は11章で扱う** |
| 給与計算ソフト・人事システムとのAPI連携 | CSV出力で代替。**個別案件のオプション** |
| **フレックスタイム制の清算期間管理** | 対象外。**要件定義段階で洗い出す論点として扱う** |
| **1年単位の変形労働時間制** | **1ヶ月単位のみ**実装。1年単位は年間カレンダーの設計が別物 |
| 管理監督者・裁量労働制の特殊な取り扱い | 従業員区分としては持つが、計算の特例は対象外 |
| 実際のメール・LINE・プッシュ送信 | プレビュー表示で再現 |
| 労務相談・法令アドバイス | **本デモは計算の実装例であり、法令解釈の保証はしない**（10章） |
| 多言語UI | 日本語のみ |

> **スコープリスク：** 勤怠は「うちの就業規則がこうで」が最も多い領域。**変形労働時間制・フレックス・みなし残業（固定残業代）・深夜割増の重複・法定休日の特定方法・週40時間の起算曜日**は会社ごとに違い、**計算の根幹に影響する**。後付けは全期間の再計算になるため、要件定義の段階で必ず洗い出す。資料PDFの「就業規則ヒアリングシート」はこの洗い出しのために作る。

---

# 4. ロールとデモ切替

ロール設計は、そのまま実案件での権限制御の設計根拠になる。勤怠は**個人情報と給与情報を扱うため、権限設計を誤ると事故になる。**

## ロール定義

| ロールID | 名称 | 見えるデータ | 特徴的な権限 |
|---|---|---|---|
| `employee` | 従業員 | **自分の打刻・勤務時間・シフト・有給のみ** | 打刻、訂正申請、休暇申請、シフト希望提出 |
| `manager` | 管理者（店長） | **自部署のメンバー全員** | シフト作成・公開、訂正/休暇の承認、日次確認、予実比較 |
| `hr` | 管理部 | **全社** | 上記 + 就業規則設定・月次確定・給与CSV出力・36協定監視・操作ログ |

## データスコープと項目スコープ（デモの見せ場）

**勤怠では「見える行」だけでなく「見える列」が権限で厳格に分かれる。** 他人の給与額が見えるのは重大な事故。

- 従業員で勤務一覧 → **自分の1ヶ月分のみ。時給・給与額は自分の分だけ**
- 管理者に切替 → **自部署8名。時給と人件費が出現する**（他部署は見えない）
- 管理部に切替 → **全社42名。就業規則設定と給与CSV出力が出現する**

| ID | 要件 |
|---|---|
| SCP-01 | **データスコープ（見える行）と項目スコープ（見える列）を、ともに `lib/repo/_scope.ts` に集約すること** |
| SCP-02 | **コンポーネント側で `role === 'employee'` のような分岐を書かないこと** |
| SCP-03 | 時給・給与額・人件費は、権限がない場合**そもそもデータに含めずに返すこと**（CSSで隠さない） |
| SCP-04 | **従業員は、他人の打刻・勤務時間を一切取得できないこと。** リポジトリが空配列を返すこと |

## ロールを跨ぐ体験（必ず動くようにする）

1. **従業員で打刻する** → 管理者に切替 → **日次の勤務状況にその打刻が反映され、予実比較の実績が動く**
2. **管理部が丸め設定を「1分」→「15分切り捨て」に変える** → 従業員に切替 → **同じ打刻なのに勤務時間と残業が減っている**（過去分も再計算される）
3. **従業員が打刻訂正を申請** → 管理者に切替 → **承認待ちに出る。承認するまで勤務時間は変わらない** → 承認 → **勤務時間が訂正後で再計算される**
4. **管理者がシフトを組む** → **必要人数の不足が赤で表示され、人件費の見込みが更新される** → 公開 → 従業員に切替 → **自分のシフトに反映されている**
5. **従業員が残業を積む** → 管理部に切替 → **36協定モニタリングでその人の残り時間が減り、上限接近が警告される**
6. **管理部が月次を確定する** → 従業員に切替 → **その月の打刻が編集不可（ロック）になっている**

**2番と3番が、このデモの核心。** 1章の特殊性②が体験として成立する瞬間。

## デモ切替バー（F-C01）

画面上部に常時表示する固定バー。**埋め込みモードでは非表示。**

- 現在のロールと氏名・部署の表示、3ロールの切替
- 従業員ロール時：3名から操作主体を選択（**残業が多い人／有給未取得の人／新人 を用意**）
- **デモ内の現在日時**（シードが相対日付のため基準を示す）
- 「デモをリセット」ボタン（確認ダイアログ付き）
- 元記事へ戻るリンク
- 「これはデモです。打刻データは送信されません」の明示

**デザイン上の扱い：** プロダクト本体のUIとは意図的に別扱い（ダークな帯 + 小さめのタイポ）にし、「アプリの外側にある操作パネル」であることを視覚的に区別する。

---

# 5. 画面一覧とユーザーフロー

全34画面。画面IDはディレクトリ構成と 1:1 で対応させる。

## 従業員側

| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-001 | **打刻・ホーム** | `/` | 大きな打刻ボタン、本日の勤務時間、休憩残り、退勤予定、今月の残業累計 |
| SC-010 | **月次勤務一覧** | `/attendance` | 日別の打刻・勤務時間・残業区分。**丸め前後の両方**、合計行 |
| SC-011 | **勤務日詳細** | `/attendance/[date]` | **打刻タイムライン（24h帯）**、丸めの適用、区分内訳、**計算の根拠** |
| SC-012 | 打刻訂正申請 | `/attendance/[date]/correct` | 訂正内容、理由、差分プレビュー |
| SC-013 | 申請状況 | `/requests` | 訂正・休暇の申請一覧、承認状況 |
| SC-020 | **残業サマリ** | `/overtime` | 当月累計、36協定の上限までの残り、月別推移 |
| SC-030 | 休暇申請 | `/leave/new` | 種別、日付、半休／時間単位、残日数の確認 |
| SC-031 | **有給残日数** | `/leave` | 付与履歴、残日数、失効予定、**年5日取得義務の進捗** |
| SC-040 | **シフト希望の提出** | `/shift/request` | 期間内の希望入力（可／不可／時間指定）、締切カウントダウン |
| SC-041 | **確定シフト** | `/shift` | 月／週表示、自分のシフト、変更通知、ics出力 |
| SC-042 | シフト交代の申請 | `/shift/swap` | 相手選択、依頼、承諾状況 |
| SC-050 | 通知センター | `/notifications` | シフト公開、承認結果、打刻忘れ、上限接近 |
| SC-900 | 埋め込みモード | `/?embed=1` | **打刻タイムライン + 丸め設定トグル**（2章） |

## 管理者側

| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-100 | **日次ダッシュボード** | `/admin` | 本日の出勤状況、未打刻者、遅刻、退勤忘れ、承認待ち件数 |
| SC-110 | **シフト表** | `/admin/shift` | **従業員×日付グリッド**、ドラッグ割当、**必要人数の過不足を色表示** |
| SC-111 | **時間帯別の充足** | `/admin/shift/coverage` | **横軸=時間、縦軸=日付。必要人数と配置人数のヒートマップ** |
| SC-112 | 必要人数の設定 | `/admin/shift/demand` | 曜日別・時間帯別の必要人数、テンプレート |
| SC-113 | **シフト自動生成** | `/admin/shift/generate` | 制約の確認、生成、**採用しなかった理由の提示** |
| SC-114 | **人件費の見込み** | `/admin/shift/cost` | シフト確定前の試算、予算との差分、従業員別内訳 |
| SC-115 | シフトの公開 | モーダル | 対象期間、通知プレビュー、公開履歴 |
| SC-116 | シフト希望の集計 | `/admin/shift/requests` | 提出状況、未提出者、希望の可視化 |
| SC-120 | **勤怠一覧（部署）** | `/admin/attendance` | メンバー×日付、勤務時間、残業、**異常の強調** |
| SC-121 | **予実比較** | `/admin/attendance/variance` | シフト（予定）と打刻（実績）の差分、人件費の予実 |
| SC-130 | **打刻訂正の承認** | `/admin/approvals/corrections` | 申請一覧、**訂正前後の差分と再計算結果**、承認／差戻し |
| SC-131 | 休暇申請の承認 | `/admin/approvals/leave` | 申請一覧、残日数、シフトへの影響、承認／差戻し |
| SC-132 | シフト交代の承認 | `/admin/approvals/swap` | 交代内容、法令制約のチェック |
| SC-140 | 従業員一覧（部署） | `/admin/members` | 勤務状況、当月残業、有給残、スキル |

## 管理部（HR）

| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-200 | **月次集計** | `/hr/monthly` | 従業員別の労働時間集計、未確定の異常、**確定ボタン** |
| SC-201 | **給与CSV出力** | `/hr/export` | 出力項目の確認、期間、プレビュー、ダウンロード |
| SC-202 | **36協定モニタリング** | `/hr/compliance` | 月45h／年360h／複数月平均80h／単月100h の進捗、**上限接近者の抽出** |
| SC-203 | **有給の取得義務管理** | `/hr/leave-compliance` | 年5日義務の進捗、未達者、失効予定 |
| SC-204 | **就業規則の設定** | `/hr/settings/rules` | **丸め、所定時間、休憩、割増率、法定休日、変形労働**。設定バージョン履歴 |
| SC-205 | 従業員マスタ | `/hr/employees` | 雇用形態、所定労働時間、時給／月給、スキル、部署、入社日 |
| SC-206 | 部署・拠点 | `/hr/orgs` | 部署、拠点、必要人数テンプレート |
| SC-207 | 休日カレンダー | `/hr/settings/calendar` | 法定休日、所定休日、祝日、年間カレンダー |
| SC-208 | 通知文面の設定 | `/hr/settings/notifications` | シフト公開・承認・打刻忘れ・上限接近の文面 |
| SC-209 | 操作ログ | `/hr/logs` | 操作者・日時・対象・内容の検索 |
| SC-210 | **計算ルールの検証** | `/hr/settings/verify` | **サンプル打刻に対する計算結果の一覧。設定変更の影響を確認** |

## 主要フロー

### フローA：記事の読者が「打刻と解釈の違い」を理解する（**最重要**）

```
SEO記事を読んでいる
   ↓
記事内の埋め込み（SC-900）
┌──────────────────────────────────────────────────────────┐
│ 2026-08-20（木）  田中さんの1日                            │
│                                                          │
│  0    6      9        12      15      18       21    24   │
│  ├────┼──────┼────────┼───────┼───────┼────────┼─────┤   │
│         ●9:03━━━━━━━━━━▓▓━━━━━━━━━━━━━━━━━━━━●19:47      │
│                     休憩57分                              │
│                                                          │
│  丸め設定  [ 1分単位 ▼ ]                                  │
│                                                          │
│  ┌── 生打刻 ──────────┐  ┌── 解釈後 ──────────────┐      │
│  │ 出勤  9:03          │  │ 所定内      8:00      │      │
│  │ 休憩 12:01-12:58    │  │ 法定外残業  1:47      │      │
│  │ 退勤 19:47          │  │ 深夜        0:00      │      │
│  │ 実働 9:47           │  │ 休憩不足    3分 ⚠     │      │
│  └────────────────────┘  └───────────────────────┘      │
│                                                          │
│  当日の割増賃金相当  ¥2,900                               │
└──────────────────────────────────────────────────────────┘
   ↓
丸め設定を「1分単位」→「15分単位・切り捨て」に変える
   ↓  ★ 同じ打刻なのに、法定外残業が 1:47 → 1:30 に減る
   ★ 割増賃金相当が ¥2,900 → ¥2,450 に減る
   ★ 同時に注意が出る：「切り捨ての丸めは、実労働時間の一部を
      賃金計算から除外することになり、適法性に注意が必要です」
   ↓
「休憩不足 3分 ⚠」にホバー
   ↓  ★「8時間超の労働には60分以上の休憩が必要です（実績57分）」
   ↓
「全画面で試す」→ 別タブ
   ↓
SC-011 勤務日詳細 ── ★ 計算の根拠が全ステップ表示される
   ↓
ロール切替「管理部」→ SC-204 就業規則の設定
   ↓  ★ 丸め単位を変えると、SC-210 で全サンプルの計算結果が変わる
SC-202 36協定モニタリング ── ★ 上限接近者が具体的な残り時間で表示される
   ↓
資料ダウンロード or 元記事に戻る
```

### フローB：打刻訂正の申請と承認（**1章の特殊性②の実装を見せる**）

```
【従業員】SC-010 月次勤務一覧
  8月18日：退勤打刻がない（打刻忘れ）→ 行が警告色
   ↓
SC-012 打刻訂正申請
  退勤時刻を「19:30」で申請、理由：「打刻忘れ」
   ↓  ★ 差分プレビューが出る
     現在：退勤なし → 勤務時間 算出不能
     訂正後：退勤 19:30 → 所定内 8:00 / 法定外残業 1:30
   ↓
申請
   ↓  ★ 生打刻は一切変更されていない（PunchCorrection として別レコード）
【管理者】SC-130 打刻訂正の承認
  申請一覧に出る
  ★ 訂正前後の差分と、再計算後の勤務時間が並んで表示される
  ★ 承認するまで勤務時間は変わらない
   ↓
承認
   ↓
【従業員】SC-011 勤務日詳細
  ★ 勤務時間が訂正後で再計算されている
  ★ 「打刻タイムライン」に、生打刻（点線）と訂正後（実線）の両方が描かれる
  ★ 「訂正あり（8/19 管理者が承認）」のバッジが付く
   ↓
【管理部】SC-209 操作ログ
  ★ 誰がいつ何を訂正・承認したかが記録されている
   ↓
★ ここで伝わること：
  「打刻は消さない。訂正は履歴として残す。だから後から検証できる」
```

### フローC：シフトを組んで、実績と比べる

```
【管理者】SC-112 必要人数の設定
  金曜 17:00-22:00 は 4名必要、平日昼は 2名
   ↓
SC-116 シフト希望の集計 ── 提出8名／未提出2名（催促の通知プレビュー）
   ↓
SC-113 シフト自動生成
  制約：希望・必要人数・スキル（レジ可）・法令（週40h、連続勤務日数）
  ↓  ★ 生成される。ただし「下書き」
  ★ 採用しなかった理由が出る：
    「8/22(金) 17-22時：1名不足。希望提出者の中に配置可能な人がいません」
   ↓
SC-110 シフト表 ── ドラッグで手修正
  ★ 必要人数を満たすと緑、不足すると赤で表示される
  ★ 法令違反になる割当は弾かれる（週40h超、連続7日勤務）
   ↓
SC-111 時間帯別の充足 ── ★ 横軸=時間のヒートマップで穴が一目で分かる
   ↓
SC-114 人件費の見込み
  ★ このシフトだと ¥842,000（予算 ¥800,000 に対し +5.3%）
  ★ 深夜割増が効いている日が強調される
   ↓
SC-115 公開 ── 通知プレビューを確認して公開
   ↓
【従業員】SC-041 確定シフト ── ★ 自分のシフトに反映されている
   ↓
（数日後）実際に働いて打刻が積まれる
   ↓
【管理者】SC-121 予実比較
  ★ シフト（予定）と打刻（実績）が並ぶ
    8/22(金)  予定 17:00-22:00（5.0h） / 実績 16:52-22:34（5.5h）
    → 予定より +0.5h、人件費 予実差 +¥1,200
  ★ 月間：予定 ¥842,000 → 実績 ¥871,300（+3.5%）
   ↓
★ ここで伝わること：
  「シフトは予定、打刻は実績。差分こそが管理対象」
```

---

# 6. 機能要件

各要件はテスト仕様書の項目と1:1で対応する。「〜できること」の粒度で、判定可能な形で記述している。

## 6.1 労働時間の算出（**このデモの心臓部**）

| ID | 要件 | 優先 |
|---|---|---|
| FR-101 | **打刻レコードを編集・削除しないこと。** 訂正は別レコード（`PunchCorrection`）として保持し、算出時に適用すること（CAL-01） | P1 |
| FR-102 | **労働時間を保存せず、生打刻と就業規則設定から常に算出すること**（CAL-02） | P1 |
| FR-103 | **丸めの単位（1分／5分／10分／15分／30分）と方向（丸めなし／切り上げ／切り捨て／四捨五入）を設定でき、算出に反映されること** | P1 |
| FR-104 | **丸めの方向を「切り捨て」に設定した場合、適法性に注意が必要である旨の警告を設定画面に表示すること**（10章） | P1 |
| FR-105 | 出勤・退勤で丸め方向を別に設定できること（出勤は切り上げ、退勤は切り捨て 等の設定を再現できること） | P2 |
| FR-106 | **法定休憩の充足を判定すること**（労働6時間超で45分以上、8時間超で60分以上）。不足時は不足分を明示して警告すること | P1 |
| FR-107 | 実績休憩が未打刻の場合、所定休憩を自動控除するか、警告するかを設定で選べること | P1 |
| FR-108 | 所定労働時間との比較で、遅刻・早退・所定内労働時間を算出すること | P1 |
| FR-109 | **法定内残業と法定外残業を切り分けること**（所定労働時間 < 法定労働時間 の場合、その差分は法定内残業） | P1 |
| FR-110 | **深夜労働（22:00〜翌5:00）の時間を抽出すること。** 日跨ぎ勤務でも正しく計算されること | P1 |
| FR-111 | **休日労働を、法定休日と所定休日で区別して判定すること。** 割増率が異なること | P1 |
| FR-112 | **割増率の重複を正しく合成すること**（法定外残業×深夜、休日×深夜 等）。**適用した率と根拠を内訳として返すこと** | P1 |
| FR-113 | 割増率は設定で変更できること（法定を下回る値を設定した場合は警告すること） | P1 |
| FR-114 | **週40時間超の労働を判定すること。** 週の起算曜日を設定できること | P1 |
| FR-115 | **1ヶ月単位の変形労働時間制に対応すること**（対象期間の総枠との比較で時間外を算出） | P2 |
| FR-116 | **日跨ぎ勤務（夜勤）を正しく扱うこと。** 労働日の帰属ルール（始業日に帰属）を設定できること | P1 |
| FR-117 | **「なぜこの時間になったか」の内訳を必ず返すこと**（丸め前後、区分ごとの時間、適用率、根拠設定）(CAL-06) | P1 |
| FR-118 | **`lib/attendance/` を純粋関数とし、現在時刻と就業規則設定を引数で受け取ること**（CAL-03） | P1 |
| FR-119 | **時刻を分数で内部処理し、日跨ぎは 24:00 超で表現すること**（CAL-05） | P1 |
| FR-120 | 算出をメモ化し、**打刻の追加と就業規則設定の変更の両方で無効化すること**（CAL-04） | P1 |
| FR-121 | **42名 × 3ヶ月分（約2,700勤務日）の月次集計が 200ms以下**であること | P1 |

## 6.2 打刻

| ID | 要件 | 優先 |
|---|---|---|
| FR-201 | **現在の勤務状態に応じて打刻ボタンが変わること**（未出勤→出勤／勤務中→休憩開始・退勤／休憩中→休憩終了） | P1 |
| FR-202 | 打刻は**1タップで完了し、確認ダイアログを出さないこと**（出勤時の摩擦を作らない） | P1 |
| FR-203 | 打刻直後に、**打刻した時刻と現在の勤務時間をその場で表示すること** | P1 |
| FR-204 | 誤打刻を**直後（既定3分以内）なら自分で取り消せること**。それ以降は訂正申請とすること | P1 |
| FR-205 | 二重打刻（勤務中に再度出勤等）を防止し、**現在の状態を明示して案内すること** | P1 |
| FR-206 | 位置情報の付与を設定で ON/OFF でき、**ON でも権限拒否時は打刻できること**（打刻をブロックしない） | P2 |
| FR-207 | 位置情報が指定範囲外の場合、打刻は成功させ、**記録に「範囲外」フラグを付けること** | P2 |
| FR-208 | 退勤打刻がないまま日付が変わった場合、**「退勤忘れ」として検出し、翌日の打刻画面で通知すること** | P1 |
| FR-209 | オフラインでも打刻でき、復帰時に自動同期すること。未同期件数をバッジ表示すること | P2 |
| FR-210 | **月次確定後の期間は打刻・訂正申請ができないこと**（ロック）。理由を表示すること | P1 |
| FR-211 | すべての打刻に、**端末種別・位置（設定時）・記録日時**を残すこと | P1 |

## 6.3 訂正・申請・承認

| ID | 要件 | 優先 |
|---|---|---|
| FR-301 | 打刻訂正を申請でき、**訂正前後の差分と再計算後の勤務時間をプレビュー表示すること** | P1 |
| FR-302 | 訂正には理由の入力を必須とすること（打刻忘れ／誤打刻／システム不具合／その他） | P1 |
| FR-303 | **申請中は勤務時間を変えないこと。** 承認時に初めて算出に反映されること | P1 |
| FR-304 | 管理者は訂正申請を承認／差戻しでき、**差戻しには理由を必須とすること** | P1 |
| FR-305 | 承認後、勤務日詳細に**「訂正あり」のバッジと、誰がいつ承認したかを表示すること** | P1 |
| FR-306 | 勤務日詳細のタイムラインに、**生打刻と訂正後の両方を描画すること**（生打刻を点線、訂正後を実線） | P1 |
| FR-307 | 休暇申請（有給／特別休暇／欠勤、全日／半休／時間単位）ができること | P1 |
| FR-308 | 休暇申請時に**残日数を表示し、不足する場合は警告すること**（ブロックはしない） | P1 |
| FR-309 | 休暇の承認時、**その日のシフトへの影響（人員不足になるか）を表示すること** | P2 |
| FR-310 | 申請一覧で、種別・状態・期間・申請者で絞り込めること | P1 |
| FR-311 | 承認・差戻し・訂正のすべてを操作ログに記録すること | P1 |

## 6.4 シフト作成

| ID | 要件 | 優先 |
|---|---|---|
| FR-401 | シフト表を**従業員×日付のグリッドで表示・編集できること**。セルにシフトパターン（早番／遅番／夜勤 等）を割り当てられること | P1 |
| FR-402 | **ドラッグで割当・コピー・移動ができること。キーボードでも操作できること** | P1 |
| FR-403 | **必要人数（曜日別・時間帯別）を設定でき、シフト表上で過不足を色表示すること** | P1 |
| FR-404 | **時間帯別の充足状況を、横軸=時間・縦軸=日付のヒートマップで表示すること** | P1 |
| FR-405 | **法令違反になる割当を弾くこと**（週40時間超、連続勤務日数の上限、勤務間インターバルの下限）。**理由を表示すること** | P1 |
| FR-406 | **シフト自動生成**：希望・必要人数・スキル・法令制約から下書きを生成すること | P1 |
| FR-407 | **自動生成で埋められなかった枠について、理由を提示すること**（「希望提出者の中に配置可能な人がいません」等） | P1 |
| FR-408 | 自動生成の結果は**必ず下書きとし、公開前に手修正できること**（自動で確定させない） | P1 |
| FR-409 | **人件費の見込みを、シフト確定前に試算すること**（時給×時間＋割増見込み）。予算との差分を表示すること | P1 |
| FR-410 | 深夜・休日の割増が発生する割当を、人件費内訳で強調表示すること | P2 |
| FR-411 | シフトを公開でき、公開時に**通知内容をプレビュー表示すること**（実際には送信しない） | P1 |
| FR-412 | 公開後の変更は履歴として残し、**変更通知をプレビュー表示すること** | P1 |
| FR-413 | シフトパターンのマスタ（名称、開始・終了、休憩、色）を管理できること | P1 |
| FR-414 | 前月のシフトを複製して作成できること | P2 |

## 6.5 シフト希望・交代

| ID | 要件 | 優先 |
|---|---|---|
| FR-501 | 従業員がシフト希望を提出できること（勤務可／不可／時間指定）。**提出締切を設定でき、締切後は提出不可とすること** | P1 |
| FR-502 | 締切までの残り時間を従業員側に表示し、**未提出者に催促通知（プレビュー）を送れること** | P2 |
| FR-503 | 管理者は希望の提出状況（提出済／未提出）と、希望内容を一覧・カレンダーで確認できること | P1 |
| FR-504 | 希望に「上限勤務日数」「週の希望勤務時間」を含められること | P2 |
| FR-505 | シフト交代を申請でき、**相手の承諾 → 管理者承認の2段階とすること** | P2 |
| FR-506 | 交代によって法令違反や人員不足が生じる場合、警告すること | P2 |
| FR-507 | 従業員は確定シフトを月／週表示で確認でき、ics をダウンロードできること | P2 |

## 6.6 集計・法令モニタリング

| ID | 要件 | 優先 |
|---|---|---|
| FR-601 | 従業員別の月次集計を表示すること（所定内／法定内残業／法定外残業／深夜／法定休日／所定休日／遅刻早退／有給／欠勤） | P1 |
| FR-602 | **異常（未打刻・退勤忘れ・休憩不足・上限超過・承認待ち）を月次集計上で強調し、確定前に一覧できること** | P1 |
| FR-603 | **月次確定ができ、確定後は打刻・訂正申請がロックされること。** 確定解除は管理部のみ可能とし、操作ログに記録すること | P1 |
| FR-604 | **給与計算用CSVを出力できること。** 出力項目を確認できるプレビューを表示すること | P1 |
| FR-605 | **36協定モニタリング**：月45時間／年360時間／複数月平均80時間／単月100時間 の各上限に対する進捗を表示すること | P1 |
| FR-606 | **上限に接近している従業員（既定80%超）を抽出し、残り時間を明示すること** | P1 |
| FR-607 | 特別条項の適用回数（年6回まで）をカウントし、表示すること | P2 |
| FR-608 | **有給の年5日取得義務の進捗を表示し、未達者を抽出すること** | P1 |
| FR-609 | 有給の付与（入社日基準／一斉付与）を管理し、**失効予定日を警告すること** | P1 |
| FR-610 | 時間単位有給の上限（年5日相当）を管理すること | P2 |
| FR-611 | 予実比較：シフト（予定）と打刻（実績）の差分を、時間と人件費の両方で表示すること | P1 |
| FR-612 | 部署別・月別の残業時間推移をグラフ表示すること | P2 |
| FR-613 | 日次ダッシュボードで、未打刻者・遅刻・退勤忘れ・承認待ちを一覧できること | P1 |

## 6.7 設定・マスタ

| ID | 要件 | 優先 |
|---|---|---|
| FR-701 | **就業規則を設定できること**：丸め（単位・方向）、所定労働時間、休憩ルール、割増率、法定休日の特定方法、週の起算曜日、変形労働の適用 | P1 |
| FR-702 | **就業規則の設定にバージョンを持たせ、適用開始日を指定できること。** 過去の期間は当時の設定で計算されること | P1 |
| FR-703 | **設定変更時に、影響を受ける従業員数と期間を事前に表示すること** | P1 |
| FR-704 | **SC-210 計算ルールの検証画面で、サンプル打刻に対する計算結果を一覧表示すること。** 設定を変えると即座に結果が変わること | P1 |
| FR-705 | 法定を下回る割増率、切り捨ての丸めを設定した場合、**警告を表示すること**（設定自体はブロックしない） | P1 |
| FR-706 | 従業員マスタで、雇用形態・所定労働時間・時給／月給・スキル・部署・入社日を管理できること | P1 |
| FR-707 | 休日カレンダーで、法定休日・所定休日・祝日を年間で設定できること | P1 |
| FR-708 | 部署・拠点と、必要人数のテンプレートを管理できること | P2 |
| FR-709 | 通知文面（シフト公開・承認結果・打刻忘れ・上限接近）を編集できること | P2 |
| FR-710 | **在庫を動かす操作に相当するもの——打刻訂正の承認・月次確定・就業規則の変更——を操作ログに記録し、検索できること** | P1 |

## 6.8 一覧・デモ基盤

| ID | 要件 | 優先 |
|---|---|---|
| FR-801 | 一覧の表示列をユーザーが選択・並び替えできること。設定は保持されること | P2 |
| FR-802 | 絞り込み条件をURLクエリに反映し、リロード・URL共有で再現できること | P1 |
| FR-803 | 絞り込み条件 + 表示列 + 並び順を「ビュー」として保存できること | P2 |
| FR-804 | 3ロールをワンクリックで切り替えられること | P1 |
| FR-805 | **データスコープと項目スコープを `lib/repo/_scope.ts` に集約すること**（SCP-01, SCP-02） | P1 |
| FR-806 | **時給・給与額・人件費は、権限がない場合データに含めずに返すこと**（SCP-03） | P1 |
| FR-807 | **従業員ロールでは、他人の打刻・勤務時間を一切取得できないこと**（SCP-04） | P1 |
| FR-808 | 「デモをリセット」で localStorage をクリアすること | P1 |
| FR-809 | 操作結果がリロード後も保持されること | P1 |
| FR-810 | デモ内の現在日時を切替バーに表示すること | P2 |
| FR-811 | ロール権限外の画面では案内画面から切り替えられること。素の404を出さないこと | P1 |
| FR-812 | 権限で不可の操作はボタンを非活性にし、理由をツールチップで示すこと | P1 |
| FR-813 | 元記事へ戻るリンクと資料ダウンロードを常設すること | P1 |
| FR-814 | 初回訪問時に3ステップのガイドツアーを表示し、「今後表示しない」を選べること | P2 |
| FR-815 | **埋め込みモード**：打刻タイムライン + 丸め設定トグルのみを表示し、位置情報を呼ばず、打刻もさせず、アプリ内遷移をしないこと（EMB-04, EMB-05） | P1 |

---

# 7. データ設計

## 型定義（`lib/types/`）

```ts
// ---- 組織・従業員 ----
type Org = { id: string; name: string; kind: 'department' | 'site'; parentId?: string }

type EmploymentType = 'full_time' | 'part_time' | 'contract' | 'arbeit'
type WageType = 'monthly' | 'hourly'

type Employee = {
  id: string
  code: string
  name: string                  // ★ 架空
  orgId: string
  role: 'employee' | 'manager' | 'hr'
  employmentType: EmploymentType
  joinedAt: string
  // --- 所定労働（★ 法定内/法定外の切り分けに使う）---
  scheduledStartMin: number     // 540 = 9:00
  scheduledEndMin: number       // 1080 = 18:00
  scheduledBreakMin: number     // 60
  scheduledWorkDays: number[]   // [1,2,3,4,5]
  // --- 賃金（★ 権限で出し入れする項目。SCP-03）---
  wageType: WageType
  hourlyWage?: number
  monthlySalary?: number
  // --- シフト ---
  skills: string[]              // 'レジ' 'キッチン' '夜勤可'
  maxWorkDaysPerMonth?: number
  color: string                 // シフト表の識別色
  isActive: boolean
}

// ---- ★★★ 打刻（生データ。絶対に書き換えない）★★★ ----
type PunchKind = 'clock_in' | 'break_start' | 'break_end' | 'clock_out'

type Punch = {
  id: string
  seq: number                   // ★ メモ化の世代管理（CAL-04）
  employeeId: string
  workDate: string              // ★ 労働日の帰属（日跨ぎは始業日に帰属。FR-116）
  kind: PunchKind
  atMin: number                 // ★ workDate の 0:00 からの分数。24:00超で日跨ぎ表現（CAL-05）
  // --- 記録の状況 ---
  device: 'web' | 'mobile' | 'offline_sync'
  coord?: { lat: number; lng: number; accuracyM: number }
  isOutOfRange?: boolean        // 位置が範囲外（FR-207）
  recordedAt: string            // 実際に記録された時刻（ISO）
  cancelledAt?: string          // 直後の自己取消（FR-204）
  syncState: 'synced' | 'pending'
}

// ---- ★ 訂正（打刻を書き換えず、別レコードで持つ。CAL-01）----
type PunchCorrection = {
  id: string
  employeeId: string
  workDate: string
  // --- 訂正内容 ---
  targetPunchId?: string        // 既存打刻の訂正。未指定なら新規追加
  kind: PunchKind
  fromAtMin?: number            // 訂正前（新規追加なら undefined）
  toAtMin: number               // 訂正後
  isDeletion: boolean           // 打刻の取消
  // --- 申請・承認 ---
  reasonCode: 'forgot' | 'mistake' | 'system' | 'other'
  reason: string
  status: 'pending' | 'approved' | 'rejected'
  requestedBy: string
  requestedAt: string
  reviewedBy?: string
  reviewedAt?: string
  rejectReason?: string
}

// ---- 就業規則（★ バージョン管理。FR-702）----
type WorkRule = {
  id: string
  version: number
  effectiveFrom: string         // ★ 適用開始日
  name: string
  // --- 丸め（FR-103）---
  rounding: {
    unitMin: 1 | 5 | 10 | 15 | 30
    inDirection: 'none' | 'up' | 'down' | 'nearest'
    outDirection: 'none' | 'up' | 'down' | 'nearest'
  }
  // --- 法定労働時間 ---
  legalDailyMin: number         // 480 = 8h
  legalWeeklyMin: number        // 2400 = 40h
  weekStartsOn: number          // 週の起算曜日（0=日）
  // --- 休憩（FR-106, FR-107）---
  breakRules: { overWorkMin: number; requiredBreakMin: number }[]  // [{360,45},{480,60}]
  autoDeductScheduledBreak: boolean   // 休憩未打刻時に所定休憩を控除するか
  // --- 深夜 ---
  nightStartMin: number         // 1320 = 22:00
  nightEndMin: number           // 300 = 5:00（翌日）
  // --- 割増率（FR-113）---
  premiums: {
    overtime: number            // 法定外残業（法定下限 0.25）
    overtimeOver60h: number     // 月60時間超（法定下限 0.50）
    night: number               // 深夜（法定下限 0.25）
    legalHoliday: number        // 法定休日（法定下限 0.35）
    scheduledHoliday: number    // 所定休日（法定外。時間外として扱う）
  }
  // --- 変形労働（FR-115）---
  flexibleMonthly: { enabled: boolean; periodStartDay: number; totalFrameMin?: number }
  // --- 日跨ぎ ---
  overnightAttribution: 'start_date' | 'end_date'
}

type HolidayCalendar = {
  year: number
  legalHolidayWeekday?: number  // 法定休日を曜日で特定する場合
  dates: { date: string; kind: 'legal_holiday' | 'scheduled_holiday' | 'national_holiday'; name: string }[]
}

// ---- ★ 日次サマリ（保存しない。常に算出）----
type DailySummary = {
  employeeId: string
  workDate: string
  dayType: 'workday' | 'legal_holiday' | 'scheduled_holiday'
  // --- 生打刻（丸め前）---
  rawPunches: { kind: PunchKind; atMin: number }[]
  rawWorkMin: number
  rawBreakMin: number
  // --- 丸め後 ---
  roundedInMin?: number
  roundedOutMin?: number
  // --- 区分ごとの時間（★ ここが給与に直結）---
  scheduledWorkMin: number      // 所定内労働
  legalInsideOtMin: number      // 法定内残業（所定超〜法定内）
  legalOutsideOtMin: number     // 法定外残業（法定超）
  over60hOtMin: number          // 月60時間超の部分
  nightMin: number              // 深夜
  legalHolidayMin: number       // 法定休日労働
  scheduledHolidayMin: number   // 所定休日労働
  lateMin: number               // 遅刻
  earlyLeaveMin: number         // 早退
  breakActualMin: number
  breakRequiredMin: number
  breakShortageMin: number      // ★ 法定休憩の不足（FR-106）
  // --- 休暇 ---
  leaveKind?: LeaveKind
  leaveUnit?: 'full' | 'half' | 'hourly'
  leaveHours?: number
  // --- 金額（★ 権限がない場合は含めない。SCP-03）---
  basePay?: number
  premiumPay?: number
  totalPay?: number
  // --- ★ 計算の根拠（CAL-06, FR-117）---
  breakdown: CalcBreakdown
  // --- 状態 ---
  hasCorrection: boolean
  anomalies: Anomaly[]
  isLocked: boolean             // 月次確定済み
}

type CalcBreakdown = {
  appliedRuleVersion: number
  steps: {
    step: 'rounding' | 'break_check' | 'scheduled_compare' | 'ot_split'
        | 'night_extract' | 'holiday_judge' | 'premium_apply' | 'weekly_40h'
    input: string               // 人が読める入力の説明
    output: string              // 人が読める結果
    note?: string               // 適用した設定・根拠
  }[]
  appliedPremiums: { kind: string; rate: number; minutes: number; amount?: number }[]
}

type Anomaly = {
  kind: 'missing_punch' | 'no_clock_out' | 'break_shortage' | 'over_limit'
      | 'out_of_range' | 'pending_correction' | 'late' | 'early_leave'
  severity: 'info' | 'warn' | 'error'
  message: string
}

// ---- 月次集計（算出値）----
type MonthlySummary = {
  employeeId: string
  yearMonth: string             // '2026-08'
  workDays: number
  totalWorkMin: number
  scheduledWorkMin: number
  legalInsideOtMin: number
  legalOutsideOtMin: number
  nightMin: number
  legalHolidayMin: number
  scheduledHolidayMin: number
  lateMin: number
  earlyLeaveMin: number
  paidLeaveDays: number
  absenceDays: number
  // --- 金額（権限依存）---
  basePay?: number
  premiumPay?: number
  totalPay?: number
  // --- 状態 ---
  anomalyCount: number
  pendingApprovals: number
  status: 'open' | 'confirmed'
  confirmedBy?: string
  confirmedAt?: string
}

// ---- 36協定（算出値。FR-605）----
type ComplianceStatus = {
  employeeId: string
  yearMonth: string
  monthlyOtMin: number          // 当月の時間外（法定外＋休日）
  monthlyLimitMin: number       // 45h = 2700
  yearlyOtMin: number           // 年度累計
  yearlyLimitMin: number        // 360h = 21600
  multiMonthAvgMin: number      // 直近2〜6ヶ月の平均
  multiMonthLimitMin: number    // 80h = 4800
  singleMonthLimitMin: number   // 100h = 6000
  specialClauseUsedCount: number // 特別条項の適用回数（年6回）
  alerts: { kind: string; usageRatio: number; remainingMin: number; severity: 'warn' | 'error' }[]
}

// ---- 休暇 ----
type LeaveKind = 'paid' | 'special' | 'absence' | 'compensatory'
type LeaveRequest = {
  id: string
  employeeId: string
  kind: LeaveKind
  unit: 'full' | 'half_am' | 'half_pm' | 'hourly'
  dates: string[]
  hours?: number
  reason: string
  status: 'pending' | 'approved' | 'rejected'
  requestedAt: string
  reviewedBy?: string
  reviewedAt?: string
}
type PaidLeaveGrant = {
  id: string
  employeeId: string
  grantedAt: string
  days: number
  expiresAt: string             // ★ 失効予定（FR-609）
  usedDays: number
  usedHours: number             // 時間単位有給
}

// ---- シフト ----
type ShiftPattern = {
  id: string
  name: string                  // 早番 / 遅番 / 夜勤 / 休
  startMin: number
  endMin: number                // 24:00超で日跨ぎ
  breakMin: number
  color: string
  requiredSkills: string[]
  isDayOff: boolean
}
type ShiftAssignment = {
  id: string
  employeeId: string
  date: string
  patternId: string
  startMin?: number             // パターンから変更する場合
  endMin?: number
  status: 'draft' | 'published'
  createdVia: 'manual' | 'auto_generated' | 'swap'
  note: string
}
type ShiftRequest = {            // 従業員の希望（FR-501）
  id: string
  employeeId: string
  periodId: string
  entries: { date: string; availability: 'available' | 'unavailable' | 'partial'; fromMin?: number; toMin?: number }[]
  maxWorkDays?: number
  submittedAt?: string
}
type ShiftPeriod = {
  id: string
  yearMonth: string
  requestDeadline: string       // ★ 希望提出の締切
  status: 'collecting' | 'drafting' | 'published'
  publishedAt?: string
}
type StaffingDemand = {          // 必要人数（FR-403）
  id: string
  orgId: string
  weekday: number
  fromMin: number
  toMin: number
  requiredCount: number
  requiredSkills: string[]
}
type SwapRequest = {
  id: string
  fromEmployeeId: string
  toEmployeeId: string
  assignmentId: string
  status: 'pending_peer' | 'pending_manager' | 'approved' | 'rejected'
  requestedAt: string
}

// ---- 監査・通知 ----
type AuditLog = {
  id: string
  actorId: string
  action: 'approve_correction' | 'reject_correction' | 'confirm_month' | 'unconfirm_month'
        | 'update_work_rule' | 'publish_shift' | 'approve_leave' | 'export_csv'
  targetType: string
  targetId: string
  changes: { field: string; before: unknown; after: unknown }[]
  createdAt: string
}
type Notification = {
  id: string
  targetEmployeeId: string
  kind: 'shift_published' | 'shift_changed' | 'approval_result' | 'punch_forgotten'
      | 'limit_approaching' | 'request_deadline'
  title: string
  body: string
  emailPreview?: { subject: string; body: string }   // ★ 送信しない。プレビューのみ
  readAt?: string
  createdAt: string
}
```

## 労働時間算出の実装（**7章の中核**）

```ts
// lib/attendance/daily.ts
export function calcDaily(input: DailyCalcInput): DailySummary {
  const { employee, workDate, punches, corrections, rule, calendar } = input
  const steps: CalcBreakdown['steps'] = []

  // ① 訂正を適用した「有効な打刻列」を作る（★ 生打刻は変更しない。CAL-01）
  const effective = applyCorrections(punches, corrections.filter(c => c.status === 'approved'))

  // ② 丸め（FR-103）
  const { inMin, outMin } = applyRounding(effective, rule.rounding)
  steps.push({
    step: 'rounding',
    input: `出勤 ${fmt(rawIn)} / 退勤 ${fmt(rawOut)}`,
    output: `出勤 ${fmt(inMin)} / 退勤 ${fmt(outMin)}`,
    note: `${rule.rounding.unitMin}分単位・出勤${dirLabel(rule.rounding.inDirection)}・退勤${dirLabel(rule.rounding.outDirection)}`,
  })

  // ③ 休憩の充足判定（FR-106）
  const grossMin = outMin - inMin
  const breakActual = sumBreaks(effective)
  const breakRequired = requiredBreak(grossMin - breakActual, rule.breakRules)
  const breakShortage = Math.max(0, breakRequired - breakActual)
  steps.push({
    step: 'break_check',
    input: `実働見込 ${fmtMin(grossMin - breakActual)} / 休憩実績 ${breakActual}分`,
    output: breakShortage > 0 ? `不足 ${breakShortage}分` : '充足',
    note: `労働${breakRequired === 60 ? '8時間超' : '6時間超'}のため${breakRequired}分以上が必要`,
  })

  // ④ 休日判定（FR-111）→ ⑤ 所定との比較 → ⑥ 残業区分（FR-109）
  //   → ⑦ 深夜抽出（FR-110）→ ⑧ 週40h（FR-114）→ ⑨ 割増合成（FR-112）
  //   ※ 各ステップで steps.push() し、根拠を必ず残す（CAL-06）

  return { /* ... */ breakdown: { appliedRuleVersion: rule.version, steps, appliedPremiums } }
}
```

## シードデータ（`lib/seed/`）

| ファイル | 内容 | 件数 |
|---|---|---|
| `orgs.ts` | 部署4（店舗2・本部2） | 4件 |
| `employees.ts` | 従業員（**正社員／パート／アルバイトを混在。夜勤可の人を数名**） | 42名 |
| `workRules.ts` | 就業規則（**バージョン2つ。3ヶ月前に丸め設定が変わった履歴を持つ**） | 2件 |
| `holidayCalendar.ts` | 休日カレンダー（法定休日・所定休日・祝日） | 1年分 |
| `shiftPatterns.ts` | シフトパターン（早番／中番／遅番／夜勤／休） | 5件 |
| `staffingDemand.ts` | 必要人数（曜日別・時間帯別。**金土夕方が厚い**） | 約40件 |
| `shiftPeriods.ts` `shiftRequests.ts` | 期間3ヶ月分。**希望未提出者を2名残す** | — |
| `shiftAssignments.ts` | シフト（過去2ヶ月は確定、当月は公開済、翌月は下書き） | 約2,600件 |
| `punches.ts` | **打刻（過去3ヶ月・42名分）** | 約10,000件 |
| `punchCorrections.ts` | 訂正（**承認済み／申請中を混在。申請中を3件残す**） | 28件 |
| `leaveRequests.ts` `paidLeaveGrants.ts` | 休暇申請・有給付与（**年5日未達者を3名作る**） | — |

**シード作成のルール（品質を左右する）**

- **実在企業名・実在人名を使わない。** 架空の従業員名・店舗名で構成する
- **氏名を「従業員A」のようなダミーにしない。** 実在しそうな架空名にする。ここが手抜きだとデモ全体が安っぽくなる
- **打刻を「9:00 きっかり」にしない。** 9:03、8:57、19:47 のように**丸めが意味を持つ端数**を持たせる。**これが最重要**
- **休憩不足の日を作る**（実働8時間超で休憩57分など）。法定休憩の警告が動いて見えること
- **退勤打刻がない日を1〜2件作る**（打刻忘れの検出とフローBの実演用）
- **深夜勤務・日跨ぎ勤務を含める**（夜勤者を数名、22:00超の退勤を複数）
- **法定休日・所定休日に出勤している日を作る**（休日割増が計算に出ること）
- **36協定の上限に接近している人を2名、超えそうな人を1名作る。** 全員余裕だとモニタリングが動いて見えない
- **有給の年5日未達者を3名、失効間近の人を1名作る**
- **シフトの予定と実績にズレを持たせる**（予定17:00-22:00 → 実績16:52-22:34 等）。予実比較が意味を持つこと
- **必要人数が不足している日を数日作る**（シフト表の赤表示とフローCの実演用）
- **就業規則のバージョンを2つ持ち、3ヶ月前に丸め設定が変わった状態にする**（FR-702 の実演用）
- 日付は**現在日時からの相対で生成する。固定日付を埋め込まない**
- 打刻列は**時系列に矛盾がないこと**（休憩終了が休憩開始より前にならない、等）

## ストアとリポジトリ

```
lib/
├── types/  seed/
├── attendance/                 # ★ 労働時間の算出。純粋関数のみ
│   ├── daily.ts                # ★ 日次サマリ（7章のコード参照）
│   ├── rounding.ts             # 丸め（FR-103, FR-105）
│   ├── breaks.ts               # 法定休憩の充足（FR-106, FR-107）
│   ├── overtime.ts             # 法定内/法定外の切り分け、週40h（FR-109, FR-114）
│   ├── night.ts                # 深夜の抽出（FR-110）
│   ├── holiday.ts             # 休日判定（FR-111）
│   ├── premium.ts              # 割増率の合成（FR-112）
│   ├── flexible.ts             # 1ヶ月単位変形労働（FR-115）
│   ├── monthly.ts              # 月次集計（FR-601）
│   ├── compliance.ts           # ★ 36協定（FR-605〜607）
│   ├── paidLeave.ts            # 有給の付与・残・年5日義務（FR-608〜610）
│   └── corrections.ts          # 訂正の適用（CAL-01）
├── shift/                      # ★ シフト。純粋関数のみ
│   ├── coverage.ts             # 必要人数の充足判定（FR-403, FR-404）
│   ├── constraints.ts          # ★ 法令制約の検証（FR-405）
│   ├── generate.ts             # 自動生成（FR-406, FR-407）
│   ├── cost.ts                 # 人件費の見込み（FR-409）
│   └── variance.ts             # 予実比較（FR-611）
├── time/                       # 分数⇔時刻、24:00超の扱い（CAL-05）
├── csv/                        # 給与CSV出力
├── query/                      # 絞り込み・ソート・ビュー
├── store/  { session, data, rules, sync, ui }
└── repo/                       # ★ コンポーネントが触るのはここだけ
    ├── _delay.ts
    ├── _scope.ts               # ★ データスコープ + 項目スコープ（SCP-01）
    ├── punches.ts  attendance.ts  corrections.ts  leave.ts
    ├── shift.ts  shiftRequests.ts  demand.ts
    ├── monthly.ts  compliance.ts  employees.ts  rules.ts  logs.ts
```

**リポジトリ層の規約（厳守）**

- 全メソッドを `async` で定義する。中身が同期でも例外なく
- 戻り値は `{ ok: true; data: T } | { ok: false; error: string }` に統一する
- **コンポーネントから Zustand ストアを直接参照しない。** 読み取りも書き込みもリポジトリ経由
- **労働時間の算出を `lib/attendance/` の外に書かない**（CAL-02）
- **法令に関わる判定をコンポーネントに書かない**（CAL-07）
- **打刻レコードを更新・削除するコードを書かない**（CAL-01）
- **スコープ適用（`_scope.ts`）を全メソッドが必ず通す。** ここが実案件で権限制御に置き換わる箇所
- **時給・給与額・人件費は、権限がない場合オブジェクトから除外して返す**（SCP-03）
- 派生値（労働時間・月次集計・36協定・人件費）はストアに保存せず算出する
- **打刻と労働時間の再算出に擬似ディレイを入れない**

---

# 8. デザイン要件

## アートディレクション

**「1日を、24時間の帯として横に描く」**

勤怠システムが扱うのは「時刻」と「長さ」である。9:03 という点と、8時間という幅。数字の羅列では、**どこが所定でどこが残業か、どこが深夜に食い込んでいるかが分からない。**

TOKIWA は1日を**横1本の24時間帯**として描き、その上に打刻の点、労働の帯、休憩の抜け、深夜のゾーン、残業の色分けを重ねる。**時間の重なりを図として読ませる。**

- **横軸は常に時間。** 日次も、シフト表も、充足ヒートマップも、横軸を時間で統一する
- **色は区分のためだけにある。** 所定内・法定内残業・法定外残業・深夜・休日。それ以外に色を使わない
- **時刻は大きく、等幅で。** `9:03` `19:47` は読み間違えると事故になる
- **警告は数字で。**「休憩不足」ではなく「休憩不足 3分」

> **RELATE / KURA との差別化（最重要）**
> 3本とも「白背景・高密度・業務システム」だが、方向を分ける。
> **RELATE：罫線と等幅数字で表を読ませる。藍。**
> **KURA：各行に在庫の水位バーを描く。ティール＋アンバー。縦長数字。**
> **TOKIWA：各行に1日の24時間帯を描く。インディゴ＋コーラル。等幅数字＋大きな時刻表示。**
> **KURA の水位バーは「量」、TOKIWA の時間帯は「位置と長さ」。** 見た目が似ないよう、TOKIWA の帯は**必ず時刻目盛りを伴う**こと。

## カラートークン

```css
:root {
  /* ニュートラル（わずかに青みのある事務的な白） */
  --bg:        #F4F5F8;   /* ページ背景 */
  --panel:     #FFFFFF;   /* テーブル・カード */
  --panel-alt: #EDEFF4;   /* 縞・ヘッダー */
  --line:      #DCDFE7;   /* 罫線 */
  --line-hi:   #B7BCC9;   /* テーブルヘッダー下、時刻目盛り */

  --ink-900:   #131722;   /* 見出し・本文・時刻 */
  --ink-600:   #4C5464;   /* 補助テキスト */
  --ink-400:   #8A92A3;   /* ラベル・非活性 */

  --primary:   #3A4CAD;   /* 主要CTA・選択状態（インディゴ） */
  --primary-d: #2C3A87;
  --primary-50:#E8EAF7;

  /* ★ 労働時間の区分。これ以外の色を帯に使わない */
  --seg-scheduled:  #3A4CAD;   /* 所定内労働（インディゴ） */
  --seg-ot-inside:  #6F7ECB;   /* 法定内残業（薄いインディゴ） */
  --seg-ot-outside: #E4633F;   /* 法定外残業（コーラル） */
  --seg-night:      repeating-linear-gradient(  /* ★ 深夜はゾーンとして重ねる */
      45deg, transparent 0 5px, rgba(58,76,173,.28) 5px 10px);
  --seg-holiday:    #B0459A;   /* 休日労働（マゼンタ寄り） */
  --seg-break:      #DCDFE7;   /* 休憩（抜き） */
  --seg-leave:      #4C9A7A;   /* 有給・休暇 */

  /* 時間帯の地 */
  --band-track:     #EDEFF4;
  --band-night-bg:  #E4E7F2;   /* 22:00-5:00 の背景 */
  --band-scheduled: #F7F8FB;   /* 所定時間帯の背景 */

  /* ★ 異常・法令警告 */
  --alert-error: #C2382C;   /* 上限超過・休憩不足 */
  --alert-warn:  #C08320;   /* 上限接近・未打刻 */
  --alert-info:  #3A6FA8;   /* 承認待ち */

  /* シフトの充足 */
  --cov-short:  #C2382C;   /* 不足 */
  --cov-exact:  #4C9A7A;   /* ちょうど */
  --cov-over:   #6F7ECB;   /* 過剰 */
}
```

**配色ルール**

- **通常の勤務日に色を持たせない。** 色がついている行は「対応が必要な行」だけ
- **深夜は塗りではなく斜線ゾーンで重ねる。** 「別の区分」ではなく「別の軸の属性」だから、塗りを奪わない
- **法定外残業だけがコーラル（暖色）。** ここが給与と法令に最も効く区分だから、唯一の暖色を割り当てる
- **上限超過を赤、上限接近を琥珀にする。** 逆にしない
- 部署にも雇用形態にも固有色を割り当てない。**バッジが色だらけの画面にしない**

## タイポグラフィ

| 役割 | 書体 | 用途 |
|---|---|---|
| UI全般 | Noto Sans JP 400 / 700 | 見出しも本文もこれ1つ |
| **時刻・時間** | **Roboto Mono 500**（`tabular-nums`） | **打刻時刻、勤務時間、残り時間、割増額** |

| トークン | サイズ | 用途 |
|---|---|---|
| page-title | 22px / 700 | 画面タイトル |
| section | 15px / 700 | セクション見出し |
| body | 13px / 400 | テーブルセル、本文 |
| label | 11px / 700（`letter-spacing:.05em`） | 項目ラベル |
| **punch-lg** | **36px / 500 Mono** | **打刻画面の現在時刻・本日の勤務時間** |
| **time-md** | 20px / 500 Mono | 勤務日詳細の主要時間、36協定の残り時間 |
| time | 13px / 500 Mono | テーブル内の時刻・時間 |

**RELATE も Roboto Mono を使うが、TOKIWA は `punch-lg`（36px）という極端に大きい時刻表示を持つ点が違う。** 打刻画面では時刻が主役になる。

## レイアウトとスペーシング

| 項目 | 定義 |
|---|---|
| スペーシング | 4pxベース：4 / 8 / 12 / 16 / 24 / 32 / 48 |
| アプリシェル | 従業員側＝ボトムナビ（モバイル）／管理側＝左サイドバー232px + ヘッダー52px |
| コンテンツ幅 | 従業員側 640px（読み進める導線）／管理側は**画面幅いっぱい** |
| テーブル行高 | 標準40px ／ コンパクト32px（切替可能） |
| **24時間帯（行内）** | 高さ10px × 幅いっぱい。**目盛りは 0/6/12/18/24 の5本** |
| **24時間帯（詳細）** | 高さ32px。**目盛りは1時間ごと、ラベルは3時間ごと** |
| **打刻ボタン** | 高さ72px、幅いっぱい。**画面下1/3に配置**（親指の届く範囲） |
| **シフト表セル** | 幅56px × 高さ40px（従業員×日付グリッド） |
| 角丸 | 3px（カード・入力・ボタン）／2px（バッジ）／0（時間帯・シフトセル） |
| 影 | ポップオーバー・モーダルのみ。**テーブルやカードに影を使わない** |
| セーフエリア | `env(safe-area-inset-bottom)` を考慮（従業員側の固定打刻ボタン） |

## 主要コンポーネント仕様

| コンポーネント | 仕様 |
|---|---|
| **24時間帯**（最重要） | 地の上に、所定時間帯の薄い背景／深夜ゾーンの背景／労働の帯（区分別の色）／休憩の抜き／打刻の点を重ねる。**必ず時刻目盛りを伴う。** ホバーで区分ごとの時間をツールチップ表示 |
| **打刻の点** | 生打刻＝小さな中空の円（点線の縦線）／訂正後＝塗りの円（実線の縦線）。**両方を同時に描く**（FR-306） |
| **打刻ボタン** | 状態別に1つだけ表示（出勤／休憩開始／休憩終了／退勤）。**確認ダイアログを出さない**（FR-202）。押下直後に時刻を `punch-lg` で表示 |
| **区分内訳** | 所定内／法定内残業／法定外残業／深夜／休日 を横棒の積み上げ + 時間（Mono）。**深夜は斜線を重ねる** |
| **計算の根拠パネル** | `CalcBreakdown.steps` を順に表示。各ステップに「入力 → 出力（根拠）」。**畳めるが既定で開いている** |
| **丸め設定トグル** | 単位セレクト + 方向セレクト。**変更すると隣の計算結果が即座に変わる**。切り捨て選択時は警告文が出現 |
| **異常バッジ** | 種別 + **具体的な数字**（「休憩不足 3分」「上限まで 4h20m」）。色は severity に従う |
| **36協定ゲージ** | 4本の横バー（月45h／年360h／複数月平均80h／単月100h）。**上限線と現在値、残り時間（Mono）**。80%超で琥珀、超過で赤 |
| **シフト表セル** | パターン名（略称）+ 時間（Mono・小）。従業員色を左端2pxの帯で。**空セルはドロップ可能であることを示す** |
| **充足ヒートマップ** | 横軸=時間（1時間ごと）、縦軸=日付。セルの色は `--cov-short` / `--cov-exact` / `--cov-over`。**数字（配置/必要）を併記** |
| **人件費カード** | 見込み額（Mono・大）／予算との差分（符号付き・色付き）／割増の内訳 |
| **予実比較行** | 日付／予定の24時間帯（薄い）／実績の24時間帯（濃い）を**上下2段で重ねて描く**／差分時間（Mono・符号付き）／人件費差分 |
| **訂正差分カード** | 訂正前 → 訂正後 を左右に並べ、**再計算後の勤務時間の変化を強調** |
| 空状態 | 全一覧に専用の空状態。**「データ0件」と「絞り込み0件」を別の文言にする** |
| スケルトン | テーブルは行形状。**24時間帯の位置も確保してレイアウトシフトを防ぐ** |
| デモ切替バー | **プロダクトのトークンとは別扱い**（ダークな帯 + 小さめタイポ） |

## インタラクション要件（業務システムの肝）

| ID | 要件 |
|---|---|
| IX-01 | **打刻は1タップで完了し、100ms以下で結果が表示されること** |
| IX-02 | **丸め設定の変更による再計算が 50ms以下で画面に反映されること**（EMB-06） |
| IX-03 | 一覧の絞り込み・並び替えは体感で即座（100ms以下） |
| IX-04 | テーブル上で `↑↓` 行移動、`Enter` 詳細を開く |
| IX-05 | **シフト表はキーボードで操作できること**（矢印でセル移動、数字キーでパターン割当、Delete で解除） |
| IX-06 | 承認画面は**連続処理できること**（`A` 承認／`R` 差戻し／`→` 次） |
| IX-07 | 破壊的操作（月次確定・確定解除・シフト公開）は確認ダイアログ + **対象件数の明示** |
| IX-08 | 承認・差戻しは**5秒間「元に戻す」を表示**すること |
| IX-09 | 保存は楽観的更新とし、失敗時のみロールバックしてエラーを表示すること |
| IX-10 | モーダルを開いても背後の一覧の状態が保持されること |

## モーション

| 対象 | duration | 内容 |
|---|---|---|
| ホバー・フォーカス | 80ms | 色のみ |
| **打刻の完了** | 240ms | 打刻の点が帯に着地する。**成功が身体で分かる程度に、しかし待たせない** |
| **丸め設定の変更** | 300ms | **帯の端が伸縮し、数値がカウント変化する。** ここは意図的に見せる |
| モーダル・ポップオーバー | 160ms | フェード + 4px |
| シフトセルのドラッグ | 150ms | 掴む/離すのスケール変化 |
| 36協定ゲージ | 400ms | バーの伸縮。**上限超過時のみ、一度だけ脈動** |
| トースト | 160ms | 右下から |

`prefers-reduced-motion: reduce` 時は全アニメーションを無効化する。

## アクセシビリティ（WCAG 2.1 AA）

| ID | 要件 |
|---|---|
| A11Y-01 | コントラスト比は通常4.5:1以上。**13px本文でも必ず満たすこと** |
| A11Y-02 | 全ての操作をキーボードのみで完遂できること |
| A11Y-03 | **シフト表のドラッグ操作にキーボード代替を用意すること**（IX-05） |
| A11Y-04 | フォーカスリングは2px・オフセット2pxで常時可視。`outline:none` の単独使用禁止 |
| A11Y-05 | **24時間帯は装飾ではなく情報である。** 区分ごとの時間を必ず数値としても表示し、帯に `aria-label`（「所定内8時間、法定外残業1時間47分、深夜なし」）を持たせること |
| A11Y-06 | **深夜ゾーンを斜線テクスチャで示し、色の違いだけに依存しないこと** |
| A11Y-07 | **異常・警告を色のみで伝えないこと。** アイコン + 具体的な数字を併記すること |
| A11Y-08 | テーブルは `<table>` を用い、`scope` と `caption` を適切に設定すること |
| A11Y-09 | フォームの各入力に `label` を関連付け、エラーは `aria-describedby` と `role="alert"` で読み上げること |
| A11Y-10 | **打刻の成功・失敗を `aria-live="polite"` で通知すること**（打刻した時刻を読み上げる） |
| A11Y-11 | 上限超過の警告を `aria-live="assertive"` で通知すること |
| A11Y-12 | モーダルはフォーカストラップ + Esc で閉じる + 起動元へフォーカス復帰 |
| A11Y-13 | **打刻ボタンのタップ領域は最低56×56px**（現場で急いで押されるため） |
| A11Y-14 | フォントサイズ200%指定でも内容が読め、テーブルは横スクロールで対応すること |

## ライティング規約

| 原則 | 例 |
|---|---|
| **警告は必ず数字で** | ○「休憩が3分不足しています（8時間超の労働には60分以上が必要）」／×「休憩時間が不足しています」 |
| **残りを示す** | ○「今月の時間外は32時間15分。上限（45時間）まで残り12時間45分」／×「残業時間が多くなっています」 |
| **計算の根拠を書く** | ○「15分単位・切り捨てで丸めた結果、退勤19:47 → 19:45 として計算しています」 |
| **法令の注意は事実として** | ○「切り捨ての丸めは、実労働時間の一部を賃金計算から除外することになり、適法性に注意が必要です」／× 断定的な違法宣言も、無言の許容も避ける |
| 打刻は結果を返す | ○「9:03 に出勤を記録しました」／×「打刻しました」 |
| **申請と反映を区別する** | ○「訂正を申請しました。承認されると勤務時間に反映されます」／×「訂正しました」 |
| 空状態を区別する | データ0件：「まだ打刻がありません」／絞り込み0件：「条件に一致する日はありません」+ 条件解除 |
| システム語を使わない | ○「この月を確定する」／×「statusをconfirmedに更新」 |

---

# 9. 技術要件・ディレクトリ構成

## 技術スタック（固定・勝手に変更しない）

| レイヤ | 技術 | 備考 |
|---|---|---|
| フレームワーク | Next.js 15（App Router）／ TypeScript strict | |
| スタイリング | Tailwind CSS + CSS Variables | トークンは CSS 変数で定義 |
| UIコンポーネント | shadcn/ui（Radix UI基盤） | a11y要件を自前実装せずに満たす |
| 状態管理 | Zustand + persist | localStorage に永続化 |
| テーブル | TanStack Table v8 | 列制御・ソート・選択。**UIは自前** |
| 仮想スクロール | TanStack Virtual | 大量行の表示 |
| **労働時間の算出** | **自前実装**（`lib/attendance/`） | **これが本プロジェクトの価値。ライブラリに任せない** |
| **シフト自動生成** | **自前実装**（貪欲法 + 制約チェック） | **最適化ソルバを入れない。** 限界を明示する（FR-408） |
| **24時間帯・ヒートマップ** | **自前実装（div または SVG）** | チャートライブラリを使わない |
| **日時処理** | **date-fns（ja locale）+ 自前の分数ユーティリティ** | **タイムゾーンは Asia/Tokyo 固定。** dayjs / moment を入れない |
| D&D | dnd-kit | シフト表。**キーボード対応必須** |
| フォーム | React Hook Form + Zod | |
| CSV | Papa Parse | 給与CSV出力 |
| グラフ | Recharts（動的import） | 残業推移、部署別集計 |
| PWA | next-pwa | オフライン打刻（FR-209） |
| アイコン | lucide-react | |
| ホスティング | Vercel | |
| テスト | Vitest / Playwright / axe-core | |

**上記以外のライブラリを入れる前に必ず提案し、承認を得ること。特に給与計算・労務計算系のライブラリ、最適化ソルバ、重量級チャートライブラリの導入は禁止。**

## ディレクトリ構成

```
/
├── CLAUDE.md
├── app/
│   ├── layout.tsx                  # アプリシェル（ロール別ナビ・デモ切替バー）
│   ├── page.tsx                    # SC-001 打刻 / SC-900（?embed=1）
│   ├── attendance/                 # SC-010〜013
│   ├── overtime/  leave/           # SC-020, 030, 031
│   ├── shift/                      # SC-040〜042
│   ├── notifications/              # SC-050
│   ├── admin/                      # SC-100〜140
│   ├── hr/                         # SC-200〜210
│   └── dev/components/
├── components/
│   ├── timeband/                   # ★ 最重要
│   │   ├── DayBand.tsx             # 24時間帯（行内・詳細の2サイズ）
│   │   ├── BandRuler.tsx           # 時刻目盛り
│   │   ├── PunchMarker.tsx         # 生打刻／訂正後の点
│   │   ├── SegmentLegend.tsx       # 区分の凡例
│   │   └── BandTooltip.tsx
│   ├── punch/                      # PunchButton, PunchClock（punch-lg）, PunchResult
│   ├── shift/                      # ShiftGrid, ShiftCell, CoverageHeatmap, CostCard, useShiftDnd
│   ├── compliance/                 # ComplianceGauge, LimitBadge
│   ├── domain/                     # CalcBreakdownPanel, RoundingToggle, AnomalyBadge, CorrectionDiff, VarianceRow
│   ├── table/                      # DataTable, FilterBar, ViewSidebar
│   ├── demo/                       # RoleSwitcher, ResetButton, GuideTour, BackToArticle
│   └── layout/
├── lib/
│   ├── types/ seed/ store/ repo/
│   ├── attendance/                 # ★ 労働時間の算出。純粋関数のみ
│   ├── shift/                      # ★ シフト。純粋関数のみ
│   ├── time/ csv/ query/ validation/ utils/
├── docs/
│   └── work-rule-checklist.md      # 資料PDFの元原稿（就業規則ヒアリングシート）
├── e2e/
└── public/
```

## コーディング規約

- TypeScript strict。`any` 禁止。やむを得ない場合は `unknown` + 型ガード
- ファイル名は kebab-case、コンポーネントは PascalCase
- 1ファイル300行を超えたら分割を検討する
- **コンポーネントから Zustand ストアを直接参照しない。必ず `lib/repo/` 経由**
- **労働時間の算出を `lib/attendance/` の外に書かない**（CAL-02）
- **法令に関わる判定をコンポーネントに書かない**（CAL-07）
- **打刻レコードを更新・削除するコードを書かない**（CAL-01）
- **`lib/attendance/` `lib/shift/` `lib/time/` は純粋関数のみ。** ストア・DOM に依存させず、**`new Date()` を内部で呼ばない**（CAL-03）
- **時刻は分数で扱い、日跨ぎは 24:00 超で表現する。** 文字列の切り貼りで時刻計算をしない（CAL-05）
- **タイムゾーンは Asia/Tokyo 固定**（実案件では要検討である旨をコメントに残す）
- 派生値（労働時間・月次集計・36協定・人件費）をストアに保存しない
- **賃金計算に浮動小数点の累積誤差を持ち込まない**（円単位の整数演算で扱い、端数処理の方針をコメントに明記する）
- コミットは1タスクごと。メッセージに要件IDを含める
  例：`feat(attendance): 深夜と法定外残業の割増重複を合成 (FR-112)`

---

# 10. 非機能要件・法令

## 性能要件

| ID | 要件 | 目標値 |
|---|---|---|
| NFR-01 | **打刻から結果表示まで** | **100ms以下** |
| NFR-02 | **丸め設定変更による再計算と画面反映** | **50ms以下** |
| NFR-03 | **42名 × 3ヶ月分（約2,700勤務日）の月次集計** | **200ms以下** |
| NFR-04 | 36協定モニタリング（42名分）の算出 | 150ms以下 |
| NFR-05 | シフト自動生成（42名 × 1ヶ月） | 2秒以下（進捗表示を出すこと） |
| NFR-06 | 一覧の絞り込み・並び替えの反映 | 100ms以下 |
| NFR-07 | LCP（デスクトップ） | 1.8秒以下 |
| NFR-08 | INP | 200ms以下 |
| NFR-09 | CLS | 0.1以下。**24時間帯の領域を最初から確保すること** |
| NFR-10 | シフト表のドラッグ操作 | 60fps維持 |
| NFR-11 | 埋め込みモードの初期JSバンドル（gzip後） | 60KB以下 |
| NFR-12 | アプリ本体の初期JSバンドル（gzip後） | 250KB以下（グラフは動的import） |
| NFR-13 | Lighthouse | Performance 90 / Accessibility 95 以上 |
| NFR-14 | 対応環境 | Chrome / Safari / Edge 最新2バージョン、iOS Safari 16以降、Android Chrome 最新 |
| NFR-15 | localStorage の容量上限に達した場合、警告を表示し、**古い月を月次集計に要約して圧縮すること**（データを破損させない） | — |

## セキュリティ・プライバシー

| ID | 要件 |
|---|---|
| SEC-01 | **打刻データ・氏名・給与額を一切外部に送信しないこと。** localStorage にのみ保存すること |
| SEC-02 | この事実を、打刻画面・設定画面・デモ切替バーで明示すること |
| SEC-03 | **GA4 に氏名・打刻データ・給与額を送らないこと**（EMB-09） |
| SEC-04 | ユーザー入力（訂正理由・シフトメモ）をサニタイズすること |
| SEC-05 | 「デモをリセット」ですべてのデータが消えることを明示し、実際に消えること |
| SEC-06 | **位置情報を取得する場合、取得目的を事前に説明すること。拒否しても打刻できること**（FR-206） |

## 法令に関する取り扱い（**この領域の最重要事項**）

**本デモは労働時間計算の実装例であり、法令解釈の保証をするものではない。** この立場を明確にする。

| ID | 要件 |
|---|---|
| LAW-01 | **デモ内に「本デモの計算は実装例です。実運用の際は社会保険労務士等の専門家による確認を推奨します」の注記を、設定画面と月次集計画面に常設すること** |
| LAW-02 | **記事内でも同じ注記を明示すること。** 法令解釈を断定しないこと |
| LAW-03 | **設定が法定を下回る場合、警告を表示すること**（割増率が法定下限未満、休憩ルールが法定未満）。ただし設定自体はブロックしない（就業規則が法定を上回る運用もあるため、判定は「下回るか」で行う） |
| LAW-04 | **切り捨ての丸めを設定した場合、適法性に注意が必要である旨を表示すること**（FR-104） |
| LAW-05 | 36協定の上限値は設定で変更できるが、**既定値は法定の上限（月45h／年360h／複数月平均80h／単月100h）とすること** |
| LAW-06 | 法定休憩の判定（6時間超45分／8時間超60分）を実装し、**不足を明示すること** |
| LAW-07 | 有給の年5日取得義務の進捗を表示し、**未達者を抽出すること** |
| LAW-08 | 割増率の既定値は法定下限（時間外0.25／深夜0.25／法定休日0.35／月60h超0.50）とすること |

> **実案件への転用時の注意（デモ本体の要件ではない。商談で聞かれた際に説明できるよう記録）**
>
> **① 勤怠システムは「作って終わり」にならない。** 法改正・就業規則変更のたびに計算ロジックの見直しが必要。**保守を前提とした契約設計が必須**
>
> **② 労働時間の記録は法定の保存義務がある。** 保存期間（賃金台帳等は原則5年、当面3年）と、**改ざんできない記録の保持**が求められる。打刻を書き換えない設計（CAL-01）はこの要求に直結する
>
> **③ 客観的な記録による把握が求められる。** 自己申告のみの運用にはリスクがあり、打刻・PCログ等の客観記録が望ましい
>
> **④ 打刻データは個人情報。** 位置情報を取得する場合は特に、利用目的の明示と同意、取得時間帯の制限が論点になる（従業員監視との線引き）
>
> **⑤ 給与計算との接続点で端数処理が問題になる。** 時間の端数、賃金額の端数の処理方法は就業規則で定め、システムと一致させる必要がある
>
> **⑥ 未払賃金の遡及リスク。** 計算誤りが見つかった場合、**過去に遡って再計算・追加支払**が発生する。だから計算ロジックのテストが他システム以上に重要
>
> **本デモはデータをサーバーに送信しないため、②〜④の義務は発生しない。**「デモである」旨と LAW-01 の注記を画面上に常時明示する

---

# 11. 費用設計

**記事の主題の半分は「費用」。この章は実装要件であると同時に、記事本文の原稿素材でもある。**

## 勤怠システムだけの特殊事情：**人数課金 × 保守が続く**

| 層 | 内容 | 特徴 |
|---|---|---|
| ① 初期開発費 | 設計・実装・テスト | **就業規則の複雑さで大きく変わる**（変形労働・フレックス・みなし残業） |
| ② **就業規則の整理・ヒアリング工数** | 実際の運用ルールの言語化 | **見積書に「お客様側作業」として明記しないと必ず揉める** |
| ③ ハードウェア費 | 打刻端末（タブレット・ICカードリーダー） | 拠点数×台数。**Web打刻なら不要** |
| ④ 固定運用費 | ホスティング、ドメイン、監視、データベース | 小さい |
| ⑤ **保守費（法改正対応）** | **他システムより重い。** 法改正・就業規則変更で計算ロジックが変わる | **勤怠固有。長期で最大の項目になりうる** |

**⑤を見積もらずに始めると、「法改正のたびに追加費用で揉める」ことになる。** 記事ではここを明示する。

## ①の分岐：既製の勤怠サービスがあるのに、なぜ自作するのか

**この問いに答えられない案件は、受けるべきではない。** 勤怠は既製サービスが極めて充実しており、法改正対応も含まれる。**正直にそう書くことが信頼を生む。**

| 自作が正当化されるケース | 内容 |
|---|---|
| **① 就業規則が既製サービスの設定範囲を超える** | 独自の交替勤務、複数事業所を跨ぐ勤務、業種特有の手当計算 |
| **② シフト作成のロジックが独特** | スキル・資格・組み合わせ制約（「有資格者を各シフトに1名以上」等）が複雑 |
| **③ 既存システムと繋ぐ必要がある** | 基幹システム・給与ソフト・POS の勤務データと一体化したい |
| **④ 人数が多く、人数課金が重い** | 数百名規模で、月額×人数が積み上がっている |
| **⑤ 勤怠データを他業務に使いたい** | 原価計算、案件別工数、生産性分析に転用したい |

**逆に、①〜⑤のどれにも当てはまらないなら、既製サービスを使うべき。** 特に**法改正対応が標準で付いてくる**ことは、自作にはない大きな価値。

## 損益分岐の計算式

```
既製サービスの年間コスト
  = 月額単価 × 従業員数 × 12
  + 初期設定費
  ( + オプション：シフト機能・有給管理・API連携 )

回収年数 = （開発費 + ハード費）÷（既製サービス年間コスト − 自作の年間運用費 − 年間保守費）
```

**ここで重要なのは、分母から「年間保守費」を引くこと。** 法改正対応を自社で持つなら、その費用を計算に入れないと損益分岐が実態と合わない。

| 規模 | 従業員数 | 判断 |
|---|---|---|
| 小 | 〜30名 | **既製サービスで十分。** 人数課金が軽く、法改正対応の価値が相対的に大きい |
| 中 | 50〜200名 | **①②③⑤に該当するなら検討価値あり** |
| 大 | 300名以上 | **人数課金が効いてくる。** ただし要件も複雑化するため、開発費も上がる |

## ③の分岐：打刻の手段（**現場を左右する判断**）

| | Web打刻（スマホ） | タブレット共有打刻 | ICカード／生体認証 |
|---|---|---|---|
| 初期費用 | **ゼロ** | 拠点あたりタブレット1台 | 拠点あたり端末 + カード |
| なりすまし耐性 | 弱い（本人以外でも押せる） | 中（その場にいる必要） | **強い** |
| 出勤時の混雑 | **なし（各自のスマホ）** | **行列ができる** | 行列ができる |
| 私物端末の利用 | 必要（BYOD の論点が発生） | 不要 | 不要 |
| 客観性 | 位置情報で補強可 | その場にいた証拠になる | **最も客観的** |

**判断の目安：**
- **オフィスワーク中心／直行直帰がある → Web打刻（スマホ）＋位置情報**
- **店舗・工場で同時刻に多人数が出勤 → タブレット共有だと行列になる。ICカードが有利**
- **なりすまし防止が重要（労務トラブルの実績がある）→ ICカード／生体認証**

**TOKIWA が Web打刻を基本にしているのは、デモとして最速で体験できるため。** 実案件では上表で選定する。

## ②の分岐：見落とされやすい「就業規則の整理」工数

**勤怠システムの導入で最も工数を食うのは、実装ではなく就業規則の言語化。**

- 丸めのルール（実は明文化されていない／部署でバラバラ）
- 法定休日の特定方法（就業規則に書いていない）
- 休憩の扱い（実績を取っていない、所定控除で運用している）
- 残業の申請フロー（事前申請なのか事後承認なのか）
- **「運用と就業規則が一致していない」ことが判明する**

**この整理が終わらないと、計算ロジックを実装できない。** 資料PDFの「就業規則ヒアリングシート」は、この工数を前倒しするために作る。**資料請求が有効なリードになる設計。**

## Webで足りるか、ネイティブが必要か

**勤怠は、Webで足りる領域。**

| やりたいこと | Web（PWA） | ネイティブ |
|---|---|---|
| 打刻・シフト確認・申請 | **できる** | できる |
| ホーム画面から1タップ起動 | **できる**（PWA） | できる |
| 位置情報の付与 | **できる**（画面を開いている間） | できる |
| **打刻忘れのプッシュ通知** | 限定的 | **できる** |
| **バックグラウンドでの位置記録** | **できない** | できる（ただし監視の論点） |
| ICカードリーダー（NFC）の利用 | 限定的 | **できる** |

**判断の目安：** 打刻忘れのプッシュ通知を重視するなら、**LINE公式アカウントでの通知**が現実的な代替になる（アプリ開発より安い）。NFC を使うなら専用端末かネイティブ。

## デモ自体の運用コストを実測して記事に載せる

| ID | 要件 |
|---|---|
| COST-01 | デモ公開後、月次で「デモ起動数・打刻数・ホスティング費用」を記録すること |
| COST-02 | **`rounding_changed` の発生率を記録すること。**「読者の何%が丸め設定を変えて計算差を確かめたか」は、記事の主張が届いた指標になる（EMB-08） |
| COST-03 | 記録した実測値を記事に掲載し、**確認日を明記すること** |
| COST-04 | **既製勤怠サービス・ICカード端末の価格は変動するため、この文書に金額を書き込まない。** 記事執筆時点で公式ページを確認し、確認日を明記すること |

---

# 12. 実装タスク

**この順に進める。フェーズを飛ばさない。** 完了時は `[x]` に更新する。

## Phase 0 — 基盤と労働時間算出エンジン（5日）

### 0-1. 初期化
- [ ] Next.js 15 / TypeScript strict / App Router / Tailwind で初期化
- [ ] ESLint / Prettier / husky（pre-commit で lint + typecheck）
- [ ] 9章のディレクトリ構成を作成

### 0-2. `lib/time/`（**純粋関数。最初に固める**）
- [ ] 分数⇔時刻文字列の相互変換、**24:00超の表現**（CAL-05）
- [ ] 時間帯の重なり・差分・分割（深夜抽出の土台）
- [ ] 週の起算曜日を考慮した週の生成
- [ ] **単体テストを書く。特に日跨ぎと時間帯の分割は境界条件を網羅**

### 0-3. `lib/attendance/`（**このプロジェクトの心臓部。ここに最も時間をかける**）
- [ ] `rounding.ts`：丸め（単位 × 方向、出勤/退勤別）(FR-103, FR-105)
- [ ] `breaks.ts`：法定休憩の充足判定、所定休憩の自動控除（FR-106, FR-107）
- [ ] `holiday.ts`：法定休日／所定休日の判定（FR-111）
- [ ] `overtime.ts`：法定内／法定外の切り分け、週40時間（FR-109, FR-114）
- [ ] `night.ts`：深夜の抽出。**日跨ぎ対応**（FR-110, FR-116）
- [ ] `premium.ts`：**割増率の合成（重複の正しい処理）**（FR-112）
- [ ] `flexible.ts`：1ヶ月単位変形労働（FR-115）
- [ ] `corrections.ts`：**訂正の適用。生打刻は変更しない**（CAL-01）
- [ ] `daily.ts`：**日次サマリと `CalcBreakdown` の生成**（7章のコード参照）(FR-117)
- [ ] `monthly.ts`：月次集計（FR-601）
- [ ] `compliance.ts`：**36協定の4上限**（FR-605〜607）
- [ ] `paidLeave.ts`：有給の付与・残・年5日義務・失効（FR-608〜610）
- [ ] **`new Date()` を内部で呼ばない。現在時刻と就業規則を引数で受け取る**（CAL-03）
- [ ] メモ化と、**打刻の追加・設定変更の両方での無効化**（CAL-04, FR-120）
- [ ] **単体テストを網羅的に書く。以下を必ず含めること：**
  - [ ] **丸め単位・方向を変えると勤務時間と残業時間が変わること**（最重要）
  - [ ] **切り捨ての丸めで実労働時間より短くなること**（そしてそれが警告対象であること）
  - [ ] 法定休憩の判定（6時間超45分／8時間超60分／ちょうど6時間・8時間の境界）
  - [ ] **所定7時間の会社で7〜8時間が法定内残業、8時間超が法定外残業になること**
  - [ ] **深夜（22:00-5:00）の抽出。日跨ぎ勤務で正しく計算されること**
  - [ ] **法定休日労働と所定休日労働の割増率が異なること**
  - [ ] **深夜×法定外残業、深夜×法定休日 の割増重複が正しく合成されること**
  - [ ] 週40時間超の判定（起算曜日を変えると結果が変わること）
  - [ ] 遅刻・早退の算出
  - [ ] **訂正を承認すると勤務時間が変わり、生打刻は変わっていないこと**
  - [ ] **就業規則のバージョンにより、過去の期間は当時の設定で計算されること**（FR-702）
  - [ ] 36協定の4上限（月45／年360／複数月平均80／単月100）が正しく判定されること
  - [ ] 有給の年5日義務と失効予定の算出
  - [ ] **`CalcBreakdown` に全ステップの根拠が入っていること**（FR-117）
- [ ] **42名 × 3ヶ月分で月次集計が 200ms以下**をベンチマークで確認（NFR-03）

### 0-4. `lib/shift/`（純粋関数）
- [ ] `coverage.ts`：必要人数の充足判定（FR-403, FR-404）
- [ ] **`constraints.ts`：法令制約の検証**（週40h、連続勤務日数、勤務間インターバル）(FR-405)
- [ ] `generate.ts`：**貪欲法での自動生成 + 埋められない理由の記録**（FR-406, FR-407）
- [ ] `cost.ts`：人件費の見込み（割増込み）(FR-409)
- [ ] `variance.ts`：予実比較（FR-611）
- [ ] **単体テストを書く。特に制約違反の割当が弾かれること**

### 0-5. 型とシード
- [ ] `lib/types/` に7章の型定義をすべて実装
- [ ] `lib/seed/` の全ファイル
- [ ] **打刻を「9:00きっかり」にしない。9:03・8:57・19:47 の端数を持たせる**（最重要）
- [ ] **休憩不足の日、退勤打刻がない日、深夜・日跨ぎ勤務、休日出勤を含める**
- [ ] **36協定の上限に接近2名・超えそう1名、有給年5日未達3名、失効間近1名を作る**
- [ ] **シフトの予定と実績にズレを持たせる。必要人数が不足している日を数日作る**
- [ ] **就業規則のバージョンを2つ持ち、3ヶ月前に丸め設定が変わった状態にする**
- [ ] 打刻列の時系列に矛盾がないこと
- [ ] 日付は現在日時からの相対で生成する

### 0-6. ストアとリポジトリ
- [ ] `lib/store/{session,data,rules,sync,ui}.ts` を Zustand + persist で実装
- [ ] **`lib/repo/_scope.ts` を最初に実装する**（データスコープ + **項目スコープ**）(SCP-01〜04, FR-805〜807)
- [ ] `lib/repo/` の全モジュール。全メソッド async、Result型、スコープ適用
- [ ] **打刻レコードを更新・削除するコードを書かない**（CAL-01）
- [ ] **スコープの単体テストをこの時点で書く**（従業員が他人の打刻を取得できないこと、時給が含まれないこと）
- [ ] **打刻と労働時間の再算出に擬似ディレイを入れない**

### 0-7. デザインシステム
- [ ] `app/globals.css` にカラートークンを CSS 変数で定義（**区分色 + 深夜の斜線 + 充足色**）
- [ ] `tailwind.config.ts` から CSS 変数を参照するよう theme を拡張
- [ ] フォント（Noto Sans JP / Roboto Mono）を `next/font` で最適化。**時刻に `tabular-nums`**
- [ ] shadcn/ui を導入し、トークンに合わせて上書き
- [ ] **`DayBand.tsx`：24時間帯**（行内10px / 詳細32pxの2サイズ）+ **`BandRuler.tsx` 時刻目盛り**
- [ ] **`PunchMarker.tsx`：生打刻（中空・点線）／訂正後（塗り・実線）の描き分け**（FR-306）
- [ ] **帯の `aria-label` と、区分ごとの数値併記**（A11Y-05, A11Y-06）
- [ ] 共通コンポーネント：`AnomalyBadge`（**数字を必ず含む**）／`SegmentLegend` / `EmptyState`（2種の出し分け）／`Skeleton`（**帯の位置も確保**）
- [ ] `prefers-reduced-motion` の対応
- [ ] **`/dev/components` にコンポーネントカタログを作成**

### 0-8. デモ基盤
- [ ] `RoleSwitcher`（3ロール + 従業員の3ペルソナ選択）(FR-804)
- [ ] デモ切替バー（ダークな帯）+ **「これはデモです。打刻データは送信されません」**（SEC-02）
- [ ] **LAW-01 の注記コンポーネント**（設定画面・月次集計画面に常設）
- [ ] 「デモをリセット」(FR-808)／ロール権限外アクセス時の案内画面（FR-811）
- [ ] 元記事リンク・資料ダウンロード（FR-813）

### Phase 0 完了チェック
- [ ] **`lib/attendance/` のテストが全パターン通る**
- [ ] **「丸め設定を変えると残業時間が変わる」がテストで検証済み**
- [ ] **「深夜×法定外残業の割増重複」がテストで検証済み**
- [ ] **「訂正の承認で勤務時間が変わり、生打刻は変わらない」がテストで検証済み**
- [ ] **従業員ロールで他人の打刻が取得できず、時給が含まれない**ことがテストで検証済み
- [ ] 42名 × 3ヶ月の月次集計が200ms以下
- [ ] `/dev/components` で24時間帯が全パターン正しく描画される

---

## Phase 1 — 打刻と勤務照会（4日）

### 1-1. 打刻
- [ ] SC-001 打刻画面：**状態別に1つのボタン**（FR-201）
- [ ] **1タップ完了、確認ダイアログなし、100ms以下**（FR-202, IX-01, NFR-01）
- [ ] 打刻直後に**時刻を `punch-lg` で表示**、`aria-live` で通知（FR-203, A11Y-10）
- [ ] 直後の自己取消（3分以内）(FR-204)／二重打刻の防止（FR-205）
- [ ] 位置情報の任意付与（**拒否しても打刻できる**）(FR-206, FR-207, SEC-06)
- [ ] 退勤忘れの検出と翌日の通知（FR-208）
- [ ] 本日の勤務時間・休憩残り・退勤予定・今月の残業累計（F-P03）
- [ ] **月次確定後はロックされ、理由を表示すること**（FR-210）
- [ ] 全打刻に端末種別・位置・記録日時を残す（FR-211）

### 1-2. 勤務照会
- [ ] SC-010 月次勤務一覧：**日別の24時間帯 + 丸め前後の両方 + 合計行**（FR-104 相当の可視化）
- [ ] **異常の強調**（未打刻・休憩不足・承認待ち。**数字を含むバッジ**）(A11Y-07)
- [ ] SC-011 勤務日詳細：**24時間帯（詳細サイズ）+ 区分内訳 + 計算の根拠パネル**（FR-117）
- [ ] **`CalcBreakdownPanel`：全ステップを「入力 → 出力（根拠）」で表示。既定で開く**
- [ ] SC-020 残業サマリ：当月累計、**36協定の上限までの残り時間**、月別推移（F-P07）
- [ ] `ComplianceGauge`：4本のバー（月45h／年360h／複数月平均80h／単月100h）

### 1-3. 埋め込みモード
- [ ] **SC-900 埋め込みモード：24時間帯 + 丸め設定トグル + 生打刻/解釈後の並列表示**（FR-815, 2章）
- [ ] **丸め設定を変えると 50ms以下で再計算され、帯と数値が変わること**（IX-02, EMB-06）
- [ ] **切り捨て選択時に適法性の注意が出現すること**（FR-104, LAW-04）
- [ ] 位置情報を呼ばない・打刻させない（EMB-04）／アプリ内遷移をしない（EMB-05）
- [ ] 初期バンドル 60KB以下を実測（NFR-11）

### Phase 1 完了チェック
- [ ] **フローAが完走する**（埋め込みで丸めを変えると計算が変わる → 全画面 → 根拠が全ステップ見える）
- [ ] **4章「ロールを跨ぐ体験」の1と2が動作する**
- [ ] 打刻が100ms以下、丸め変更が50ms以下
- [ ] **従業員ロールで他人のデータが一切見えない**

---

## Phase 2 — 訂正・申請・承認（3日）

- [ ] SC-012 打刻訂正申請：**訂正前後の差分と再計算結果のプレビュー**（FR-301）
- [ ] 理由コードの必須化（FR-302）
- [ ] **申請中は勤務時間を変えないこと**（FR-303）
- [ ] SC-130 打刻訂正の承認：**差分と再計算結果を並べて表示**、承認／差戻し（FR-304）
- [ ] **連続処理（`A` 承認／`R` 差戻し／`→` 次）**（IX-06）
- [ ] 取り消し可能なトースト（IX-08）
- [ ] 承認後の「訂正あり」バッジと承認者の表示（FR-305）
- [ ] **勤務日詳細のタイムラインに、生打刻と訂正後の両方を描画**（FR-306）
- [ ] SC-030 休暇申請（有給／特別／欠勤、全日／半休／時間単位）(FR-307)
- [ ] 残日数の表示と不足時の警告（FR-308）
- [ ] SC-131 休暇申請の承認：**シフトへの影響表示**（FR-309）
- [ ] SC-031 有給残日数：付与履歴、残、**失効予定、年5日義務の進捗**（FR-609, FR-608）
- [ ] SC-013 申請状況／SC-050 通知センター
- [ ] 承認・差戻し・訂正を操作ログに記録（FR-311）

### Phase 2 完了チェック
- [ ] **フローBが完走する**（打刻忘れ → 訂正申請 → 差分確認 → 承認 → 再計算 → 生打刻は残る）
- [ ] **4章「ロールを跨ぐ体験」の3が動作する**
- [ ] 承認前に勤務時間が変わっていないことを確認

---

## Phase 3 — シフト（5日）

### 3-1. シフト希望
- [ ] SC-040 シフト希望の提出：期間内の希望入力、**締切カウントダウン**（FR-501）
- [ ] 締切後の提出不可、未提出者への催促プレビュー（FR-502）
- [ ] SC-116 希望の集計：提出状況、未提出者、希望の可視化（FR-503）
- [ ] 上限勤務日数・週の希望時間（FR-504）

### 3-2. シフト表
- [ ] SC-112 必要人数の設定（曜日別・時間帯別、テンプレート）(FR-403)
- [ ] SC-110 シフト表：**従業員×日付グリッド**、パターン割当（FR-401）
- [ ] **dnd-kit でのドラッグ割当・コピー・移動**（FR-402）
- [ ] **キーボード操作**（矢印移動、数字キーで割当、Delete で解除）(IX-05, A11Y-03)
- [ ] **必要人数の過不足を色表示**（FR-403）
- [ ] **法令違反の割当を弾き、理由を表示**（FR-405）
- [ ] SC-111 **時間帯別の充足ヒートマップ**（横軸=時間、縦軸=日付）(FR-404)
- [ ] SC-113 **シフト自動生成 + 埋められなかった理由の提示**（FR-406, FR-407）
- [ ] **生成結果は必ず下書き。自動確定しない**（FR-408）
- [ ] SC-114 **人件費の見込み**（割増込み、予算差分、深夜/休日の強調）(FR-409, FR-410)
- [ ] SC-115 公開：**通知プレビュー**、公開履歴（FR-411, FR-412）
- [ ] シフトパターンのマスタ（FR-413）／前月複製（FR-414）
- [ ] SC-041 確定シフト（従業員側、月／週表示、ics）(FR-507)
- [ ] SC-042 / SC-132 シフト交代（**相手の承諾 → 管理者承認の2段階**）(FR-505, FR-506)

### Phase 3 完了チェック
- [ ] **フローCの前半が完走する**（必要人数 → 希望集計 → 自動生成 → 手修正 → 人件費 → 公開 → 従業員に反映）
- [ ] **4章「ロールを跨ぐ体験」の4が動作する**
- [ ] シフト表がキーボードのみで操作完遂できる
- [ ] 法令違反の割当が弾かれ、理由が表示される

---

## Phase 4 — 集計・法令モニタリング・設定（4日）

### 4-1. 管理者の日次・予実
- [ ] SC-100 日次ダッシュボード（**未打刻者・遅刻・退勤忘れ・承認待ち**）(FR-613)
- [ ] SC-120 勤怠一覧（部署）：メンバー×日付、**異常の強調**
- [ ] SC-121 **予実比較：予定と実績の24時間帯を上下2段で重ねて描画**、時間差分・人件費差分（FR-611）
- [ ] SC-140 従業員一覧（当月残業、有給残、スキル）

### 4-2. 月次と法令
- [ ] SC-200 月次集計：**従業員別の全区分、異常の一覧**（FR-601, FR-602）
- [ ] **月次確定とロック。確定解除は管理部のみ + 操作ログ**（FR-603）
- [ ] SC-201 **給与CSV出力 + 出力項目のプレビュー**（FR-604）
- [ ] SC-202 **36協定モニタリング：4上限の進捗、上限接近者の抽出と残り時間**（FR-605, FR-606）
- [ ] 特別条項の適用回数（FR-607）
- [ ] SC-203 **有給の取得義務管理：年5日の進捗、未達者、失効予定**（FR-608, FR-609）
- [ ] 時間単位有給の上限管理（FR-610）
- [ ] 部署別・月別の残業推移グラフ（FR-612、Recharts 動的import）

### 4-3. 設定
- [ ] SC-204 **就業規則の設定**（丸め・所定・休憩・割増率・法定休日・週起算・変形労働）(FR-701)
- [ ] **設定のバージョン管理と適用開始日。過去は当時の設定で計算**（FR-702）
- [ ] **設定変更時に影響を受ける従業員数と期間を事前表示**（FR-703）
- [ ] **SC-210 計算ルールの検証画面：サンプル打刻への計算結果一覧。設定変更で即座に変わる**（FR-704）
- [ ] **法定を下回る設定・切り捨ての丸めに警告**（FR-705, LAW-03, LAW-04）
- [ ] SC-205 従業員マスタ（FR-706）／SC-207 休日カレンダー（FR-707）
- [ ] SC-206 部署・拠点（FR-708）／SC-208 通知文面（FR-709）
- [ ] SC-209 操作ログ（FR-710）
- [ ] **LAW-01 の注記が設定画面と月次集計画面に常設されていること**

### Phase 4 完了チェック
- [ ] **4章「ロールを跨ぐ体験」の5と6が動作する**
- [ ] **就業規則の丸め設定を変えると、過去分も含めて再計算される**
- [ ] 36協定モニタリングに意味のある警告が出ている（シードの品質確認）
- [ ] 月次確定後に打刻・訂正申請ができない

---

## Phase 5 — オフライン・記事統合・仕上げ（3日）

### 5-1. オフライン
- [ ] Service Worker / PWA マニフェスト／オフライン検知とバナー
- [ ] **オフライン打刻と未同期バッジ**（FR-209）
- [ ] オンライン復帰時の自動同期と進捗表示
- [ ] localStorage 上限時の圧縮処理（NFR-15）

### 5-2. 記事統合
- [ ] 記事側の埋め込みコードを作成し、**実際の記事ページで動作確認**（EMB-01〜06）
- [ ] **埋め込みで「丸め設定を変えると計算が変わる」が伝わることを確認**
- [ ] 記事の Core Web Vitals を埋め込み前後で比較検証
- [ ] 「全画面で試す」カードの設置
- [ ] 資料PDF（**就業規則ヒアリングシート**・損益分岐計算シート）の作成とダウンロード導線
- [ ] 元記事リンク・問い合わせ導線
- [ ] GA4 イベント設定。**`rounding_changed` を必ず含める**（EMB-07, EMB-08）
- [ ] **GA4 に氏名・打刻データ・給与額を送っていないことを確認**（EMB-09, SEC-03）
- [ ] `?embed=1` を `noindex` に、デモ本体の title / description / OGP（EMB-10）
- [ ] **記事内に LAW-02 の注記を明示**

### 5-3. 体験の総点検
- [ ] 4章「ロールを跨ぐ体験」の6パターンをすべて手動で確認
- [ ] 全34画面を開き、**空の画面が1つもないこと**を確認
- [ ] 空状態（データ0件／絞り込み0件）の出し分けを確認
- [ ] ガイドツアーを実装（FR-814）

### 5-4. アクセシビリティ監査
- [ ] axe-core を全画面に実行し、Critical / Serious を0件に
- [ ] **13px本文のコントラスト比を実測**（A11Y-01）
- [ ] **24時間帯の `aria-label` と区分の数値併記を確認**（A11Y-05, A11Y-06）
- [ ] **警告が色のみで伝わっていないことを確認**（数字とアイコンの併記）(A11Y-07)
- [ ] キーボードのみで全画面・打刻・シフト表・承認を完遂
- [ ] **打刻ボタンのタップ領域56px以上を確認**（A11Y-13）
- [ ] フォントサイズ200%での確認（A11Y-14）

### 5-5. パフォーマンス
- [ ] グラフを動的import に切り出し、初期バンドルを250KB以下に（NFR-12）
- [ ] **打刻100ms以下、丸め変更50ms以下、月次集計200ms以下を実測**（NFR-01〜03）
- [ ] シフト自動生成2秒以下、ドラッグ60fpsを実測（NFR-05, NFR-10）
- [ ] Lighthouse で Performance 90 / Accessibility 95（NFR-13）

### 5-6. 公開と記録
- [ ] Playwright で フローA・B・C の E2E
- [ ] Vitest：`lib/attendance` `lib/shift` `lib/time` `_scope` のカバレッジ **90%以上**（他テーマより高く設定する。計算誤りが未払賃金に直結するため）
- [ ] `/dev/components` を本番で非公開に
- [ ] Vercel へデプロイ
- [ ] **打刻数・`rounding_changed` 発生率の記録を開始**（COST-01, COST-02）
- [ ] 解説動画（3分）の収録：**打刻 → 丸め設定を変えると残業が変わる → 計算の根拠 → 打刻訂正の承認 → シフト作成と人件費 → 36協定の警告**
- [ ] 各フェーズのキャプチャを整理し、記事の「方法」パートの素材にまとめる

---

**合計 24営業日（約5週間）**／1名専任 + レビュー体制。

> **工数配分の意図：** Phase 0 に5日、**うち `lib/attendance/` のテストに3日**を充てる。労働時間の算出は**このプロジェクトの価値そのもの**であり、かつ**間違っていても画面上は正しく動いているように見える**。そして間違いは**未払賃金の遡及**という形で表面化する。他テーマよりテストカバレッジの目標を高く（90%）設定しているのはこの理由。
> Phase 3（シフト）に5日を割いているのは、シフト表のグリッドとD&D、充足ヒートマップ、自動生成、人件費試算が**それぞれ独立した実装量を持つ**ため。

---

# 13. 受入基準

1. 6章の優先度 P1 の要件がすべて実装され、テストで合格していること
2. 5章の主要フローA・B・Cが、エンドツーエンドで完走すること
3. 4章「ロールを跨ぐ体験」の6パターンがすべて動作すること
4. **打刻レコードが編集・削除されておらず、訂正が別レコードとして保持されていること**
5. **労働時間が保存されておらず、常に生打刻と就業規則設定から算出されていること**
6. **丸め設定（単位・方向）を変更すると、勤務時間・残業区分・割増額が変わること。かつ 50ms以下で反映されること**
7. **就業規則にバージョンがあり、過去の期間が当時の設定で計算されること**
8. **法定休憩の不足が、具体的な分数で警告されること**
9. **法定内残業と法定外残業が正しく切り分けられること**（所定 < 法定 の会社で検証）
10. **深夜（22:00-5:00）が日跨ぎ勤務でも正しく抽出され、割増の重複が正しく合成されること**
11. **法定休日労働と所定休日労働の割増率が異なること**
12. **`CalcBreakdown` に全ステップの入力・出力・根拠が入っており、画面に表示されていること**
13. **訂正申請中は勤務時間が変わらず、承認時に初めて反映されること。かつ生打刻が変わっていないこと**
14. **36協定の4上限（月45h／年360h／複数月平均80h／単月100h）が判定され、残り時間が明示されること**
15. **有給の年5日取得義務の進捗と未達者、失効予定が表示されること**
16. **シフト表で法令違反になる割当が弾かれ、理由が表示されること**
17. **シフト自動生成の結果が下書きであり、埋められなかった理由が提示されること**
18. **人件費の見込みがシフト確定前に試算され、予算との差分が表示されること**
19. **予実比較で、シフト（予定）と打刻（実績）が同じ時間軸上で並んで表示されること**
20. **月次確定後、打刻と訂正申請がロックされること**
21. **ロールを切り替えると、見える行と見える列（時給・給与額・人件費）の両方が変わること。従業員は他人の打刻を一切取得できないこと**（CSSで隠していない）
22. 打刻が100ms以下、月次集計（42名×3ヶ月）が200ms以下であること
23. **24時間帯が `aria-label` を持ち、区分ごとの時間が数値としても表示されていること**
24. **警告が色のみで伝えられておらず、具体的な数字を含んでいること**
25. **`LAW-01` の注記（専門家確認の推奨）が設定画面と月次集計画面に常設されていること**
26. **切り捨ての丸め・法定を下回る割増率に警告が表示されること**
27. **打刻データ・氏名・給与額が外部に一切送信されていないこと**（DevTools と GA4 のイベントで確認）
28. 全34画面のいずれにも空の状態がなく、リアルなデータが表示されていること
29. `lib/attendance` `lib/shift` `lib/time` のテストカバレッジが 90%以上であること
30. axe-core で Critical / Serious の指摘が0件であること
31. データアクセスがすべて `lib/repo/` を経由し、**労働時間の算出が `lib/attendance/` の外に存在しないこと**
32. **RELATE / KURA と並べたときに、明確に別のプロダクトとして見えること**（24時間帯と時刻目盛りが効いていること）

---

# 判断に迷ったときのルール

1. **計算ロジックを推測で書かない。** 割増賃金・休憩・36協定は法令に基づく。**曖昧なら必ず質問する。** これがこのプロジェクトで最も重要なルール
2. **打刻を書き換えない。** 訂正は別レコード + 承認。これがこのプロジェクトの存在理由
3. **労働時間を保存しない。** 生打刻と設定から常に算出する
4. **労働時間の算出を `lib/attendance/` の外に書かない。** 法令判定をコンポーネントに書かない
5. **`lib/attendance/` `lib/shift/` `lib/time/` を純粋関数に保つ。** 現在時刻と設定は必ず引数で受け取る
6. **時刻は分数で扱う。** 日跨ぎは 24:00超。文字列の切り貼りで時刻計算をしない
7. **計算の根拠（`CalcBreakdown`）を必ず返す。** 「なぜこの時間か」が説明できない実装をしない
8. **警告に必ず数字を入れる。**「不足しています」ではなく「3分不足しています」
9. **時給・給与額は権限がなければデータに含めない。** CSSで隠すのは実装ミス。**従業員が他人の打刻を取得できてはならない**
10. **法令解釈を断定しない。** LAW-01 の注記を削らない。設定は警告するがブロックしない
11. **自動生成を自動確定にしない。** シフトは必ず人が確認して公開する
12. スコープ外（3章 Won't have）は実装しない。**特にフレックス・1年単位変形労働・みなし残業は要件定義段階で洗い出す**
13. 「デモだから」を理由に品質を落とす判断はしない
14. **RELATE / KURA のトークン・コンポーネントをコピーしない。** TOKIWA の帯は**必ず時刻目盛りを伴う**
