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

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

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

### 対象アイテム

8

### REQ

1

### ARCH

1

### SPEC

2

### TST

2

### IMPL

2

### レビュー済

8/8

### Suspect

0

### グループ

DB

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

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

グループ | REQ | ARCH | SPEC | TST | IMPL
--- | --- | --- | --- | --- | ---
DB | REQ003 ✓ キャッシュのメタデータ（関数名、入力ID、バージョン、有効期限等）をデータベースに永続化できること。 抽象インターフェースにより、DBやストレージなどのバック... | ARCH003 ✓ ## コンポーネント構成 ```mermaid classDiagram class TaskDBCore { <<Protocol>> ... | SPEC007 ✓ ## インターフェース ```python class SQLiteTaskDB(TaskDBBase): def __init__(self, db... | TST007 ✓ ## 目的 `SQLiteTaskDB` はキャッシュメタデータの永続化を担う中核コンポーネントであり、DB の不整合はキャッシュの消失や重複実行を引き起こす... | IMPL007 ✓ ## 実装概要 `SQLiteTaskDB` クラスが中核。Writer Thread + Reader Threads パターンで実装。 単一の `_wri...
DB | REQ003 ✓ キャッシュのメタデータ（関数名、入力ID、バージョン、有効期限等）をデータベースに永続化できること。 抽象インターフェースにより、DBやストレージなどのバック... | ARCH003 ✓ ## コンポーネント構成 ```mermaid classDiagram class TaskDBCore { <<Protocol>> ... | SPEC008 ✓ ## インターフェース ```python @runtime_checkable class TaskDBCore(Protocol): def ge... | TST008 ✓ ## 目的 `TaskDBCore` / `TaskDBBase` のプロトコル階層は、サードパーティによるカスタムDB実装（Redis、PostgreSQL... | IMPL008 ✓ ## 実装概要 DB 層のプロトコル階層を定義。`TaskDBCore`（ランタイム必須の CRUD）、 `Flushable`（書き込み同期）、`Shutd...

## カバレッジ（局所）

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

## アイテム詳細

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

キャッシュのメタデータ（関数名、入力ID、バージョン、有効期限等）をデータベースに永続化できること。

抽象インターフェースにより、DBやストレージなどのバックエンド実装を差し替え可能であること。

キャッシュのメタデータ（関数名、入力ID、バージョン、有効期限等）をデータベースに永続化できること。 抽象インターフェースにより、DBやストレージなどのバックエンド実装を差し替え可能であること。

親: —

子: ARCH003

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

## コンポーネント構成

    classDiagram
      class TaskDBCore {
        <<Protocol>>
        +init_schema() void
        +get(cache_key) TaskRecord
        +save(cache_key, ...) void
        +delete(cache_key) bool
      }
      class Maintenable {
        <<Protocol>>
        +delete_expired() int
        +prune(older_than) int
        +delete_all() int
      }
      class TaskDBMaintenable {
        <<Protocol>>
      }
      TaskDBCore <|-- TaskDBMaintenable
      Maintenable <|-- TaskDBMaintenable

      class SQLiteTaskDB {
        -db_path: Path
        -_write_queue: Queue
        +init_schema() void
        +get(cache_key) TaskRecord
        +save(cache_key, ...) void
      }
      TaskDBMaintenable <|-- SQLiteTaskDB

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
`TaskDBCore` | キャッシュ実行時に必要な最小限のメタデータDBアクセス | `init_schema()`, `get()`, `save()`, `delete()`
`Maintenable` | GCやCLI等、運用・保守に必要な拡張操作 | `delete_expired()`, `prune()`, `delete_all()`
`TaskDBMaintenable` | 実行用と保守用の両方を備えた上位Protocol | `TaskDBCore` + `Maintenable`
`SQLiteTaskDB` | SQLiteを用いたデフォルト実装。非同期書き込みキューを内包 | `TaskDBMaintenable` 実装

## データフロー

    sequenceDiagram
      participant Spot as core.Spot
      participant DB as SQLiteTaskDB
      participant Q as WriteQueue
      participant Thread as WriterThread
      participant SQLite as SQLiteDB

      Spot->>DB: get(cache_key)
      DB->>SQLite: SELECT
      SQLite-->>DB: TaskRecord
      DB-->>Spot: TaskRecord

      Spot->>DB: save(metadata)
      DB->>Q: put(WriteTask)
      DB-->>Spot: void (Non-blocking)

      Thread->>Q: get()
      Thread->>SQLite: INSERT / UPDATE

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
デフォルトメタデータDB | SQLite | ゼロ設定、組み込み可能、ローカルキャッシュとして十分な性能
バックグラウンド書き込み | キュー + 専用スレッド | SQLiteの排他制御によるメインスレッドのブロックを防ぐため
抽象化 | `Protocol`ベースのDI | `TaskDBCore`を満たせばPostgreSQL等に容易に差し替え可能

## 非機能要件方針

- 性能: 読み込みはメインスレッドで高速に処理（WALモード）、書き込みは専用スレッドによる非同期化。
- スレッド安全性: SQLiteへの書き込みは1つのスレッドに直列化し、データベースロックを回避する。
- 拡張性: すべての操作はProtocol (`TaskDBCore`, `Maintenable`) で定義され、利用側（`core.Spot`や`_CacheManager`）は実装に依存しない。

## コンポーネント構成 ```mermaid classDiagram class TaskDBCore { <<Protocol>> +init_schema() void +get(cache_key) TaskRecord +save(cache_key, ...) void +delete(cache_key) bool } class Maintenable { <<Protocol>> +delete_expired() int +prune(older_than) int +delete_all() int } class TaskDBMaintenable { <<Protocol>> } TaskDBCore <|-- TaskDBMaintenable Maintenable <|-- TaskDBMaintenable class SQLiteTaskDB { -db_path: Path -_write_queue: Queue +init_schema() void +get(cache_key) TaskRecord +save(cache_key, ...) void } TaskDBMaintenable <|-- SQLiteTaskDB ``` ## コンポーネント責務 | コンポーネント | 責務 | インターフェース | |---|---|---| | `TaskDBCore` | キャッシュ実行時に必要な最小限のメタデータDBアクセス | `init_schema()`, `get()`, `save()`, `delete()` | | `Maintenable` | GCやCLI等、運用・保守に必要な拡張操作 | `delete_expired()`, `prune()`, `delete_all()` | | `TaskDBMaintenable` | 実行用と保守用の両方を備えた上位Protocol | `TaskDBCore` + `Maintenable` | | `SQLiteTaskDB` | SQLiteを用いたデフォルト実装。非同期書き込みキューを内包 | `TaskDBMaintenable` 実装 | ## データフロー ```mermaid sequenceDiagram participant Spot as core.Spot participant DB as SQLiteTaskDB participant Q as WriteQueue participant Thread as WriterThread participant SQLite as SQLiteDB Spot->>DB: get(cache_key) DB->>SQLite: SELECT SQLite-->>DB: TaskRecord DB-->>Spot: TaskRecord Spot->>DB: save(metadata) DB->>Q: put(WriteTask) DB-->>Spot: void (Non-blocking) Thread->>Q: get() Thread->>SQLite: INSERT / UPDATE ``` ## 技術選定 | 技術領域 | 選定 | 理由 | |---|---|---| | デフォルトメタデータDB | SQLite | ゼロ設定、組み込み可能、ローカルキャッシュとして十分な性能 | | バックグラウンド書き込み | キュー + 専用スレッド | SQLiteの排他制御によるメインスレッドのブロックを防ぐため | | 抽象化 | `Protocol`ベースのDI | `TaskDBCore`を満たせばPostgreSQL等に容易に差し替え可能 | ## 非機能要件方針 - **性能**: 読み込みはメインスレッドで高速に処理（WALモード）、書き込みは専用スレッドによる非同期化。 - **スレッド安全性**: SQLiteへの書き込みは1つのスレッドに直列化し、データベースロックを回避する。 - **拡張性**: すべての操作はProtocol (`TaskDBCore`, `Maintenable`) で定義され、利用側（`core.Spot`や`_CacheManager`）は実装に依存しない。

親: REQ003

子: SPEC007, SPEC008

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

## インターフェース

    class SQLiteTaskDB(TaskDBBase):
        def __init__(self, db_path: str | Path, timeout: float = 30.0): ...
        def save(self, task: TaskRecord) -> None: ...
        def get(self, input_key: str) -> TaskRecord | None: ...

## 振る舞い

### ライタースレッド方式

- 書き込み操作は全て `_WriteTask` キューに投入され、専用のバックグラウンドスレッドで直列に実行される。これにより SQLite の `database is locked` エラーを回避する。

### 読み取り並行性

- 読み取りはスレッドローカルな接続を使用し、`PRAGMA query_only = ON` 状態で並行して実行される。

### スキーマ管理

- 起動時に `tasks` テーブルの存在を確認し、必要に応じて自動的に `ALTER TABLE` によるカラム追加（マイグレーション）を行う。

## パラメータ詳細

設定 | デフォルト | 説明
--- | --- | ---
`journal_mode` | `WAL` | 書き込みと読み取りの並行性を高める設定
`synchronous` | `NORMAL` | パフォーマンスと耐久性のバランス設定
`timeout` | `30.0` | データベースロック待機時間の閾値

## エラーハンドリング

- 接続失敗: `sqlite3.Error` をキャッチし、適切にラップして再送出する
- シャットダウン時: ライタースレッドへの新規タスク投入を停止し、保留中のタスクを完了させてから接続を閉じる

## エッジケース

- 破損DB: DBファイルが壊れている場合は、初期化時に検知しエラーとする
- インメモリDB: `:memory:` 指定時は、プロセスの生存期間中のみキャッシュが保持される

## インターフェース ```python class SQLiteTaskDB(TaskDBBase): def __init__(self, db_path: str | Path, timeout: float = 30.0): ... def save(self, task: TaskRecord) -> None: ... def get(self, input_key: str) -> TaskRecord | None: ... ``` ## 振る舞い ### ライタースレッド方式 - 書き込み操作は全て `_WriteTask` キューに投入され、専用のバックグラウンドスレッドで直列に実行される。これにより SQLite の `database is locked` エラーを回避する。 ### 読み取り並行性 - 読み取りはスレッドローカルな接続を使用し、`PRAGMA query_only = ON` 状態で並行して実行される。 ### スキーマ管理 - 起動時に `tasks` テーブルの存在を確認し、必要に応じて自動的に `ALTER TABLE` によるカラム追加（マイグレーション）を行う。 ## パラメータ詳細 | 設定 | デフォルト | 説明 | |---|---|---| | `journal_mode` | `WAL` | 書き込みと読み取りの並行性を高める設定 | | `synchronous` | `NORMAL` | パフォーマンスと耐久性のバランス設定 | | `timeout` | `30.0` | データベースロック待機時間の閾値 | ## エラーハンドリング - **接続失敗**: `sqlite3.Error` をキャッチし、適切にラップして再送出する - **シャットダウン時**: ライタースレッドへの新規タスク投入を停止し、保留中のタスクを完了させてから接続を閉じる ## エッジケース - **破損DB**: DBファイルが壊れている場合は、初期化時に検知しエラーとする - **インメモリDB**: `:memory:` 指定時は、プロセスの生存期間中のみキャッシュが保持される

親: ARCH003

子: IMPL007, TST007

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

## インターフェース

    @runtime_checkable
    class TaskDBCore(Protocol):
        def get(self, input_key: str) -> TaskRecord | None: ...
        def save(self, record: TaskRecord) -> None: ...

    class TaskDBBase(ABC):
        # TaskDBCore を満たしつつ、メンテナンスのデフォルト実装を提供

## 振る舞い

### 階層構造

1. TaskDBCore: キャッシュの実行に必要な最小限のメソッド定義
2. Maintenable: GCや統計取得に必要な管理用メソッドの定義
3. TaskDBBase: 共通のバリデーションや例外処理を実装する基底クラス

## パラメータ詳細 (主要メソッド)

メソッド | 役割
--- | ---
`get_blob_refs()` | 全レコードの `blob_key` を列挙する（GC用）
`delete_expired()` | `expires_at` を経過したレコードを一括削除する
`get_history()` | 実行履歴（統計）を取得する

## エラーハンドリング

- プロトコルに未実装のメソッドを呼び出した場合、実行時に `NotImplementedError` を送出する代わりに、`runtime_checkable` による事前チェックを推奨する。

## エッジケース

- 複合実装: 複数のプロトコルを一つのクラスで実装する場合、`TaskDBMaintenable` 構成プロトコルとして扱う。

## インターフェース ```python @runtime_checkable class TaskDBCore(Protocol): def get(self, input_key: str) -> TaskRecord | None: ... def save(self, record: TaskRecord) -> None: ... class TaskDBBase(ABC): # TaskDBCore を満たしつつ、メンテナンスのデフォルト実装を提供 ``` ## 振る舞い ### 階層構造 1. **TaskDBCore**: キャッシュの実行に必要な最小限のメソッド定義 2. **Maintenable**: GCや統計取得に必要な管理用メソッドの定義 3. **TaskDBBase**: 共通のバリデーションや例外処理を実装する基底クラス ## パラメータ詳細 (主要メソッド) | メソッド | 役割 | |---|---| | `get_blob_refs()` | 全レコードの `blob_key` を列挙する（GC用） | | `delete_expired()` | `expires_at` を経過したレコードを一括削除する | | `get_history()` | 実行履歴（統計）を取得する | ## エラーハンドリング - プロトコルに未実装のメソッドを呼び出した場合、実行時に `NotImplementedError` を送出する代わりに、`runtime_checkable` による事前チェックを推奨する。 ## エッジケース - **複合実装**: 複数のプロトコルを一つのクラスで実装する場合、`TaskDBMaintenable` 構成プロトコルとして扱う。

親: ARCH003

子: IMPL008, TST008

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

## 目的

`SQLiteTaskDB` はキャッシュメタデータの永続化を担う中核コンポーネントであり、DB の不整合はキャッシュの消失や重複実行を引き起こす。WALモード・専用ライタースレッドという独自アーキテクチャの信頼性を保証する。

## 検証観点

- スキーマ初期化: 新規DBファイルに対してテーブルとインデックスが正しく作成されること。既存DBへの自動マイグレーション（content_type, version, result_data, func_identifier, expires_at カラム追加）が冪等に動作すること
- CRUD操作: タスクの作成・取得・更新・削除が正しく動作し、存在しないキーへのアクセスが適切にハンドルされること
- 読み取り接続リカバリ: スレッドローカルな読み取り接続がクローズされた場合に自動的に再作成され、接続リークが発生しないこと
- ライタースレッドのタイムアウト: 書き込みキューのタイムアウト時に適切なエラーが返り、ライタースレッドがデッドロックしないこと
- GC耐性: ガベージコレクション実行後もDBファイルが存続し、有効なタスクが失われないこと

## 目的 `SQLiteTaskDB` はキャッシュメタデータの永続化を担う中核コンポーネントであり、DB の不整合はキャッシュの消失や重複実行を引き起こす。WALモード・専用ライタースレッドという独自アーキテクチャの信頼性を保証する。 ## 検証観点 - **スキーマ初期化**: 新規DBファイルに対してテーブルとインデックスが正しく作成されること。既存DBへの自動マイグレーション（content_type, version, result_data, func_identifier, expires_at カラム追加）が冪等に動作すること - **CRUD操作**: タスクの作成・取得・更新・削除が正しく動作し、存在しないキーへのアクセスが適切にハンドルされること - **読み取り接続リカバリ**: スレッドローカルな読み取り接続がクローズされた場合に自動的に再作成され、接続リークが発生しないこと - **ライタースレッドのタイムアウト**: 書き込みキューのタイムアウト時に適切なエラーが返り、ライタースレッドがデッドロックしないこと - **GC耐性**: ガベージコレクション実行後もDBファイルが存続し、有効なタスクが失われないこと

親: SPEC007

子: —

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

## 目的

`TaskDBCore` / `TaskDBBase` のプロトコル階層は、サードパーティによるカスタムDB実装（Redis、PostgreSQL 等）の差し替えを可能にする拡張ポイントである。インターフェース契約が不明確だと、カスタム実装が実行時に予期しないエラーを起こす。

## 検証観点

- インターフェース準拠: `TaskDBCore` の必須メソッド（save, load, delete 等）を実装したクラスが `isinstance` チェックをパスすること
- TaskDBBase 拡張: `TaskDBBase` がメンテナンス系メソッド（prune, stats 等）のデフォルト実装を提供し、サブクラスが必須メソッドのみの実装で動作すること
- デフォルト動作: メンテナンスメソッドのデフォルト実装が安全なno-op（空リスト返却等）であること
- カスタム実装の注入: `bs.Spot(db=CustomDB())` でプロトコル準拠のカスタムDBが正常に動作すること

## 目的 `TaskDBCore` / `TaskDBBase` のプロトコル階層は、サードパーティによるカスタムDB実装（Redis、PostgreSQL 等）の差し替えを可能にする拡張ポイントである。インターフェース契約が不明確だと、カスタム実装が実行時に予期しないエラーを起こす。 ## 検証観点 - **インターフェース準拠**: `TaskDBCore` の必須メソッド（save, load, delete 等）を実装したクラスが `isinstance` チェックをパスすること - **TaskDBBase 拡張**: `TaskDBBase` がメンテナンス系メソッド（prune, stats 等）のデフォルト実装を提供し、サブクラスが必須メソッドのみの実装で動作すること - **デフォルト動作**: メンテナンスメソッドのデフォルト実装が安全なno-op（空リスト返却等）であること - **カスタム実装の注入**: `bs.Spot(db=CustomDB())` でプロトコル準拠のカスタムDBが正常に動作すること

親: SPEC008

子: —

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

## 実装概要

`SQLiteTaskDB` クラスが中核。Writer Thread + Reader Threads パターンで実装。 単一の `_writer_thread`（デーモン）が `_write_queue` から書き込みタスクを取り出して 直列処理し、読み取りは `threading.local()` のスレッドローカル接続で並行実行する。 WAL モードにより読み取りと書き込みが並行可能。

## 設計判断

### 専用ライタースレッドの採用

SQLite は単一書き込み接続しかサポートしないため、全書き込みを1つのスレッドに 集約する Producer-Consumer パターンを採用。キュー経由でタスクを受け渡し、 ライタースレッドがコミット/ロールバックを管理する。 マルチスレッドからの並行書き込みによるロック競合を根本的に回避する。

### スレッドローカル読み取り接続

`threading.local()` に `_ReadConnWrapper` を格納し、スレッドごとに 読み取り専用接続（`PRAGMA query_only = ON`）を保持する。 接続エラー時は自動的に再作成し、リーク防止のため `WeakSet` で全接続を追跡する。

### _WriteTask 状態マシン

書き込みタスクを `PENDING → RUNNING → DONE`（または `CANCELLED`）の 状態マシンで管理。タイムアウト時の `try_cancel()` により、 キュー内で待機中のタスクを安全にキャンセルできる。 既に `RUNNING` 状態のタスクはキャンセルせず完了を待つ。

## 実装メモ

- スキーマの自動マイグレーションは `PRAGMA table_info` で既存カラムを確認し、 不足カラム（content_type, version, result_data, func_identifier, expires_at）を追加する
- Lazy Expiration: `get()` 時に `expires_at < NOW` を確認し、期限切れなら `None` を返す。 物理削除は `beautyspot gc` コマンドに委譲する
- `flush()` は空の no-op タスクをキューに投入し、その完了を待つことで 先行する全書き込みの完了を保証する
- `shutdown()` は全読み取り接続のクローズ → `_STOP` センチネルの投入 → ライタースレッドの join で安全に停止する
- `_read_connect()` のダブルチェックパターンで TOCTOU 競合を回避している

## 実装概要 `SQLiteTaskDB` クラスが中核。Writer Thread + Reader Threads パターンで実装。 単一の `_writer_thread`（デーモン）が `_write_queue` から書き込みタスクを取り出して 直列処理し、読み取りは `threading.local()` のスレッドローカル接続で並行実行する。 WAL モードにより読み取りと書き込みが並行可能。 ## 設計判断 ### 専用ライタースレッドの採用 SQLite は単一書き込み接続しかサポートしないため、全書き込みを1つのスレッドに 集約する Producer-Consumer パターンを採用。キュー経由でタスクを受け渡し、 ライタースレッドがコミット/ロールバックを管理する。 マルチスレッドからの並行書き込みによるロック競合を根本的に回避する。 ### スレッドローカル読み取り接続 `threading.local()` に `_ReadConnWrapper` を格納し、スレッドごとに 読み取り専用接続（`PRAGMA query_only = ON`）を保持する。 接続エラー時は自動的に再作成し、リーク防止のため `WeakSet` で全接続を追跡する。 ### _WriteTask 状態マシン 書き込みタスクを `PENDING → RUNNING → DONE`（または `CANCELLED`）の 状態マシンで管理。タイムアウト時の `try_cancel()` により、 キュー内で待機中のタスクを安全にキャンセルできる。 既に `RUNNING` 状態のタスクはキャンセルせず完了を待つ。 ## 実装メモ - スキーマの自動マイグレーションは `PRAGMA table_info` で既存カラムを確認し、 不足カラム（content_type, version, result_data, func_identifier, expires_at）を追加する - Lazy Expiration: `get()` 時に `expires_at < NOW` を確認し、期限切れなら `None` を返す。 物理削除は `beautyspot gc` コマンドに委譲する - `flush()` は空の no-op タスクをキューに投入し、その完了を待つことで 先行する全書き込みの完了を保証する - `shutdown()` は全読み取り接続のクローズ → `_STOP` センチネルの投入 → ライタースレッドの join で安全に停止する - `_read_connect()` のダブルチェックパターンで TOCTOU 競合を回避している

references: src/beautyspot/db.py

親: SPEC007

子: —

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

## 実装概要

DB 層のプロトコル階層を定義。`TaskDBCore`（ランタイム必須の CRUD）、 `Flushable`（書き込み同期）、`Shutdownable`（安全な停止）、 `Maintenable`（GC・統計等の管理操作）の4つのプロトコルと、 それらを統合した `TaskDBMaintenable` を提供する。 `TaskDBBase` は ABC として `Maintenable` のデフォルト実装（安全な no-op）を提供する。

## 設計判断

### プロトコルの細分化

単一の巨大インターフェースではなく、責務ごとにプロトコルを分割した。 これにより、カスタム DB 実装は `TaskDBCore` のみ実装すればランタイムで動作し、 メンテナンス機能は段階的にオプトインできる。

### デフォルト実装の安全性

`TaskDBBase` のメンテナンスメソッド（`delete_expired()`, `prune()` 等）は デフォルトで空リスト返却や no-op を行い、サブクラスが未実装でも安全に動作する。 CLI の `gc` コマンドが未対応の DB バックエンドでもエラーにならない設計。

## 実装メモ

- `Flushable.flush(timeout)` のデフォルトは no-op。SQLiteTaskDB のみがキュー同期を実装
- `Shutdownable.shutdown(wait)` のデフォルトは no-op。リソースを持たない軽量実装に配慮
- カスタム DB 実装は `isinstance(db, Maintenable)` で機能の有無を判定できる

## 実装概要 DB 層のプロトコル階層を定義。`TaskDBCore`（ランタイム必須の CRUD）、 `Flushable`（書き込み同期）、`Shutdownable`（安全な停止）、 `Maintenable`（GC・統計等の管理操作）の4つのプロトコルと、 それらを統合した `TaskDBMaintenable` を提供する。 `TaskDBBase` は ABC として `Maintenable` のデフォルト実装（安全な no-op）を提供する。 ## 設計判断 ### プロトコルの細分化 単一の巨大インターフェースではなく、責務ごとにプロトコルを分割した。 これにより、カスタム DB 実装は `TaskDBCore` のみ実装すればランタイムで動作し、 メンテナンス機能は段階的にオプトインできる。 ### デフォルト実装の安全性 `TaskDBBase` のメンテナンスメソッド（`delete_expired()`, `prune()` 等）は デフォルトで空リスト返却や no-op を行い、サブクラスが未実装でも安全に動作する。 CLI の `gc` コマンドが未対応の DB バックエンドでもエラーにならない設計。 ## 実装メモ - `Flushable.flush(timeout)` のデフォルトは no-op。SQLiteTaskDB のみがキュー同期を実装 - `Shutdownable.shutdown(wait)` のデフォルトは no-op。リソースを持たない軽量実装に配慮 - カスタム DB 実装は `isinstance(db, Maintenable)` で機能の有無を判定できる

references: src/beautyspot/db.py

親: SPEC008

子: —