型付き辺: 出 2 / 入 0
ADR-0223: Retention Manager の運用実装を具体化
1. Executive Summary
- 文脈: 帳票・証憑管理 SaaS の初期検証フェーズで受領した証憑は電子帳簿保存法の施行規則第4条第1項第3号 + 法人税法 保存年限 7 年/10 年に基づき Retention Lock 付きで長期保存する。ADR-0208 で運用実装のフレームワーク (Cloud Run による日次 sweep · Postgres SoT · GCS の retainUntil ビットが一致しているか) は確定したが、実際に動く module (sweep 実装 · 起動 · 再開 · error 処理 · alert) が未確定で、本 ADR がこれを埋める。
- 課題: Phase 0 の dogfood 開始日から日次で計測する重要な指標の観測値が採取できず、Phase 1 GA 判断 (当該指標継続で 3 ヶ月連続 100% 達成) の実測分母が最悪 3 ヶ月遅延する。
- 対策: Retention Manager を 5 つのモジュール (役割はそれぞれ sweep 実装 / 起動側の不可分実行 / 中断再開 / error 処理 / alert) に分解し Cloud Run Job + Cloud Scheduler + Cloud Tasks の per-tenant queue で日次 sweep する。
- 効果: Phase 0 の dogfood 開始日から日次で計測する重要な指標の観測値が採取でき、Phase 1 GA 前に 3 ヶ月連続 100% 達成の実測 (ADR-0208 KPI 5) を確定できる。
- 代償: Cloud Run + Cloud Tasks で月額 $10/月未満 (Phase 1 GA 50 顧客時点)、Cloud Tasks queue 依存が 1 個増える。
2. 何を解決するか
2.1 直面していること
適合性:
- 最初の証憑保存 PR (PR-4 相当) の merge 前に Retention Manager runtime を deploy する経路がなく、PR merge 時点で日次 sweep が動かない状態になる。
- 実装 module 未確定の間は sweep-audit-atomicity.test.ts (integration test) の PASS だけが担保で、実 GCP 環境での動作は billing 有効化 PR 以降まで検証できない。
2.2 放置すると起こること
信頼性:
- 最初の証憑保存 PR merge 時に Retention Manager が deploy されていないと、日次 KPI 9 (孤立 audit_log 行数 = 0) の観測値採取が翌日以降にずれ、Phase 1 GA 前 3 ヶ月連続の実測分母が最悪 3 ヶ月遅延する。
- Retention Manager の error 分類 (transient GCS API error / permanent auth error / DB deadlock / etc.) が未定義のまま実装するとリトライ戦略が場当たり的になり、dead-letter queue に落ちない silent failure が発生する。
2.3 潜在リスク (実害未発生 · 発生確率不明)
保守性:
- Cloud Run Job task-timeout 6h + parallelism 4 の値が Phase 0 dogfood (tenant 1 · 想定 receipts 100-1000 件) 向けで、Phase 1 GA (50 顧客 · 想定 receipts 50-500K 件) では過小になり、複数 task 分割 + Cloud Tasks 経由の並列 fan-out にリファクタが必要になるリスク (行き先: §8 撤退条件 1)。
3. 採用したい方針
3.1 対策方針
Retention Manager を役割の異なる 5 module に分解し、Cloud Run Job を entrypoint に Cloud Scheduler + Cloud Tasks の per-tenant queue で日次 sweep する。1 receipt の削除は Postgres transaction + GCS delete を不可分に扱い、GCS delete 失敗時は Postgres を rollback して整合性を保つ。task 中断からの再開は Postgres の checkpoint 表で per-tenant に管理し、error は分類 tree で transient/permanent/skip/unknown に振り分けて unknown と permanent を dead-letter queue + alert に集約する。sweep-loop-runner の pre-filter で retention_expires_at > now() の receipt を除外し、GCS Object Retention Lock 未到達の証憑を sweep 対象から外して alert 飽和を防ぐ。legal_hold 解除イベントは即時 sweep タスクを enqueue して監査証跡の gap を最小化する。
- 適合性: ADR-0208 §8 撤退条件 6 の retention_expires_at と GCS retainUntil の bit 一致検証を sweep 前 check として実装する。
- 信頼性: lock-transaction-boundary で Postgres commit と GCS delete の順序を固定し、GCS delete 失敗時に Postgres 側を rollback して silent partial delete を防ぐ。
- 運用性: dead-letter queue に message が積まれたら Cloud Monitoring alert 発火 + 代表取締役 ([email protected]) へ email 通知し、backup 通知チャンネル (別メールアドレスまたは Slack webhook) にも同時配送して sweep 失敗の見落としを 0 件にする。
3.2 守るべき設計制約
- 適合性: ADR-0208 の framework (Cloud Run daily sweep · Postgres SoT · GCS retainUntil bit 一致 · Terraform lifecycle 3 規則) を上書きせず具体化する範囲に留める。
- 適合性: ADR-0218 の legal_holds 表に active hold が存在する receipt は削除せず deletion_events に理由 = 'legal_hold_active' で INSERT する。
- 信頼性: 1 receipt の削除は Postgres transaction 開始 → legal_hold gate → GCS delete → deletion_events + audit_log INSERT → receipts DELETE → Postgres commit の順序を固定する。
- 運用性: tenant 単位の並列度 gate で他 tenant への影響を分離する (Cloud Tasks per-tenant queue)。
3.3 仕様
- 適合性: 5 module = sweep-loop-runner (Cloud Run Job entrypoint · per-tenant loop · pre-filter で retention_expires_at > now() の receipt を sweep 対象から除外) / lock-transaction-boundary (1 receipt 削除の不可分実行) / checkpoint-resumer (task 再起動時に sweep_checkpoint 表から続きから再開) / error-classifier (transient/permanent/skip/unknown 振り分け) / dead-letter-alerter (dead-letter queue → Cloud Monitoring alert + email + backup チャンネル)。
- 運用性: Cloud Run Job の task-timeout 6h · parallelism 4 · Cloud Scheduler は日次 UTC 15:00 (JST 00:00) cron 起動 · payload = { tenantIds: null (all tenants) or [specific tenant] }。
- 運用性: Cloud Tasks per-tenant queue で max_dispatches_per_second = 1 · max_concurrent_dispatches = 4 を設定して並列度 gate を規定する。
- 運用性: Cloud Monitoring alert policy の通知チャンネルに primary = 代表取締役 email + backup = 別メールアドレスまたは Slack webhook を設定し、単一通知先の受信障害・スパム誤判定・担当者変更で通知が無音になるのを防ぐ。alert 到達確認は §9 Confirmation KPI-7 の月次テストで検証する。
- 保守性: sweep_checkpoint 表を新設 (tenant_id UUID PRIMARY KEY · last_processed_receipt_id UUID NOT NULL · last_processed_at TIMESTAMPTZ NOT NULL DEFAULT now() · status TEXT NOT NULL CHECK (status IN ('in_progress', 'completed', 'failed')))。
- 保守性: sweep-loop-runner は per-tenant loop 開始時に
INSERT ... ON CONFLICT DO UPDATEで status = 'in_progress' を書き、loop 完了時に 'completed' を書く。task 再起動時に status = 'in_progress' の row を read して last_processed_receipt_id より大きい id から再開。 - 信頼性: SIGTERM ハンドラで sweep_checkpoint.status を 'failed' に更新する 実装を義務付け、Cloud Run Job の task-timeout 強制終了 (SIGTERM → SIGKILL 10 秒間) で 'in_progress' が残るのを防ぐ。加えて 日次 sweep 開始時に「status = 'in_progress' かつ last_processed_at が 8 時間以上前の row」をタイムアウト孤立として自動検出し alert する ロジックを sweep-loop-runner 起動 hook に実装する。
- 信頼性: error 分類 tree = HTTP 5xx from GCS API (500/502/503/504) → transient (exponential backoff 3 回) / HTTP 429 from GCS API → transient / Postgres deadlock (SQLSTATE 40P01) → transient / HTTP 403 RETENTION_POLICY_NOT_MET from GCS API → skip (deletion_events に理由 = 'retention_lock_active' で INSERT · alert なし) / HTTP 403 その他 (auth 障害) → permanent (dead-letter + alert) / HTTP 401 from GCS API → permanent (dead-letter + alert) / HTTP 404 from GCS API → skip (既に削除済みの idempotent case · deletion_events に理由 = 'already_deleted' で INSERT · alert なし) / Postgres unique violation (SQLSTATE 23505) → skip (sweep 対象が既に消えている idempotent case) / Postgres foreign key violation (SQLSTATE 23503) → permanent (dead-letter + alert) / legal_hold_placements の active hold 存在 → skip (deletion_events に理由 = 'legal_hold_active' で INSERT · alert なし) / unknown exception → dead-letter + alert (定期的に分類 tree を拡張する)。error-classifier の全分岐は分類結果を structured log に出力し、unknown 枝に到達した件数を Cloud Monitoring カスタムメトリクスとして独立集計する (KPI-5 と KPI-6 の分母)。
- 信頼性: legal_hold 解除イベントは ADR-0218 の legal_holds 表更新 trigger から Cloud Tasks へ即時 sweep タスクを enqueue し、hold 解除後 1 時間以内の再 sweep を保証する。deletion_events に sweep_triggered_reason カラム (daily_sweep / hold_release_sweep) を追加 して監査証跡の gap をなくす。
- 保守性: deletion_events の reason 列に
retention_lock_activealready_deletedlegal_hold_activeの 3 skip 理由を CHECK 制約で許容し、監査時に skip の原因を区別可能にする。
4. 判断基準
4.1 評価軸
| # | 軸 (Q42) | 日本語軸名 | 重要度 (係数) | 案件特有の解釈 |
|---|---|---|---|---|
| 1 | #suitable | 適合性 | Must (×2.0) | ADR-0208 framework (Cloud Run daily sweep · Postgres SoT · GCS retainUntil bit 一致 · Terraform lifecycle 3 規則) を上書きせず具体化範囲に留める |
| 2 | #reliable | 信頼性 | Must (×2.0) | silent partial delete (Postgres commit 済で GCS delete 失敗) と silent failure (dead-letter に落ちない unknown) を防ぐ |
| 3 | #operable | 運用性 | High (×1.0) | 代表取締役 1 名運用で alert escalation と node 管理負荷が閉じる |
| 4 | #maintainable | 保守性 | High (×1.0) | Phase 1 GA (50 顧客 · receipts 50-500K 件) までスケール余地を持つ |
| 5 | #efficient | 効率性 | Medium (×0.5) | Phase 1 GA 50 顧客時点で月額 $10/月未満 |
K.O. criterion: Must 軸 (適合性 · 信頼性) の score < 3 は不採用。
4.2 評価軸 × 案スコア表
| 軸 | 係数 | 採択案 (Cloud Run Job) | 案 B (GKE CronJob) | 案 C (Cloud Functions) |
|---|---|---|---|---|
| 適合性 | ×2.0 | 5 | 2 | 3 |
| 信頼性 | ×2.0 | 5 | 4 | 2 |
| 運用性 | ×1.0 | 5 | 2 | 4 |
| 保守性 | ×1.0 | 4 | 4 | 1 |
| 効率性 | ×0.5 | 5 | 2 | 4 |
| 加重和 (正規化) | 0.936 | 0.518 | 0.500 | |
| K.O. 通過 (Must ≥3) | ✓ | ❌ (適合性 = 2) | ❌ (信頼性 = 2) |
5. 検討した代替案
5.1 案 1 (採用): Cloud Run Job + Cloud Scheduler + Cloud Tasks
採用案。詳細は §3.1 対策方針 参照。
- 適合性: ADR-0179 の GCP 単一 serverless 前提に沿い、ADR-0208 framework を上書きせず具体化範囲に留まる。
- 信頼性: lock-transaction-boundary で Postgres transaction と GCS delete の順序を固定でき、silent partial delete を防げる。
- 運用性: per-tenant queue で tenant 単位の失敗を分離し、node 管理・cluster upgrade が不要。
- 保守性: task-timeout 6h + parallelism 4 で Phase 0 dogfood を完走でき、Phase 1 GA では複数 task 分割 + fan-out へのリファクタ余地がある (行き先: §8 撤退条件 1)。
- 効率性: Cloud Run Job 従量課金 + Cloud Tasks + Cloud Scheduler で Phase 1 GA 50 顧客時点 $10/月未満。
5.2 案 2 (不採用): GKE CronJob
- 効率性: GKE cluster の常時稼働 cost が Cloud Run Job の従量課金を大きく上回り Phase 0 dogfood 規模では過剰。
- 運用性: GKE の node 管理・upgrade 運用が代表取締役 1 名運用の負荷を増やす。
- 適合性: ADR-0179 (JTBD-012 SaaS 基盤 GCP 単一) の serverless 前提から外れる。
5.3 案 3 (不採用): Cloud Functions timer trigger
- 保守性: Cloud Functions の実行時間上限 (2nd gen で 60 分) が dogfood 想定 receipts 1000 件でも余裕がなく Phase 1 GA スケールに耐えない。
- 信頼性: task 中断からの checkpoint 再開が Cloud Functions では自然に扱えず、独自 state 管理が必要になる。
5.4 案 4 (不採用): shared queue + tenant_id タグによるフィルタリング
- 効率性: GCP Cloud Tasks の queue 上限 500 件/プロジェクトを回避でき、テナント数が 250 を超えても quota 申請が不要。
- 信頼性: shared queue では 1 テナントの詰まり (permanent error 連発 · GCS API rate limit) が queue 全体の throughput を落とし、他テナントの sweep 完走率 (KPI-1) が連鎖劣化する。
- 運用性: tenant_id タグでのフィルタリング実装が sweep-loop-runner の複雑度を上げ、per-tenant queue に比べ debug 難度が高い。
- 保守性: 現時点 (Phase 1 GA 50 顧客) では queue 上限 500 件の半数まで余裕があり、テナント数 250 到達時点で本方式への切替を検討する余地は残る (行き先: §6.2 負の影響 · 撤退条件 6)。
6. 影響
6.1 正の影響
- 適合性: Phase 0 dogfood 開始日から日次 KPI 9 (孤立 audit_log 行数 = 0) の観測値が採取でき Phase 1 GA 前 3 ヶ月連続 100% 達成の実測分母が確定する。
- 信頼性: error 分類 tree + dead-letter queue + alert + backup 通知チャンネルで sweep 失敗の見落としが 0 件になる。
- 信頼性: legal_hold 解除後 1 時間以内の再 sweep 保証と deletion_events.sweep_triggered_reason カラムで、税務調査時の「改ざん可能期間」指摘リスクを最小化する。
- 運用性: per-tenant queue で tenant 単位の失敗が他 tenant に波及しない。
- 保守性: sweep_checkpoint 表と SIGTERM ハンドラで task 中断からの再開経路が明示され spot instance 中断や task-timeout 到達時の recovery が自動化される。
6.2 負の影響 / リスク
- 効率性: Cloud Tasks queue 依存が 1 個増え APIs & Services の依存本数が増える。
- 効率性: テナント数が 250 を超えると Cloud Tasks queue 上限 500 件/プロジェクトの半数を Retention Manager が占有し、他用途の queue 追加に GCP quota 申請が必要になる (行き先: §8 撤退条件 6 · §5.4 shared queue 案への切替検討)。
- 保守性: Phase 1 GA (50 顧客 · receipts 50-500K 件) で task-timeout 6h · parallelism 4 の値が過小になり複数 task 分割 + fan-out へのリファクタが必要になるリスク (行き先: §8 撤退条件 1)。GCS delete の p99 レイテンシが 300-500ms のケースでは 500K 件/テナントで parallelism 4 のまま 10 時間相当となり task-timeout 6h を超える試算のため、Phase 0 dogfood で 1 件あたり p50/p99 レイテンシを実測して撤退条件 1 の定量閾値を前倒し判定する。
- 保守性: error 分類 tree の unknown 枝が dead-letter に落ちるたびに tree 拡張の運用作業が発生する。
7. コスト試算
7.1 前提条件
| 項目 | 値 | 根拠 |
|---|---|---|
| Phase 0 dogfood tenant 数 | 1 | JTBD-012 Phase 0 定義 |
| Phase 0 想定 receipts | 100-1000 件 | dogfood 想定利用量 |
| Phase 1 GA 顧客数 | 50 | ADR-0208 KPI 5 の Phase 1 GA 分母 |
| Phase 1 GA 想定 receipts | 50-500K 件 | Phase 1 GA 顧客数 × 平均 receipts/顧客 |
| Cloud Run Job 実行時間 (Phase 0) | 720 秒/日 × 30 日 = 21,600 秒/月 | task-timeout 6h × parallelism 4 の実効平均 |
| Cloud Scheduler cron | 30 dispatches/月 | 日次 UTC 15:00 起動 |
| GCS delete レイテンシ (想定) | p50 100ms · p99 300-500ms | Google Cloud Status History 2023-2024 実績。Phase 0 dogfood で実測して §8 撤退条件 1 の定量閾値を確定 |
| Phase 1 GA parallelism 4 完走上限 (試算) | 約 288K 件/テナント (300ms × 500K ÷ 4 = 37,500 秒 > task-timeout 6h = 21,600 秒 のため) | GCS delete p99 300ms 前提 · 実測後に上振れ・下振れを判定 |
| 為替 | 150 円/USD | 起案時レート |
7.2 月コスト試算
- Phase 0 dogfood (tenant 1): Cloud Run Job 実行時間 + Cloud Tasks dispatches + Cloud Scheduler + Cloud Monitoring alert で月額 300 円未満 ($2/月未満) を目安。
- Phase 1 GA (50 顧客): 同構成で月額 1,500 円未満 ($10/月未満) を目安。
- 実装工数: 5 module + sweep_checkpoint schema + deletion_events.sweep_triggered_reason カラム追加 + SIGTERM ハンドラ + タイムアウト孤立自動検出 + Terraform lifecycle 追記 で 6 人日相当 (盲点対応で +1 人日)。
8. 撤退条件
- Cloud Run Job task-timeout 到達で 3 日連続 checkpoint 未完了、または Phase 0 dogfood 実測 GCS delete p99 × 想定 receipts 数 ÷ parallelism が task-timeout 6h の 80% を超過 → 発火から 14 日以内に 複数 task 分割 + Cloud Tasks 経由の fan-out へのリファクタを扱う後続 ADR を起票する (起票義務)。起票までの暫定運用は parallelism 4 → 8 の増強 (撤退条件 3 と同型) で凌ぐ。
- dead-letter queue が 30 日連続で 0 件 → error 分類 tree の網羅性 review (silent failure 疑い) + §9 KPI-6 の月次 chaos drill 結果を確認。
- sweep 実行時間の月次中央値が 6h の 80% 超 → parallelism を 4 → 8 へ増強 or task 分割
- error 分類 tree の unknown 比率が月次 20% 超 → tree の枝を追加し unknown を permanent/transient/skip に再分類
- 月額実コストが想定レンジ (Phase 1 GA 時点 1,500 円/月) を 2 倍超過 → parallelism / cron 頻度 / queue 設定の見直し
- テナント数が 250 に到達 → Cloud Tasks queue 上限 500 件/プロジェクトの半数占有時点で §5.4 shared queue + tenant_id タグ方式への切替 ADR を起票する (起票義務)。
9. Confirmation
- KPI-1 sweep 完走率 = 100% (検証: sweep_checkpoint.status = 'completed' の割合 · 頻度: 日次 · 違反時: sweep-loop-runner の error log を review し error-classifier に枝追加)
- KPI-2 GCS delete 成功率 = 100% (検証: gcs_delete_status = 'success' の割合 · 頻度: 日次 · 違反時: lock-transaction-boundary の rollback が動いたか監査)
- KPI-3 dead-letter queue の 30 日累積件数 ≤ 3 件 (検証: Cloud Tasks queue metric · 頻度: 月次 · 違反時: 積まれた message の error 分類を review し tree 拡張)
- KPI-4 sweep 実行時間の P95 ≤ task-timeout の 80% (検証: Cloud Run Job execution metric · 頻度: 週次 · 違反時: parallelism 4 → 8 増強 or task 分割検討)
- KPI-5 error 分類の transient/permanent/unknown 比率 (検証: error-classifier の structured log 集計 + Cloud Monitoring カスタムメトリクス · 頻度: 月次 · 違反時: unknown が 20% 超なら分類 tree 拡張)
- KPI-6 dead-letter 経路の生存確認 (月次 chaos drill) (検証: 意図的にテスト用 unknown exception を inject して dead-letter queue に到達 + alert 発火 + backup 通知チャンネル到達を月次で確認 · 頻度: 月次 · 違反時: dead-letter-alerter または通知チャンネル設定の障害と判断し 7 日以内に修正)
- KPI-7 alert 通知チャンネル到達確認 (検証: primary email + backup チャンネル両方に月次テスト alert を送信し受信を確認 · 頻度: 月次 · 違反時: 未達チャンネルの設定を 7 日以内に修正)
- KPI-8 legal_hold 解除後の再 sweep 遅延 (検証: legal_hold 解除イベント時刻から次の deletion_events INSERT (sweep_triggered_reason = 'hold_release_sweep') までの経過時間 · 頻度: 月次 · 違反時: 60 分超過事例が 1 件でもあれば legal_holds 表 trigger の enqueue 経路を再点検)
10. 参照
10.1 関連 ADR
- Amends: ADR-0208 (証憑の保存年限管理と期限後廃棄) — 本 amend は §3.1 対策方針 / §3.3 仕様 / §11 実装配線 を通じて ADR-0208 の運用実装 framework を module 単位に具体化する
- ADR-0203 (receipts と audit_log の schema 基礎) — 本 ADR が sweep で参照する schema の SoT
- ADR-0218 (legal_holds 表新設) — 本 ADR の lock-transaction-boundary で legal_hold gate を呼ぶ相手側 schema。legal_holds 表更新 trigger から Cloud Tasks への即時 sweep enqueue 経路を追加する
- ADR-0217 (税務申告完了関連 schema 追加) — 本 ADR の checkpoint 表新設と同一 amend cycle で扱う schema 変更 (Phase 0 KPI-4 分母への影響を review 要)
- ADR-0179 (JTBD-012 SaaS 基盤 GCP 単一) — 本 ADR の Cloud Run / Cloud Scheduler / Cloud Tasks が GCP 単一基盤前提
10.2 関連 PR/Issue
- PR-4 (最初の証憑保存 endpoint · session 24 shipping · #5085) — 本 ADR の Retention Manager runtime deploy を merge 前に済ませる必要がある depends
- vlt session 24 handover §4 継承残タスク Claude 単独可 (2) — 本 draft prompt 起票の元指示
10.3 外部資料
- Cloud Run Jobs docs: task-timeout / parallelism / retry policy / SIGTERM グレースフル期間
- Cloud Scheduler docs: cron 表現 / target = Cloud Run Job invocation
- Cloud Tasks docs: per-queue rate limit / retry policy / dead-letter / queue 上限 500 件/プロジェクト
- GCS Object Retention Lock docs: HTTP 403 RETENTION_POLICY_NOT_MET の返却条件
- Google SRE Book Chapter 6: alert escalation policy · 通知チャンネル冗長化
11. 実装配線
- lint 配線:
scripts/vlt-schema-lint.mjs(= vlt schema の追記専用トリガー等を検査する lint script) に sweep_checkpoint 表の追記専用トリガー存在チェック · lock-transaction-boundary の同一トランザクション性検査 · deletion_events.sweep_triggered_reason カラム CHECK 制約検査を追加 (path-scopedvlt/**/*.sql· WIRING gate) ·scripts/adr-body-lint.mjs(= ADR 本文の網羅性 lint script) の error 分類 tree 節に「HTTP status code の網羅性 (401/403/404/429/500/502/503/504) と Postgres SQLSTATE の網羅性 (23505/23503/40P01) と HTTP 403 の RETENTION_POLICY_NOT_MET / auth 障害への分岐存在」を検査項目として追加 - 起案フロー配線: 本 ADR 受理後、
.claude/rules/vlt-schema.md(session 23 で作成済) に「sweep_checkpoint 表は tenant_id PRIMARY KEY で per-tenant checkpoint · in_progress 中の receipt id より大きい id から再開 · SIGTERM ハンドラで 'failed' に更新 · タイムアウト孤立 (in_progress かつ last_processed_at 8 時間超) を日次 sweep 開始時に自動検出」と「deletion_events.sweep_triggered_reason カラム (daily_sweep / hold_release_sweep) と reason skip 3 値 (retention_lock_active / already_deleted / legal_hold_active)」を追記 · ADR-0208 §10.1 起票枠を「本 ADR で消化済」に更新し ADR-0208 の amended_by に本 ADR を追加 - docs SSoT 同期:
docs/architecture/arch_jtbd012_voucher_saas_draft.md(= JTBD-012 の層別 architecture draft) の 層 ④ RetentionManager 節に「5 module (sweep-loop-runner · lock-transaction-boundary · checkpoint-resumer · error-classifier · dead-letter-alerter)」の構造図と「legal_hold 解除 → 即時 sweep enqueue」の flow を追記 ·docs/architecture/arch_jtbd012_decision_inventory.md(= JTBD-012 の決定塊 inventory) の D9 塊 (Retention Manager) 実装状態を「実装 module 確定 (ADR-NNNN)」へ更新 ·docs/adr/INDEX.mdの関係図に本 ADR を追加し amends ADR-0208 の辺を追加