# Doorstop アイテム記述ガイド — 詳細テンプレート

## 概要

Doorstop の `text` フィールドは **Markdown** をサポートしている。
1行の要約だけでなく、構造化された詳細な記述を行うことで、
仕様の正確性・レビュー効率・トレーサビリティが大幅に向上する。

本ガイドでは各層（REQ / SPEC / IMPL / TST）の詳細な書き方テンプレートと
実例を示す。

---

## 共通ルール

- `header` は **短い見出し**（20文字以内推奨）
- `text` に **Markdown で詳細を記述**する
- `groups` で機能グループを横断分類する（リスト型）
- `priority` で優先度を設定する（REQ/NFRのみ。`critical` / `high` / `medium` / `low` / `none` / `done`）
- `level` でドキュメント内の階層を表現する
- YAMLのリテラルブロック `|` を使い、複数行テキストを記述する
- `ref` は非推奨 — 外部ファイル紐付けには `references` を使う

---

## REQ（要件）— What を定義する

### テンプレート

```yaml
active: true
derived: false
groups:
  - CACHE
header: |
  関数キャッシュ
level: 1.0
links: []
normative: true
priority: high
reviewed: null
text: |
  ## 概要

  関数の実行結果をキャッシュし、同一入力に対して再実行せずに
  キャッシュから結果を返せること。

  ## 背景・動機

  データパイプラインやML実験では、同一パラメータでの再実行が頻発する。
  計算コストの高い関数の結果をキャッシュすることで、
  開発サイクルを大幅に短縮できる。

  ## 受入基準

  - 同期関数・非同期関数の両方をサポートすること
  - 同一引数での2回目以降の呼び出しでは、関数を実行せずキャッシュを返すこと
  - キャッシュキーは引数の値に基づき、引数の順序に依存しないこと
  - キャッシュの有無はユーザーコードに透過的であること

  ## 制約事項

  - ジェネレータ関数はキャッシュ対象外とする
  - 副作用のある関数のキャッシュは利用者の責任とする

  ## スコープ外

  - 分散キャッシュ（Redis等）は本要件の対象外
  - キャッシュの自動無効化（依存ファイル変更検知等）は別要件で扱う
```

### ポイント

| セクション | 目的 |
|---|---|
| 概要 | 1-2文で要件の本質を述べる |
| 背景・動機 | なぜこの要件が必要か（Why） |
| 受入基準 | 検証可能な条件のリスト |
| 制約事項 | 明示的な制限・前提条件 |
| スコープ外 | 意図的に含めないもの（誤解防止） |

---

## NFR（非機能要件）— どの品質水準かを定義する

NFRは「何をするか」ではなく「どの品質水準で動くか」を定義する。
REQと並列のルートドキュメント（`parent: null`）として管理し、
設計文書やテストから直接リンクできる。

### テンプレート

```yaml
active: true
derived: false
groups:
  - PERF
header: |
  キャッシュヒット性能
level: 1.0
links: []
normative: true
priority: high
reviewed: null
text: |
  ## 要件

  キャッシュヒット時のオーバーヘッド（キャッシュキー生成からデシリアライズ完了まで）が
  p99 で **1ms 以内** であること。

  ## 計測方法

  - `pytest-benchmark` で 1000 回以上の反復で p99 を計測
  - 計測対象: `@spot.mark()` デコレートされた関数の、
    キャッシュヒット時の呼び出し時間
  - 計測環境: CI の標準ランナー

  ## 根拠

  ユーザーが `@spot.mark()` を適用する関数は、元の実行時間が秒単位のものが多い。
  キャッシュ層のオーバーヘッドが元の実行時間に対して無視できる水準（1ms以内）でないと、
  「キャッシュの有無がユーザーコードに透過的」という要件を満たせない。

  ## 許容範囲

  | パーセンタイル | 基準値 | 区分 |
  |---|---|---|
  | p50 | 0.5ms 以内 | 目標 |
  | p99 | 1ms 以内 | 必須 |
  | p99.9 | 5ms 以内 | 許容 |
```

### NFR の書き方の原則

**定量的な基準値を必ず含める。** 曖昧なNFRは検証できない。

| 悪い例（曖昧） | 良い例（定量的） |
|---|---|
| 「高速であること」 | 「キャッシュヒット時のオーバーヘッドが p99 で 1ms 以内であること」 |
| 「安全であること」 | 「シリアライズデータに pickle を使用しないこと」 |
| 「使いやすいこと」 | 「公開 API のメソッド数が 10 以下であること」 |
| 「スケーラブルであること」 | 「10万件のキャッシュエントリで検索が 100ms 以内であること」 |

### ポイント

| セクション | 目的 |
|---|---|
| 要件 | 定量的な品質基準を1-2文で述べる |
| 計測方法 | どう検証するか（ツール、手順、環境） |
| 根拠 | なぜこの基準値か（ビジネス要件との接続） |
| 許容範囲 | 必須/目標/許容の段階的な基準値 |

### NFR のカテゴリとグループ名（ISO 25010 ベース）

| カテゴリ | グループ名 | 対象の品質特性 | 典型的な基準 |
|---|---|---|---|
| **性能** | `PERF` | 応答時間、スループット、リソース使用量 | p99レイテンシ、RPS、メモリ上限 |
| **セキュリティ** | `SEC` | 認証、認可、暗号化、監査 | 暗号方式、入力検証 |
| **信頼性** | `REL` | 可用性、耐障害性、復旧性 | SLA、RTO/RPO |
| **保守性** | `MNT` | モジュール性、再利用性、テスト容易性 | 循環的複雑度、コードカバレッジ |
| **可搬性** | `PRT` | OS互換、クラウド互換、DB互換 | サポートOS、Python バージョン |

### セキュリティ NFR の例（複雑度「低」）

```yaml
active: true
derived: false
groups:
  - SEC
header: |
  pickle 使用禁止
level: 2.0
links: []
normative: true
priority: critical
reviewed: null
text: |
  シリアライズ/デシリアライズに `pickle` モジュールを使用しないこと。

  - `pickle.loads()` は任意コード実行の脆弱性がある
  - 代替として MessagePack または JSON を使用する
  - 第三者ライブラリが内部で pickle を使用する場合も対象とする
```

> **NFR のトレーサビリティ:**
> NFR はルートドキュメントのため、設計文書（ARCH/SPEC 等）から NFR へリンクして
> 非機能制約の実現方針を明示する。テストからも直接 NFR にリンクして検証を対応付ける。
>
> ```
> REQ ← ARCH ← SPEC ← IMPL
>                ↑       ↑
> NFR ──────────┘       TST（非機能テスト）
> ```

---

## 見出し・コンテキスト（Non-normative）— 文脈を提供する

`normative: false` を設定することで、システム要件ではなく「人間が読むための章見出しや背景説明」としてアイテムを定義できます。

### テンプレート

```yaml
active: true
derived: false
groups:
  - CACHE
header: |
  キャッシュシステム概要
level: 1.0
links: []
normative: false
reviewed: null
text: |
  本章では、システムのパフォーマンス向上のためのキャッシュレイヤーの要件を定義する。

  ## 目的
  計算コストの高い関数の結果を再利用することで、全体のスループットを向上させる。

  ## 用語定義
  - **キャッシュヒット**: 保存された結果を再利用すること
  - **キャッシュミス**: 結果が未保存のため、関数を再実行すること
```

### ポイント
- ドキュメントをフラットなリストにせず、`level: X.0` と組み合わせて章立てを作るために使用します。
- `normative: false` のアイテムはカバレッジやリンク検証の対象から外れるため、`links` が空でもエラーになりません。

---

## ADR（決定）— Why を定義する

ADR（Architecture Decision Records）は、あらゆるレベルの意思決定を記録します。
要求・要件・仕様・設計・実装のどの段階でも、重要な判断があれば ADR に記録します。
ADR は独立したルート文書であり、`links` で関連する任意のアイテムを指せます。

### テンプレート

```yaml
active: true
derived: false
groups:
  - CACHE
header: |
  シリアライザの選定
level: 1.0
links:
- ARCH001: <fingerprint>
normative: true
status: accepted
reviewed: null
text: |
  ## コンテキスト（背景）

  関数の実行結果をキャッシュする際、PythonオブジェクトをそのままDBやBlobストレージに
  保存することはできない。何らかのシリアライザが必要となる。
  標準の `pickle` は任意コード実行の脆弱性があり、`json` はバイナリデータや
  カスタムオブジェクト（datetime等）の扱いに難がある。

  ## 決定

  デフォルトのシリアライザとして **MessagePack** を採用する。

  ## 結果（トレードオフ）

  - **メリット**: `json` より高速・コンパクトであり、バイナリ型をネイティブにサポートする。`pickle` のような任意コード実行の脆弱性がない。
  - **デメリット**: カスタムオブジェクトをシリアライズするには、拡張型の定義（ExtType）が必要になる。
  - **制約事項**: MessagePack に依存するため、パッケージのサイズが若干増加する。

  ## 棄却された代替案

  - **JSON**: テキストベースであり人間が読みやすいが、バイト列（`bytes`）を扱うたびにBase64エンコードが必要となり、キャッシュ用途としては非効率なため棄却。
  - **Pickle**: 標準ライブラリで完結するが、信頼できないデータをデシリアライズする際のリスクが許容できないため棄却。
```

### ポイント

| セクション | 目的 |
|---|---|
| コンテキスト（背景） | この判断が必要になった背景、問題の所在 |
| 決定 | 採用した具体的な方針・技術・アーキテクチャパターン |
| 結果（トレードオフ） | その決定によって生じるメリットとデメリット（制約） |
| 棄却された代替案 | 検討した他の選択肢と、なぜそれらを選ばなかったか |

> **status 属性**:
> `accepted`（採用）、`proposed`（提案中）、`deprecated`（廃止）、`superseded`（上書き）のいずれかを使用し、判断の状態を管理します。
>
> **links の柔軟性**:
> ADR は独立ルート文書（`parent: null`）のため、`links` は任意のアイテムを指せます。
> 例: 設計判断なら `ARCH001`、仕様判断なら `SPEC003`、要求判断なら `REQ005` にリンク。

---

## ARCH（基本設計）— Architecture を定義する（standardプロファイル）

ARCHはコンポーネント分割、コンポーネント間インターフェース、データフロー、
技術選定を定義する。「何を作るか」ではなく「どう構成するか」に焦点を当てる。
加えて、「**なぜこの構成にしたのか**」の設計根拠を記述し、
アーキテクチャ判断の追跡可能性を確保する。
fullプロファイルではHLD（High-Level Design）が同等の役割を担う。

### テンプレート

```yaml
active: true
derived: false
groups:
  - CACHE
header: |
  キャッシュサブシステム構成
level: 1.0
links:
- REQ001: <fingerprint>
normative: true
reviewed: null
text: |
  ## コンポーネント構成

  ```mermaid
  graph TD
    A[bs.Spot Factory] -->|DI| B[core.Spot Engine]
    B --> C[TaskDBBase]
    B --> D[SerializerProtocol]
    B --> E[BlobStorageBase]
    B --> F[StoragePolicyProtocol]
    B --> G[LimiterProtocol]
    B --> H[LifecyclePolicy]
  ```

  ## コンポーネント責務

  | コンポーネント | 責務 | インターフェース |
  |---|---|---|
  | core.Spot | キャッシュエンジン。キー生成→検索→実行→保存の制御 | `mark()`, `cached_run()` |
  | TaskDBBase | メタデータ永続化。キャッシュキーによる検索と保存 | `find_by_key()`, `insert()` |
  | SerializerProtocol | データのシリアライズ/デシリアライズ | `pack()`, `unpack()` |
  | BlobStorageBase | 大規模データの外部ストレージ保存 | `save()`, `load()`, `delete()` |
  | StoragePolicyProtocol | Blob保存の判定ポリシー | `should_save_as_blob()` |

  ## データフロー

  ```mermaid
  sequenceDiagram
    participant User as ユーザーコード
    participant Spot as core.Spot
    participant DB as TaskDB
    participant Ser as Serializer
    participant Blob as BlobStorage

    User->>Spot: fn(args)
    Spot->>DB: find_by_key(cache_key)
    alt キャッシュヒット
      DB-->>Spot: cached_data
      Spot->>Ser: unpack(data)
    else キャッシュミス
      Spot->>Spot: fn(args) 実行
      Spot->>Ser: pack(result)
      Spot->>DB: insert(metadata)
      opt Blob保存
        Spot->>Blob: save(key, data)
      end
    end
    Spot-->>User: result
  ```

  ## 設計根拠

  ### 駆動要因

  以下の品質特性と制約がこのアーキテクチャを形成した:

  - **拡張性**: ユーザーがストレージやシリアライザを差し替えたい
    → 全コンポーネントをProtocol/ABCで抽象化し、DI構成とした
  - **ゼロ設定**: ライブラリとして即座に使えること
    → ファクトリ関数（`bs.Spot()`）でデフォルト実装を自動ワイヤリング
  - **非同期IO**: 大規模データ保存がユーザーコードをブロックしないこと
    → ThreadPoolExecutorによるバックグラウンド永続化を分離

  ### なぜこのコンポーネント分割か

  キャッシュエンジン（core.Spot）を中心に、永続化関心事（DB, Storage, Serializer）と
  制御関心事（Policy, Limiter, Lifecycle）を分離した。

  - **永続化と制御の分離**: 保存先の変更（ローカル↔S3）が制御ロジックに影響しないため
  - **単一ファクトリへのDI集約**: 構成の複雑さをユーザーから隠蔽し、
    テスト時のモック差し替えも容易にするため
  - **Policy のプロトコル化**: 閾値判定ロジックの差し替えを可能にし、
    ユースケースごとの柔軟な設定を実現するため

  ### 検討した代替構成

  - **モノリシック構成**（全機能を1クラスに集約）: シンプルだが、
    ストレージ差し替え時にキャッシュロジックまで変更が波及するため棄却
  - **イベント駆動構成**（保存をイベントバスで分離）: 柔軟だが、
    ライブラリとしてはオーバーヘッドが大きく、デバッグも困難なため棄却

  > **関連ADR**: ADR001（シリアライザ選定）、ADR008（DB DI）等を参照。
  > 個別の技術選定判断はADRに記録し、ここでは構成全体の根拠を記述する。

  ## 技術選定

  | 技術領域 | 選定 | 理由 |
  |---|---|---|
  | メタデータDB | SQLite | ゼロ設定、組み込み可能、十分な性能 |
  | シリアライズ | MessagePack | JSONより高速・コンパクト、バイナリ対応 |
  | ストレージ | ローカルファイル / S3 | 小規模はローカル、大規模はS3で透過切替 |

  ## 非機能要件方針

  - **性能**: キャッシュヒット時のオーバーヘッドは1ms以内
  - **スレッド安全性**: `default_wait=False` 時はThreadPoolExecutorで非同期IO
  - **拡張性**: 全コンポーネントはProtocol/ABCで抽象化、DI差し替え可能
```

### ポイント

| セクション | 目的 |
|---|---|
| コンポーネント構成 | システムの構成要素と関係を図示（Mermaid推奨） |
| コンポーネント責務 | 各コンポーネントの責務とインターフェース |
| データフロー | コンポーネント間のデータの流れ（シーケンス図推奨） |
| **設計根拠** | **なぜこの構成にしたか。駆動要因・分割理由・代替案と棄却理由** |
| 技術選定 | 採用技術とその理由 |
| 非機能要件方針 | 性能、セキュリティ、スケーラビリティの目標値 |

> **ARCHとSPECの書き分け基準**:
> ARCH はコンポーネント **間** の設計（外から見た構造）、
> SPEC はコンポーネント **内** の設計（中の実装方針）。
> 「このインターフェースを通じて何をやりとりするか」はARCH、
> 「このインターフェースの中でどう処理するか」はSPEC。
>
> **ARCHとADRの書き分け基準**:
> ADR は**個別の設計判断**の記録（「なぜMessagePackか」「なぜDIか」）、
> ARCH の設計根拠は**構成全体の根拠**（「なぜこのコンポーネント分割か」「何がこの構造を駆動したか」）。
> 個別判断はADRに書き、ARCHからADRを参照する形で連携する。

---

## HLD（基本設計）— Architecture を定義する（fullプロファイル）

HLDはARCHと同等の役割だが、V字モデルの用語に合わせてHLD（High-Level Design）と呼称する。
サブシステム分割、コンポーネント間インターフェース、データフロー、技術選定を定義し、
**結合テスト（integration test）に対応する設計レベル**である。

### テンプレート

```yaml
active: true
derived: false
groups:
  - AUTH
header: |
  認証サブシステム構成
level: 1.0
links:
- REQ001: <fingerprint>
normative: true
reviewed: null
text: |
  ## サブシステム構成

  ```mermaid
  graph TD
    A[API Gateway] -->|認証ヘッダ| B[AuthService]
    B --> C[TokenVerifier]
    B --> D[SessionStore]
    C --> E[JWK Provider]
    D --> F[Redis]
  ```

  ## サブシステム責務

  | サブシステム | 責務 | 外部インターフェース |
  |---|---|---|
  | AuthService | 認証フロー制御。トークン検証とセッション管理を統括 | `authenticate()`, `logout()` |
  | TokenVerifier | JWT トークンの署名検証と有効期限チェック | `verify(token) → Claims` |
  | SessionStore | セッション状態の永続化（Redis） | `get()`, `set()`, `delete()` |
  | JWK Provider | 外部 IdP から公開鍵を取得・キャッシュ | `get_signing_key(kid)` |

  ## サブシステム間データフロー

  ```mermaid
  sequenceDiagram
    participant GW as API Gateway
    participant Auth as AuthService
    participant TV as TokenVerifier
    participant SS as SessionStore

    GW->>Auth: authenticate(token)
    Auth->>TV: verify(token)
    TV-->>Auth: Claims
    Auth->>SS: set(session_id, claims)
    Auth-->>GW: AuthResult(user_id, session_id)
  ```

  ## 設計根拠

  ### 駆動要因

  - **セキュリティ**: トークン検証は専用コンポーネントに分離し、
    検証ロジックの変更がフロー制御に影響しないようにする
  - **可用性**: セッションストアをRedisに外部化し、
    アプリケーションのスケールアウトを可能にする

  ### 検討した代替構成

  - **モノリシック認証**（1クラスで全機能を担う）: シンプルだが、
    トークン方式の変更時に全体を修正する必要があるため棄却
  - **外部認証サービス委譲**（Auth0等に全面委託）: 運用負荷は下がるが、
    細粒度の認可制御がしにくいため棄却

  ## 技術選定

  | 技術領域 | 選定 | 理由 |
  |---|---|---|
  | トークン方式 | JWT (RS256) | ステートレス検証、公開鍵配布が容易 |
  | セッションストア | Redis | 高速、TTL 自動削除、クラスタ対応 |
  | 鍵管理 | JWK | 鍵ローテーションが容易 |

  ## 非機能要件方針

  - **性能**: トークン検証はp99で10ms以内（公開鍵キャッシュ前提）
  - **可用性**: Redisダウン時はトークン検証のみで動作（グレースフルデグラデーション）
  - **セキュリティ**: トークンの有効期限は最大1時間、リフレッシュトークンは7日
```

### ポイント

HLDはARCHと同じセクション構成を使うが、以下の点が異なる：

| 観点 | ARCH（standard） | HLD（full） |
|---|---|---|
| V字モデル対応 | 暗黙的 | 明示的（結合テストレベルに対応） |
| 下位文書 | SPEC | LLD |
| テスト対応 | TST (test_level=integration) | TST (test_level=integration) |
| 想定規模 | チーム開発 | 多チーム開発、規制産業 |

> **HLDとLLDの書き分け基準:**
> HLD はサブシステム **間** の関係（外部IF、データフロー、技術選定理由、非機能要件の具体値）。
> LLD はサブシステム **内** の設計（関数シグネチャ、アルゴリズム詳細、状態遷移、エラー処理）。
>
> - HLD: 「認証サブシステムはOAuth 2.0 + JWTを採用する」
> - LLD: 「JWTトークン生成はRS256で署名し、有効期限は3600秒」

---

## LLD（詳細設計）— How を定義する（fullプロファイル）

LLDはSPECと同等の役割だが、V字モデルの用語に合わせてLLD（Low-Level Design）と呼称する。
HLDで定義されたコンポーネントの内部設計を定義し、
**単体テスト（unit test）に対応する設計レベル**である。

### テンプレート

```yaml
active: true
derived: false
gherkin: |
  Scenario: 有効なJWTトークンの検証
    Given RS256で署名された有効なJWTトークンがある
    When TokenVerifier.verify(token) を呼び出す
    Then Claims オブジェクトが返り user_id と exp が含まれる

  Scenario: 期限切れトークンの拒否
    Given 有効期限を過ぎたJWTトークンがある
    When TokenVerifier.verify(token) を呼び出す
    Then TokenExpiredError が送出される

  Scenario: 不正署名の拒否
    Given 異なる秘密鍵で署名されたJWTトークンがある
    When TokenVerifier.verify(token) を呼び出す
    Then InvalidSignatureError が送出される

  Scenario: 公開鍵キャッシュの自動更新
    Given キャッシュされた公開鍵の kid がトークンのヘッダと一致しない
    When TokenVerifier.verify(token) を呼び出す
    Then JWK Provider から最新の公開鍵を取得してから検証する
groups:
  - AUTH
header: |
  TokenVerifier
level: 1.0
links:
- HLD001: <fingerprint>
normative: true
reviewed: null
text: |
  ## インターフェース

  ```python
  class TokenVerifier:
      def __init__(
          self,
          jwk_provider: JWKProvider,
          algorithms: list[str] = ["RS256"],
          leeway: int = 30,  # 秒
      ) -> None: ...

      def verify(self, token: str) -> Claims:
          """JWT トークンを検証し、Claims を返す。

          Raises:
              TokenExpiredError: トークンが有効期限切れ
              InvalidSignatureError: 署名が不正
              MalformedTokenError: トークン形式が不正
          """
          ...
  ```

  ## 振る舞い

  ### 検証フロー

  1. トークンのヘッダから `kid`（Key ID）を抽出する
  2. `JWKProvider` から対応する公開鍵を取得する（キャッシュ優先）
  3. 署名を検証する
  4. `exp`（有効期限）を現在時刻 + `leeway` と比較する
  5. **成功時**: Claims オブジェクトを返す
  6. **失敗時**: 種別に応じた例外を送出する

  ### 公開鍵キャッシュ戦略

  - 初回アクセス時に JWK エンドポイントから取得しキャッシュする
  - `kid` が不一致の場合のみ再取得する（鍵ローテーション対応）
  - 再取得は最大60秒に1回に制限する（DoS防止）

  ## パラメータ詳細

  | パラメータ | デフォルト | 説明 |
  |---|---|---|
  | `algorithms` | `["RS256"]` | 許可する署名アルゴリズム。`none` は常に拒否 |
  | `leeway` | `30` | 有効期限の許容誤差（秒）。クロック差を吸収 |

  ## エラーハンドリング

  | エラー | 条件 | HTTP ステータス |
  |---|---|---|
  | `TokenExpiredError` | `exp + leeway < now` | 401 |
  | `InvalidSignatureError` | 署名不一致 | 401 |
  | `MalformedTokenError` | ヘッダ/ペイロードのデコード失敗 | 400 |

  ## 設計判断

  ### `leeway` パラメータのデフォルト値

  30秒に設定した。RFC 7519 では具体的な推奨値を定めていないが、
  NTP同期が前提の環境ではクロック差は通常数秒以内であり、
  30秒は十分なマージンを持ちつつ、期限切れトークンの悪用リスクを抑える妥当な値と判断した。

  **棄却した代替案**: `leeway=0`（厳密モード）をデフォルトにする案。
  クロック差によるユーザー体験の劣化リスクが高いため棄却。

  ### 例外の階層設計

  `TokenExpiredError`, `InvalidSignatureError`, `MalformedTokenError` を
  個別の例外クラスとし、共通基底クラス `TokenVerificationError` を持たせた。
  呼び出し側が「全部まとめてキャッチ」も「種別ごとに分岐」もできるようにするため。

  ## エッジケース

  - `alg: none` のトークンは常に拒否する（alg confusion attack 防止）
  - JWK エンドポイント到達不能時はキャッシュ済みの鍵でフォールバック
  - キャッシュも空の場合は `JWKUnavailableError` を送出
```

### ポイント

LLDはSPECと同じセクション構成を使うが、以下の点が異なる：

| 観点 | SPEC（lite/standard） | LLD（full） |
|---|---|---|
| 上位文書 | REQ（lite）/ ARCH（standard） | HLD |
| V字モデル対応 | 暗黙的 | 明示的（単体テストレベルに対応） |
| `gherkin` | 推奨 | 推奨（テストケースへの直接変換を重視） |
| 想定規模 | 小〜中規模 | 大規模、規制産業 |

---

## SPEC（仕様）— How を定義する

> **プロファイルによる役割の違い:**
> - **lite**: 基本設計＋詳細設計を兼ねる（REQの直下）
> - **standard**: 詳細設計に特化する（ARCHの直下）
> - fullプロファイルではLLD（Low-Level Design）が同等の役割を担う

### テンプレート

```yaml
active: true
derived: false
gherkin: |
  Scenario: 基本キャッシュ動作
    Given 空のDBとLocalStorageが初期化されている
    When @spot.mark() でデコレートされた関数を同一引数で2回呼び出す
    Then 1回目は関数が実行されキャッシュされる
    And 2回目はキャッシュから結果が返り関数は実行されない

  Scenario: sync/async 自動判定
    Given sync関数が定義されている
    When @spot.mark() でデコレートする
    Then _execute_sync() に委譲される

  Scenario: ジェネレータ関数の拒否
    Given ジェネレータ関数が定義されている
    When @spot.mark() を適用する
    Then ConfigurationError が送出される

  Scenario: version によるキャッシュ無効化
    Given version="v1" でキャッシュが作成されている
    When version="v2" に変更して同一引数で呼び出す
    Then キャッシュミスとなり関数が再実行される
groups:
  - CACHE
header: |
  mark デコレータ
level: 1.0
links:
- REQ001: <fingerprint>
normative: true
reviewed: null
text: |
  ## インターフェース

  ```python
  @spot.mark(
      save_blob: bool | None = None,
      keygen: KeyGen | None = None,
      version: str = "",
      content_type: ContentType = ContentType.PYTHON_OBJECT,
      serializer: SerializerProtocol | None = None,
      save_sync: bool | None = None,
      retention: str | None = None,
      hooks: list[HookBase] | None = None,
  )
  def my_func(x, y): ...
  ```

  ## 振る舞い

  ### 基本フロー

  1. デコレータが対象関数をラップする
  2. 呼び出し時にキャッシュキーを生成する
  3. DBからキャッシュを検索する
  4. **ヒット時**: デシリアライズして結果を返す（関数は実行しない）
  5. **ミス時**: 関数を実行し、結果をシリアライズしてDB/Blobに保存する

  ### sync/async 自動判定

  - `inspect.iscoroutinefunction(fn)` で判定する
  - sync関数 → `_execute_sync()` に委譲
  - async関数 → `_execute_async()` に委譲

  ## パラメータ詳細

  | パラメータ | デフォルト | 説明 |
  |---|---|---|
  | `save_blob` | `None` | `None`: StoragePolicy に委譲、`True`: 強制Blob保存、`False`: DB内保存 |
  | `keygen` | `None` | キャッシュキー生成のカスタマイズ。特定引数の除外等 |
  | `version` | `""` | バージョン文字列。変更するとキャッシュが無効化される |
  | `content_type` | `PYTHON_OBJECT` | セマンティックなコンテンツ種別 |
  | `serializer` | `None` | `None`: Spotのデフォルトを使用。関数単位でオーバーライド可能 |
  | `save_sync` | `None` | `None`: Spotのデフォルト（`default_wait`）に従う |
  | `retention` | `None` | ライフサイクルポリシーの保持期間（例: `"30d"`, `"1y"`） |
  | `hooks` | `None` | 関数単位のフックリスト |

  ## エラーハンドリング

  - ジェネレータ関数を渡した場合 → `ConfigurationError` を送出
  - シリアライズ失敗時 → `SerializationError` を送出
  - Blob保存失敗（`save_sync=False`） → ERROR ログに記録、例外は送出しない

  ## 設計判断

  ### デコレータファクトリパターンの採用

  `@mark` と `@mark()` の両方をサポートするため、二段階デコレータファクトリを採用した。
  Flask の `@app.route` 等で広く使われるパターンであり、ユーザーの学習コストが低い。

  **棄却した代替案**: メタクラスベースの登録方式。宣言的だがデバッグが困難なため棄却。

  ### sync/async 分岐をデコレーション時に行う理由

  呼び出し時に毎回 `iscoroutinefunction()` で判定する方式も可能だが、
  ランタイムオーバーヘッドをゼロにするため、デコレーション時に確定させる方式を選択した。
  動的に async に切り替えるケースは実用上ないと判断した。

  > **ADR との使い分け**: 上記の判断はこの SPEC の文脈でのみ意味を持つ局所的な判断である。
  > もしシリアライザ選定のように複数コンポーネントに影響し、代替案の検討が重い判断であれば、
  > ADR に切り出す。

  ## エッジケース

  - ラップされた関数の `__name__`, `__doc__`, `inspect.signature` は保持される
  - デコレータの多重適用は未定義動作とする
  - `version` が空文字の場合と未指定の場合は同一とみなす
```

### `text` と `gherkin` の役割分担

| | `text` | `gherkin` |
|---|---|---|
| **内容** | 技術仕様（API、アルゴリズム、パラメータ、エラー処理） | 振る舞いシナリオ（Given/When/Then） |
| **対象読者** | 実装者・レビュアー | テスト設計者・ステークホルダー |
| **答える問い** | 「どう実装するか？」 | 「どう振る舞うか？」 |
| **TST への変換** | 間接的（テスト設計に判断が必要） | 直接的（シナリオ ≒ テストケース） |

### gherkin の書き方

- 1つの SPEC アイテムに対して、主要な振る舞いを **3〜7 シナリオ** で記述する
- 各シナリオは TST のテストケースに 1:1 で対応させることを目指す
- 正常系 → 異常系 → エッジケースの順で記述する
- 技術的な実装詳細は `text` に書き、`gherkin` はユーザー視点の振る舞いに集中する
- 複雑度が低い SPEC（自明な機能）では `gherkin` を省略してもよい

### ポイント

| セクション | 目的 |
|---|---|
| インターフェース | コードレベルのAPI定義（シグネチャ） |
| 振る舞い | 処理フローをステップで記述 |
| パラメータ詳細 | 各パラメータの意味・デフォルト・挙動を表で整理 |
| **設計判断** | **なぜこのAPI設計/アルゴリズムにしたか。代替案と棄却理由** |
| エラーハンドリング | 異常系の挙動を明示 |
| エッジケース | 境界条件・未定義動作を記述 |

---

## IMPL（実装）— 実装の生きた記録

IMPLは単なる「何を実装したか」のリストではなく、**実装ジャーナル**として機能する。
設計判断の根拠、実装中の気づき、将来の課題、仕様へのフィードバックを記録し、
コードだけでは伝わらない知識を保持する。

### `references` と `text` の役割分担

| | `references` | `text` |
|---|---|---|
| **粒度** | ファイルレベル（最大2–3ファイル） | クラス・メソッド・関数レベル |
| **対象読者** | ツール・トレーサビリティレポート | 開発者・レビュアー |
| **答える問い** | 「どのファイルを見ればいいか？」 | 「そのファイルの中の何を、なぜそう実装したか？」 |
| **データ性質** | 構造化ポインタ（機械可読） | 自由記述の知識（人間可読） |

`references` はファイルレベルに留め、クラス/メソッド/関数の詳細は `text` に記述する。
ファイルパスは安定的だが、クラス・メソッド名はリファクタリングで変わりやすい。
構造化フィールドに入れると更新負荷が高いため、`text` に柔軟に記述する方が適切である。

### テンプレート

```yaml
active: true
derived: false
groups:
  - CACHE
header: |
  mark デコレータ実装
level: 1.0
links:
- SPEC001: <fingerprint>
normative: true
references:
  - path: src/beautyspot/core.py
    type: file
reviewed: null
text: |
  ## 実装概要

  `Spot.mark()` メソッドが本仕様の中核。二段階デコレータファクトリとして、
  `_execute_sync()` と `_execute_async()` に処理を委譲する。
  sync/async の判定は**デコレーション時**に `inspect.iscoroutinefunction()` で行い、
  呼び出し時の判定オーバーヘッドを回避している。

  ## 設計判断

  ### 二段階デコレータファクトリの採用

  `mark()` がオプションを受け取り、内部の `decorator()` が実際のラッピングを行う。
  `@mark` (括弧なし) と `@mark()` (括弧あり) の両方をサポートするため、
  引数の型で分岐するパターンを採用した。

  ### sync/async 分岐のタイミング

  デコレーション時に判定する方式を採用。呼び出し時に毎回判定する方式と比較し、
  ランタイムオーバーヘッドがゼロになる利点がある。ただし、デコレーション後に
  関数の性質が変わるケース（動的に async に切り替える等）には対応できない。

  ## 実装メモ

  - ジェネレータ判定は `isgeneratorfunction` と `isasyncgenfunction` の
    両方をチェックする必要がある（async generator も拒否対象）
  - `functools.wraps(fn)` により `__name__`, `__doc__`, `__module__`,
    `__qualname__`, `__annotations__` が保持される。ただし
    `inspect.signature` の保持は `__wrapped__` 属性による

  ## TODO / 技術的負債

  - デコレータの多重適用時の挙動が未定義（検出・警告の追加を検討）
  - `@mark` 括弧なし記法のテストカバレッジが不足

  ## 仕様への遡上事項

  - `version=""` と `version` 未指定の扱いが SPEC001 で曖昧
    → 現実装では同一として扱っているが、明示的な仕様化が望ましい
```

### ポイント

| セクション | 目的 | 必須度 |
|---|---|---|
| 実装概要 | クラス・メソッドレベルの実装場所と処理の流れ | 必須 |
| 設計判断 | なぜこの実装方法を選んだか。代替案と棄却理由 | 必須 |
| 実装メモ | 実装中の気づき、工夫、ハマった点 | 推奨 |
| TODO / 技術的負債 | 既知の改善点、将来の課題 | 該当時 |
| 仕様への遡上事項 | 実装中に発覚したSPEC/ARCHの不備・曖昧さ | 該当時 |

> **`references` フィールド**: 実装ファイルパスを `references` に設定すると、
> 1つのアイテムに対して複数のファイルを紐付けられる。
> ドメインロジックの実装ファイルのみに厳選し（最大2–3ファイル）、
> `conftest.py` や共通ユーティリティは含めない（レビュー疲れ防止）。
> クラス・メソッド名は `references` に含めず、`text` の実装概要に記述する。

---

## TST（テスト）— Verify を定義する

TSTは3層構造で記述する。全TSTに「目的」と「検証観点」を必須とし、
複雑度が高いTSTには「主要シナリオ（TC-N）」を追加する。

### テンプレート（複雑度「高」の場合）

```yaml
active: true
derived: false
groups:
  - CACHE
header: |
  mark デコレータテスト
level: 1.0
links:
- SPEC001: <fingerprint>
normative: true
references:
  - path: tests/integration/core/test_mark.py
    type: file
reviewed: null
text: |
  ## 目的

  `@spot.mark()` はユーザーが最も頻繁に使う公開APIであり、
  キャッシュの正確性・透過性・拡張性の基盤となる。
  デコレーションによって元の関数の挙動やメタデータが損なわれると、
  ユーザーコードのデバッグや型チェックに支障をきたすため、
  ラッパーの透過性を厳密に検証する。

  ## 検証観点

  - **正常系**: 同一引数での2回目呼び出しでキャッシュヒットし、
    元の関数が再実行されないこと
  - **Blobモード**: `save_blob=True` 指定時にBlobストレージ経由で
    保存されること
  - **ラッパー透過性**: `__name__`, `__doc__`, `inspect.signature` が
    元の関数と一致すること
  - **キャッシュ無効化**: `version=` を変更するとキャッシュキーが変わり、
    既存キャッシュがヒットしなくなること

  ## 主要シナリオ

  ### TC-1: 基本キャッシュ動作

  **前提条件**: 空のDBとLocalStorageが初期化されている

  1. `@spot.mark()` で関数をデコレートする
  2. 同一引数で2回呼び出す
  3. **期待結果**:
     - 1回目: 関数が実行され、結果がキャッシュされる
     - 2回目: 関数は実行されず、キャッシュから結果が返る
     - 両方の戻り値が一致する

  ### TC-2: Blobモード保存

  **前提条件**: `save_blob=True` で設定

  1. `@spot.mark(save_blob=True)` で関数をデコレートする
  2. 関数を実行する
  3. **期待結果**:
     - 結果がBlobストレージに保存される
     - DB上の `storage_type` が `FILE` である

  ### TC-3: シグネチャ保持

  1. docstring と型アノテーション付きの関数をデコレートする
  2. **検証項目**:
     - `fn.__name__` が元の関数名と一致する
     - `fn.__doc__` が元のdocstringと一致する
     - `inspect.signature(fn)` が元のシグネチャと一致する

  ### TC-4: ジェネレータ関数の拒否

  1. ジェネレータ関数に `@spot.mark()` を適用する
  2. **期待結果**: `ConfigurationError` が送出される

  ### TC-5: version によるキャッシュ無効化

  1. `version="v1"` でキャッシュを作成する
  2. `version="v2"` に変更して同一引数で呼び出す
  3. **期待結果**: キャッシュミスとなり、関数が再実行される
```

### テンプレート（複雑度「中〜低」の場合）

```yaml
text: |
  ## 目的

  `TokenBucket` のレート制御が不正だと、APIの過負荷や
  スループットの不必要な低下を招く。GCRA アルゴリズムの
  スムーズなレート制御を検証する。

  ## 検証観点

  - **初期化バリデーション**: rate ≤ 0 等の不正パラメータが拒否されること
  - **max_cost 超過拒否**: バケット容量を超えるリクエストが即座に拒否されること
  - **GCRA 待機動作**: トークン補充まで適切な時間だけ待機すること
  - **バースト許容**: 容量分のリクエストがバーストで処理できること
```

### ポイント

| セクション | 目的 | 必須度 |
|---|---|---|
| 目的 | なぜこのテストが必要か（リスク・影響） | 必須 |
| 検証観点 | 何を確認するか（観点の箇条書き） | 必須 |
| 主要シナリオ | 具体的なTC-N（前提条件・手順・期待結果） | 複雑度「高」のみ |

### 複雑度の判断基準

| 複雑度 | 構成 | 例 |
|---|---|---|
| **低** | 目的 + 検証観点（3-4項目） | Retention パース、CLI version コマンド |
| **中** | 目的 + 検証観点（5-7項目） | ストレージポリシー、LifecyclePolicy |
| **高** | 目的 + 検証観点 + 主要シナリオ（TC-N） | mark デコレータ、LocalStorage、Thundering Herd |

---

## 実践ガイダンス

### Why の書き分け — アイテム内の根拠 vs. ADR

各層のアイテムは **What** や **How** だけでなく、「なぜそうしたか」という **Why** も記録する。
Why は一枚岩ではなく、層ごとに異なる種類があり、書く場所も異なる。

#### 層ごとの Why

| 層 | 答える問い | Why の種類 | `text` 内のセクション名 |
|---|---|---|---|
| REQ | なぜこの要件が必要か | ビジネスの動機 | 背景・動機 |
| NFR | なぜこの品質水準か | 品質目標の根拠 | 根拠 |
| ARCH/HLD | なぜこの構成にしたか | 構造の根拠 | 設計根拠 |
| SPEC/LLD | なぜこのAPI/アルゴリズムにしたか | 詳細設計の判断 | 設計判断 |
| IMPL | なぜこの実装方法を選んだか | 実装上の判断 | 設計判断 |
| TST | なぜこのテストが必要か | リスクの認識 | 目的 |
| ADR | なぜその選択をしたか | **重大な意思決定** | （ADR全体） |

#### アイテム内に埋め込む Why vs. ADR に切り出す Why

**原則: まずアイテム内に書く。重みがあるならADRに昇格させる。**

| 判断基準 | アイテム内に埋め込む | ADR に切り出す |
|---|---|---|
| **影響範囲** | そのアイテムの文脈でのみ意味を持つ | 複数のコンポーネント/層に影響する |
| **代替案の検討** | 軽い比較で済んだ | 複数の代替案を真剣に検討した |
| **見直しの可能性** | 今後変わる見込みが低い | 技術環境や要件の変化で再検討しうる |
| **合意の必要性** | 個人の判断で十分 | チーム内での合意が必要だった |
| **後からの理解** | コードや仕様を読めば自明 | 背景を知らないと判断の妥当性が分からない |

#### 具体例: 同じ判断をどちらに書くか

**アイテム内に埋め込むケース（SPEC の設計判断セクション）:**

> sync/async の分岐をデコレーション時に行うか、呼び出し時に行うかを検討した。
> ランタイムオーバーヘッドをゼロにするため、デコレーション時に確定させる方式を選択した。

→ この判断は `mark` デコレータの SPEC の中でのみ意味を持ち、他のコンポーネントに影響しない。

**ADR に切り出すケース:**

> シリアライザとして MessagePack / JSON / Pickle を検討した。
> セキュリティ、性能、バイナリ対応を総合的に評価し、MessagePack を採用した。

→ シリアライザの選定はストレージ、DB、テスト戦略、依存パッケージなど広範囲に影響する。
代替案の検討も重く、技術環境の変化で再検討する可能性がある。

#### ADR との連携パターン

アイテム内の Why と ADR は相互参照で連携する。

```
SPEC の text:
  ## 設計判断
  シリアライザには MessagePack を採用した。
  選定の詳細な検討と棄却理由は ADR005 を参照。

ADR005 の links:
  - ARCH005: <fingerprint>   ← 影響を受ける設計
```

アイテム内には判断の**結論と理由の要約**を書き、
検討過程の詳細（代替案の分析、ベンチマーク結果等）はADRに記録する。
これにより、SPECを読む人は「何を選んだか」を即座に把握でき、
「なぜ選んだか」の詳細が必要な場合はADRをたどれる。

---

### `level` のナンバリング戦略

`level` はドキュメント内の階層構造を表現する。
ドキュメントタイプ（REQ, SPEC 等）の中での章立てに使い、
Non-normative アイテムと組み合わせてセクションヘッダを作る。

#### ナンバリングルール

| レベル | 用途 | 例 |
|---|---|---|
| `X.0` | セクションヘッダ（通常 `normative: false`） | `1.0` = キャッシュシステム |
| `X.Y` | セクション内のアイテム | `1.1` = 関数キャッシュ、`1.2` = キャッシュ無効化 |

#### 番号の付け方

**隙間を空けて採番する。** 将来のアイテム挿入に備える。

```
1.0  [non-normative] キャッシュシステム概要
1.1  REQ001: 関数キャッシュ
1.3  REQ002: キャッシュ無効化         ← 1.2 を空けている
1.5  REQ003: キャッシュキー生成
2.0  [non-normative] ストレージシステム概要
2.1  REQ004: ローカルストレージ
2.3  REQ005: S3ストレージ
```

- 連番（`1.1`, `1.2`, `1.3`）ではなく、奇数（`1.1`, `1.3`, `1.5`）で採番すると挿入しやすい
- 深いネスト（`1.1.1`）は避ける。最大2階層まで

### `groups` の運用ガイドライン

`groups` はドキュメント横断の機能分類に使う。
同一機能に関連するアイテムは、ドキュメントタイプ（REQ/ARCH/SPEC 等）を越えて
同じグループ名を共有する。

#### 命名規則

| ルール | 説明 |
|---|---|
| **大文字英字** | `CACHE`, `DB`, `CLI`（小文字不可） |
| **3〜5文字** | 短く、一目で意味が分かる略語 |
| **層を越えて統一** | REQ001 が `CACHE` なら、対応する ARCH/SPEC/IMPL/TST も `CACHE` |

#### FR グループと NFR グループの使い分け

| 種別 | グループ名の例 | 説明 |
|---|---|---|
| **FR（機能要件）** | `CACHE`, `DB`, `SERIAL`, `KEY`, `CLI`, `MAINT`, `LIMIT` | 機能ドメインに基づく |
| **NFR（非機能要件）** | `PERF`, `SEC`, `REL`, `MNT`, `PRT` | ISO 25010 カテゴリに基づく |

#### 基本は1アイテム1グループ

```yaml
# 推奨: 1グループ
groups:
  - CACHE

# 例外的に許容: 横断的関心事
groups:
  - CACHE
  - PERF
```

複数グループは「この機能は性能にも影響する」のような横断的関心事の場合に限定する。
迷ったら1グループにして、NFR 側のグループ（`PERF` 等）は NFR アイテム自体に付ける。

### `derived: true`（派生要求）の使い方

上位要件からの直接的な分解ではなく、**技術的制約やアーキテクチャ選択から
論理的に導かれた要求**に使う。`links` が空でもトレーサビリティ検証をパスする。

#### 使用可能な層

- **設計層のみ**: ARCH / SPEC（standard）、HLD / LLD（full）
- **IMPL / TST での使用は禁止**

#### テンプレート

```yaml
active: true
derived: true
groups:
  - DB
header: |
  SQLite WALモード設定
level: 2.5
links: []
normative: true
reviewed: null
text: |
  ## 仕様

  SQLite データベースの接続時に WAL（Write-Ahead Logging）モードを有効にすること。

  ## 派生の根拠

  ARCH001 で採用した「非同期バックグラウンド保存」アーキテクチャにより、
  複数スレッドからの同時読み書きが発生する。SQLite のデフォルト（ジャーナルモード）
  では書き込み中に読み取りがブロックされるため、WAL モードが技術的に必須となる。

  この要件は上位要件からの直接的な分解ではなく、
  アーキテクチャ選択（SQLite + マルチスレッド）から論理的に導かれた制約である。
```

#### ポイント

| 項目 | ルール |
|---|---|
| `links` | 空リスト `[]`。親がないから `derived` を使う |
| `text` に「派生の根拠」セクション | **必須**。なぜこの制約が生じたか、どの設計判断に起因するか |
| 上位文書との関係 | `links` は空だが、`text` 内で関連する ARCH/HLD アイテムに言及する |

#### 使うべき場面と使うべきでない場面

| 場面 | `derived` を使うか | 理由 |
|---|---|---|
| アーキテクチャ選択から生じる技術制約 | **使う** | WALモード、接続プール設定等 |
| セキュリティポリシーから導かれる制約 | **使う** | TLS必須、暗号方式の制約等 |
| 上位要件の分解で得られた仕様 | **使わない** | 通常の `links` でトレースする |
| 優先度が低いので後回しにしたい | **使わない** | `priority: low` を使う |

### マルチリンクパターン

1つのアイテムが複数の親にリンクするケースがある。
リンクの方向は常に **子 → 親** である。

#### パターン1: 複数の要件を統合する設計

1つの設計アイテムが複数のREQを実現する場合。

```yaml
# ARCH003: ストレージ抽象化
links:
- REQ003: <fingerprint>   # ローカルストレージ要件
- REQ019: <fingerprint>   # S3ストレージ要件
```

両方の要件を統合して1つのインターフェースで実現する設計判断があることを示す。

#### パターン2: 設計文書から NFR へのリンク

非機能制約の実現方針を明示する場合。

```yaml
# ARCH001: キャッシュエンジン構成
links:
- REQ001: <fingerprint>   # 機能要件（キャッシュ機能）
- NFR001: <fingerprint>   # 非機能要件（1ms以内の性能）
```

#### パターン3: ADR から複数アイテムへのリンク

ADR は独立ルート文書のため、関連する任意のアイテムにリンクできる。

```yaml
# ADR005: シリアライザの選定
links:
- ARCH005: <fingerprint>  # 影響を受ける設計
- REQ005: <fingerprint>   # 関連する要件
```

#### パターン4: 非機能テストから NFR へのリンク

```yaml
# TST: キャッシュヒット性能テスト
links:
- SPEC001: <fingerprint>  # 検証対象の仕様
- NFR001: <fingerprint>   # 対応する非機能要件
```

> **注意**: マルチリンクは意味のある関係がある場合のみ使う。
> 「関連しそう」という程度の弱い関係ではリンクしない。
> リンクが増えると変更影響分析の範囲が広がり、suspect 解消の負荷が増す。

---

## 詳細度の判断基準

全てのアイテムを最大詳細度で書く必要はない。以下の基準で判断する:

| 複雑度 | 詳細度 | 例 |
|---|---|---|
| **低** — 自明な要件 | 2-3行の簡潔な記述 | 「バージョン情報を表示するCLIコマンド」 |
| **中** — 標準的な機能 | 概要 + 受入基準/振る舞い | 「ストレージポリシーの閾値判定」 |
| **高** — 複雑なロジック | フルテンプレート | 「mark デコレータ」「キャッシュキー生成」 |

### 簡潔版の例（低複雑度）

```yaml
text: |
  `beautyspot version` コマンドで、パッケージのバージョン情報を表示する。

  - `__version__` から取得した値を `rich.Panel` で表示する
  - 引数なし、オプションなし
```

---

## Markdown 記法チートシート（text フィールド内）

```markdown
## 見出し（H2推奨、H1はheaderフィールドと重複するため避ける）

### サブ見出し

**太字** で重要な用語を強調

- 箇条書き
  - ネスト可能

1. 番号付きリスト
2. 手順の記述に適する

| 列1 | 列2 | 列3 |
|---|---|---|
| セル | セル | セル |

`インラインコード` でクラス名・関数名・パラメータ名を囲む

\```python
# コードブロック（言語指定付き）
def example():
    pass
\```

> 引用・補足情報
```

## YAML記述時の注意

- `text: |` のリテラルブロックを使う（`>` は改行が消えるため非推奨）
- インデント（2スペース）を揃えること
- コードブロック内のインデントもYAMLのインデントに加算される
- `---` (YAMLセパレータ) と Markdown の水平線が衝突するため、
  text 内では `---` の代わりに見出し (`##`) で区切る

---

## 図表・画像・数式の挿入

図表（Mermaid、PlantUML）、数式（MathJax）、および画像の挿入方法やレンダリング環境（カスタムHTMLテンプレートの設定など）については、別紙 [図表・画像・数式ガイド](./diagram_and_image_guide.md) を参照してください。
