局所トレーサビリティ — グループ: LIFE

# 局所トレーサビリティビュー グループ: LIFE

生成日時: 2026-03-09 23:19:54

### 対象アイテム

8

### REQ

1

### ARCH

1

### SPEC

2

### TST

2

### IMPL

2

### レビュー済

8/8

### Suspect

0

### グループ

LIFE

## トレーサビリティマトリクス

✓=レビュー済 ○=未レビュー ⚠=Suspect（複数同時表示あり。IDクリックで詳細へ）

グループ | REQ | ARCH | SPEC | TST | IMPL
--- | --- | --- | --- | --- | ---
LIFE | REQ007 ✓ キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。 | ARCH007 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]... | SPEC016 ✓ ## インターフェース ```python class LifecyclePolicy: def add_rule(self, pattern: st... | TST016 ✓ ## 目的 `LifecyclePolicy` はキャッシュデータの保持期間を関数名パターンで制御する。パターンマッチングの不備は、重要なキャッシュの意図しな... | IMPL016 ✓ ## 実装概要 `LifecyclePolicy` クラスは `Rule` オブジェクトのリストを保持し、 関数名に基づいてキャッシュの保持期間を決定する。 ...
LIFE | REQ007 ✓ キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。 | ARCH007 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]... | SPEC017 ✓ ## インターフェース ```python def parse_retention(val: Any) -> float | None: ... ``` #... | TST017 ✓ ## 目的 `Retention` はキャッシュの有効期間をユーザーフレンドリーな形式で指定する値オブジェクトである。パースの不備は意図しない保持期間（例: ... | IMPL017 ✓ ## 実装概要 `Retention` クラスを名前空間として使用し、保持期間のパースや 特殊な定数（`INDEFINITE`, `FOREVER`）を管理す...

## カバレッジ（局所）

リンク方向 | カバー数 | カバー率 | 未カバー
--- | --- | --- | ---
ARCH → REQ | 1 / 1 | 100.0% | —
SPEC → ARCH | 1 / 1 | 100.0% | —
TST → SPEC | 2 / 2 | 100.0% | —
IMPL → SPEC | 2 / 2 | 100.0% | —

## アイテム詳細

### REQ007 REQ {h(g)} ✓ レビュー済

キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。

キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。

親: —

子: ARCH007

### ARCH007 ARCH {h(g)} ✓ レビュー済

## コンポーネント構成

    graph TD
      A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]
      B -->|ルールリスト| C[Rule]
      C -->|パターン照合| D[fnmatch]
      B -->|パース| E[Retention]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
LifecyclePolicy | 複数のルールを管理し、関数名に最適な有効期限を決定する | `resolve_with_fallback()`
Rule | 1つのパターンとそれに対応する保持期間のペア | `match(func_name)`
Retention | 多様な時間表現（文字列/数値）を秒数に正規化する | `parse_retention()`

## データフロー

    sequenceDiagram
      participant Spot as core.Spot
      participant Policy as LifecyclePolicy
      participant DB as TaskDB

      Spot->>Policy: resolve_with_fallback("module.func")
      Policy->>Policy: ルール順次照合 (fnmatch)
      Policy-->>Spot: expires_at (timestamp)
      Spot->>DB: save(..., expires_at)
      Note over DB: get() 時に現在時刻と比較判定

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
パターンマッチ | fnmatch (Glob) | 正規表現よりも直感的で、開発者にとって馴染みのある指定方法
保持期間指定 | 文字列パーサー ("30d"等) | 設定ファイルやデコレータでの可読性を向上
判定タイミング | 遅延判定 (Lazy) | 書き込み・読み込み時のオーバーヘッドを最小化

## 非機能要件方針

- 優先順位: 完全修飾名（モジュール名込み）が短縮名より優先される二段構えのマッチング
- 堅牢性: ポリシーにマッチしない場合は「無期限」とするフェイルセーフな設計
- 拡張性: `Retention.FOREVER` 等の定数により、グローバルポリシーの個別上書きに対応

## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|マッチング要求| B[LifecyclePolicy] B -->|ルールリスト| C[Rule] C -->|パターン照合| D[fnmatch] B -->|パース| E[Retention] ``` ## コンポーネント責務 | コンポーネント | 責務 | インターフェース | |---|---|---| | LifecyclePolicy | 複数のルールを管理し、関数名に最適な有効期限を決定する | `resolve_with_fallback()` | | Rule | 1つのパターンとそれに対応する保持期間のペア | `match(func_name)` | | Retention | 多様な時間表現（文字列/数値）を秒数に正規化する | `parse_retention()` | ## データフロー ```mermaid sequenceDiagram participant Spot as core.Spot participant Policy as LifecyclePolicy participant DB as TaskDB Spot->>Policy: resolve_with_fallback("module.func") Policy->>Policy: ルール順次照合 (fnmatch) Policy-->>Spot: expires_at (timestamp) Spot->>DB: save(..., expires_at) Note over DB: get() 時に現在時刻と比較判定 ``` ## 技術選定 | 技術領域 | 選定 | 理由 | |---|---|---| | パターンマッチ | fnmatch (Glob) | 正規表現よりも直感的で、開発者にとって馴染みのある指定方法 | | 保持期間指定 | 文字列パーサー ("30d"等) | 設定ファイルやデコレータでの可読性を向上 | | 判定タイミング | 遅延判定 (Lazy) | 書き込み・読み込み時のオーバーヘッドを最小化 | ## 非機能要件方針 - **優先順位**: 完全修飾名（モジュール名込み）が短縮名より優先される二段構えのマッチング - **堅牢性**: ポリシーにマッチしない場合は「無期限」とするフェイルセーフな設計 - **拡張性**: `Retention.FOREVER` 等の定数により、グローバルポリシーの個別上書きに対応

親: REQ007

子: SPEC016, SPEC017

### SPEC016 SPEC {h(g)} ✓ レビュー済

## インターフェース

    class LifecyclePolicy:
        def add_rule(self, pattern: str, retention: Any) -> None: ...
        def resolve_with_fallback(self, func_name: str) -> float | None: ...

## 振る舞い

1. 順次照合: 登録された順にルールを走査する

2. パターンマッチ: `fnmatch.fnmatch` を使用し、関数名がパターンに合致するか判定

3. 二段階解決:

   1. 最初に「完全修飾名 (module.qualname)」で照合

   2. マッチしなければ「短縮名 (qualname)」で照合

4. 結果返却: 最初にマッチしたルールの保持期間を秒数に変換して返す

## パラメータ詳細

パラメータ例説明`pattern"my_mod.*"`, `"*_test"`Glob形式のパターン`retention"30d"`, `3600`保持期間の定義

## デフォルト保持期間

- `default_retention` パラメータで、どのルールにもマッチしない場合のデフォルト保持期間を指定できる
- `LifecyclePolicy.default()` のデフォルト値は `"30d"`（30日）
- `None` を指定すると無期限保持（従来互換）

## エラーハンドリング

- マッチなし: 全てのルールにマッチしない場合は、`default_retention` で指定された保持期間を返す（デフォルト: 30日）

## エッジケース

- 重複パターン: 先に登録されたルールが優先される。

## インターフェース ```python class LifecyclePolicy: def add_rule(self, pattern: str, retention: Any) -> None: ... def resolve_with_fallback(self, func_name: str) -> float | None: ... ``` ## 振る舞い 1. **順次照合**: 登録された順にルールを走査する 2. **パターンマッチ**: `fnmatch.fnmatch` を使用し、関数名がパターンに合致するか判定 3. **二段階解決**: 1. 最初に「完全修飾名 (module.qualname)」で照合 2. マッチしなければ「短縮名 (qualname)」で照合 4. **結果返却**: 最初にマッチしたルールの保持期間を秒数に変換して返す ## パラメータ詳細 パラメータ例説明`pattern"my_mod.*"`, `"*_test"`Glob形式のパターン`retention"30d"`, `3600`保持期間の定義 ## デフォルト保持期間 - `default_retention` パラメータで、どのルールにもマッチしない場合のデフォルト保持期間を指定できる - `LifecyclePolicy.default()` のデフォルト値は `"30d"`（30日） - `None` を指定すると無期限保持（従来互換） ## エラーハンドリング - **マッチなし**: 全てのルールにマッチしない場合は、`default_retention` で指定された保持期間を返す（デフォルト: 30日） ## エッジケース - **重複パターン**: 先に登録されたルールが優先される。

親: ARCH007

子: IMPL016, TST016

### SPEC017 SPEC {h(g)} ✓ レビュー済

## インターフェース

    def parse_retention(val: Any) -> float | None: ...

## 振る舞い

### 文字列パース

- `"7d"` (7日), `"12h"` (12時間), `"30m"` (30分), `"10s"` (10秒) 形式を秒数に変換する。大文字小文字は区別しない。

### 数値・オブジェクト

- `int`, `float`: 秒数としてそのまま扱う。
- `timedelta`: `total_seconds()` を取得。
- `None`: ポリシーに従う（または無期限）を意味する。

## パラメータ詳細 (単位)

単位 | 倍率 (秒) | 備考
--- | --- | ---
`d` | 86400 | 日数
`h` | 3600 | 時間
`m` | 60 | 分
`s` | 1 | 秒

## エラーハンドリング

- 不正な形式: パーサが理解できない形式の場合は `ValidationError` を送出する。
- 負の値: 過去の時間は指定できず、`ValidationError` となる。

## エッジケース

- FOREVER: 特別な定数 `Retention.FOREVER` (無限大の秒数) を提供し、ポリシーに関わらず永続化を指示できる。

## インターフェース ```python def parse_retention(val: Any) -> float | None: ... ``` ## 振る舞い ### 文字列パース - `"7d"` (7日), `"12h"` (12時間), `"30m"` (30分), `"10s"` (10秒) 形式を秒数に変換する。大文字小文字は区別しない。 ### 数値・オブジェクト - `int`, `float`: 秒数としてそのまま扱う。 - `timedelta`: `total_seconds()` を取得。 - `None`: ポリシーに従う（または無期限）を意味する。 ## パラメータ詳細 (単位) | 単位 | 倍率 (秒) | 備考 | |---|---|---| | `d` | 86400 | 日数 | | `h` | 3600 | 時間 | | `m` | 60 | 分 | | `s` | 1 | 秒 | ## エラーハンドリング - **不正な形式**: パーサが理解できない形式の場合は `ValidationError` を送出する。 - **負の値**: 過去の時間は指定できず、`ValidationError` となる。 ## エッジケース - **FOREVER**: 特別な定数 `Retention.FOREVER` (無限大の秒数) を提供し、ポリシーに関わらず永続化を指示できる。

親: ARCH007

子: IMPL017, TST017

### TST016 TST {h(g)} ✓ レビュー済

## 目的

`LifecyclePolicy` はキャッシュデータの保持期間を関数名パターンで制御する。パターンマッチングの不備は、重要なキャッシュの意図しない削除（データロス）や不要なキャッシュの蓄積（ディスク圧迫）を引き起こす。

## 検証観点

- パターンマッチング: glob パターン（`"train_*"`, `"*.preprocess"` 等）がルールの `func_pattern` として正しく機能し、マッチした関数に対して指定の `Retention` が適用されること
- ルール優先順位: 複数ルールがマッチした場合に、リスト順（先勝ち or 後勝ち）でルールが決定されること
- resolve_with_fallback: 完全修飾名（`module.qualname`）でルールが見つからない場合に、短縮名（`qualname` のみ）でフォールバック検索が行われること
- デフォルト保持期間: どのルールにもマッチしない関数に対して `default_retention` で指定された保持期間（デフォルト: 30日）が適用されること
- 無期限保持の互換性: `default_retention=None` を指定した場合に無期限保持（`None`）が返されること

## 目的 `LifecyclePolicy` はキャッシュデータの保持期間を関数名パターンで制御する。パターンマッチングの不備は、重要なキャッシュの意図しない削除（データロス）や不要なキャッシュの蓄積（ディスク圧迫）を引き起こす。 ## 検証観点 - **パターンマッチング**: glob パターン（`"train_*"`, `"*.preprocess"` 等）がルールの `func_pattern` として正しく機能し、マッチした関数に対して指定の `Retention` が適用されること - **ルール優先順位**: 複数ルールがマッチした場合に、リスト順（先勝ち or 後勝ち）でルールが決定されること - **resolve_with_fallback**: 完全修飾名（`module.qualname`）でルールが見つからない場合に、短縮名（`qualname` のみ）でフォールバック検索が行われること - **デフォルト保持期間**: どのルールにもマッチしない関数に対して `default_retention` で指定された保持期間（デフォルト: 30日）が適用されること - **無期限保持の互換性**: `default_retention=None` を指定した場合に無期限保持（`None`）が返されること

親: SPEC016

子: —

### TST017 TST {h(g)} ✓ レビュー済

## 目的

`Retention` はキャッシュの有効期間をユーザーフレンドリーな形式で指定する値オブジェクトである。パースの不備は意図しない保持期間（例: "7d" を 7秒と誤解釈）を招き、重要なキャッシュの早期削除やディスクの際限ない肥大化に繋がる。

## 検証観点

- 文字列パース: `"7d"` → 7日、`"12h"` → 12時間、`"30m"` → 30分、`"10s"` → 10秒として正しく解釈されること
- timedelta 入力: `timedelta(days=7)` がそのまま保持期間として受け入れられること
- 秒数入力: 整数・浮動小数点数が秒として解釈されること
- FOREVER シングルトン: `Retention.FOREVER` が無期限保持を表し、シングルトンであること（`is` 比較が成立）
- バリデーション: 負値（`"-1d"`）やゼロ（`"0s"`）が `ValidationError` を送出すること。不正な形式（`"abc"`, `""` 等）も拒否されること

## 目的 `Retention` はキャッシュの有効期間をユーザーフレンドリーな形式で指定する値オブジェクトである。パースの不備は意図しない保持期間（例: "7d" を 7秒と誤解釈）を招き、重要なキャッシュの早期削除やディスクの際限ない肥大化に繋がる。 ## 検証観点 - **文字列パース**: `"7d"` → 7日、`"12h"` → 12時間、`"30m"` → 30分、`"10s"` → 10秒として正しく解釈されること - **timedelta 入力**: `timedelta(days=7)` がそのまま保持期間として受け入れられること - **秒数入力**: 整数・浮動小数点数が秒として解釈されること - **FOREVER シングルトン**: `Retention.FOREVER` が無期限保持を表し、シングルトンであること（`is` 比較が成立） - **バリデーション**: 負値（`"-1d"`）やゼロ（`"0s"`）が `ValidationError` を送出すること。不正な形式（`"abc"`, `""` 等）も拒否されること

親: SPEC017

子: —

### IMPL016 IMPL {h(g)} ✓ レビュー済

## 実装概要

`LifecyclePolicy` クラスは `Rule` オブジェクトのリストを保持し、 関数名に基づいてキャッシュの保持期間を決定する。 `resolve()` メソッドが `fnmatch.fnmatch()` を使って関数名をパターンと照合し、 最初にマッチしたルールの保持期間を返す。

## 設計判断

### First-Match セマンティクス

ルールのリストは順序が意味を持ち、最初にマッチしたものが採用される。 これにより、特定プレフィックスの関数には短い保持期間を設定し、 最後に `*`（ワイルドカード）でデフォルトポリシーを設定するような フォールバック構造が簡単に記述できる。

### resolve_with_fallback の導入

後方互換性と柔軟性のため、まず `func_identifier` (モジュール名付きの完全修飾名) で マッチングを試み、マッチしなかった場合は `func_name` (短い関数名) で再度マッチングを 試みる `resolve_with_fallback()` を提供している。

### default_retention の導入

`LifecyclePolicy` コンストラクタに `default_retention` パラメータを追加。 どのルールにもマッチしない場合に返す保持期間を指定できる。 `LifecyclePolicy.default()` は `default_retention="30d"` で生成し、 デフォルトで30日の保持期間を設定する。

## 実装メモ

- `LifecyclePolicy.default()` は空のルールリスト + `default_retention="30d"` を持つ
- `default_retention=None` を指定すれば従来通り無期限保持となる

## 実装概要 `LifecyclePolicy` クラスは `Rule` オブジェクトのリストを保持し、 関数名に基づいてキャッシュの保持期間を決定する。 `resolve()` メソッドが `fnmatch.fnmatch()` を使って関数名をパターンと照合し、 最初にマッチしたルールの保持期間を返す。 ## 設計判断 ### First-Match セマンティクス ルールのリストは順序が意味を持ち、最初にマッチしたものが採用される。 これにより、特定プレフィックスの関数には短い保持期間を設定し、 最後に `*`（ワイルドカード）でデフォルトポリシーを設定するような フォールバック構造が簡単に記述できる。 ### resolve_with_fallback の導入 後方互換性と柔軟性のため、まず `func_identifier` (モジュール名付きの完全修飾名) で マッチングを試み、マッチしなかった場合は `func_name` (短い関数名) で再度マッチングを 試みる `resolve_with_fallback()` を提供している。 ### default_retention の導入 `LifecyclePolicy` コンストラクタに `default_retention` パラメータを追加。 どのルールにもマッチしない場合に返す保持期間を指定できる。 `LifecyclePolicy.default()` は `default_retention="30d"` で生成し、 デフォルトで30日の保持期間を設定する。 ## 実装メモ - `LifecyclePolicy.default()` は空のルールリスト + `default_retention="30d"` を持つ - `default_retention=None` を指定すれば従来通り無期限保持となる

references: src/beautyspot/lifecycle.py

親: SPEC016

子: —

### IMPL017 IMPL {h(g)} ✓ レビュー済

## 実装概要

`Retention` クラスを名前空間として使用し、保持期間のパースや 特殊な定数（`INDEFINITE`, `FOREVER`）を管理する。 `parse_retention()` 関数は文字列（"7d", "12h"等）、`timedelta`、 または秒数（`int`/`float`）を受け取り、標準化された `timedelta` オブジェクトに変換する。

## 設計判断

### _ForeverSentinel シングルトン

`Retention.FOREVER` はポリシーを強制的にバイパスするための特殊値。 PEP 703 (free-threading) 環境での安全性を考慮し、 `_ForeverSentinel` は `threading.Lock` を用いたダブルチェックロッキングで 厳密なシングルトンとして実装されている。

### 直感的な文字列表現のサポート

`"7d"`, `"12h"`, `"30m"`, `"10s"` のような文字列フォーマットを 正規表現 `_TIME_PATTERN` でパースすることで、設定ファイルや ハードコード時の可読性を高めている。

## 実装メモ

- `parse_retention()` は無効なフォーマットや負の値に対して `ValidationError` を送出する
- `_ForeverSentinel` は `__bool__()` で `True` を返すようオーバーライドされている

## 実装概要 `Retention` クラスを名前空間として使用し、保持期間のパースや 特殊な定数（`INDEFINITE`, `FOREVER`）を管理する。 `parse_retention()` 関数は文字列（"7d", "12h"等）、`timedelta`、 または秒数（`int`/`float`）を受け取り、標準化された `timedelta` オブジェクトに変換する。 ## 設計判断 ### _ForeverSentinel シングルトン `Retention.FOREVER` はポリシーを強制的にバイパスするための特殊値。 PEP 703 (free-threading) 環境での安全性を考慮し、 `_ForeverSentinel` は `threading.Lock` を用いたダブルチェックロッキングで 厳密なシングルトンとして実装されている。 ### 直感的な文字列表現のサポート `"7d"`, `"12h"`, `"30m"`, `"10s"` のような文字列フォーマットを 正規表現 `_TIME_PATTERN` でパースすることで、設定ファイルや ハードコード時の可読性を高めている。 ## 実装メモ - `parse_retention()` は無効なフォーマットや負の値に対して `ValidationError` を送出する - `_ForeverSentinel` は `__bool__()` で `True` を返すようオーバーライドされている

references: src/beautyspot/lifecycle.py

親: SPEC017

子: —