1. Executive Summary

  • 文脈: Retention Manager (ADR-0223) の 5 つのモジュールを統合したパイプラインは audit_log INSERT を module 責務外とし、Phase 1 で event-emitter 経路に委譲する設計 (§3.3 仕様)。
  • 課題: Phase 0 期間は event-emitter が VLT_AUDIT_LOG_ENABLED=false default で無効化されるため、5 module 有効化 (P3-2 flag flip) すると audit_log INSERT が消失し、JIIMA 認証審査 (ADR-0206) の監査の完全性に関する設問に説明不能となる。
  • 対策: トランザクションロック境界の deleteReceiptWithLock 関数内に audit_log INSERT を Phase 0 期間限定で追加し、ADR-0223 §3.3 仕様の責務外条項を Phase 0 限定で amend する。
  • 効果: P3-2 flag flip 可能 · sweep のアトミシティを完全に維持 · Phase 0 audit 完全性 100% 維持 · 5 module 分解の周辺機構を Phase 0 から享受。
  • 代償: トランザクションロック境界の責務境界が「lock + delete + audit」の 3 者に一時的に拡大 (Phase 1 でイベントエミッターへの移送により解消) · 実装工数 5 人日 (schema · 実装 · e2e/integration test · lint 4 rule · 補正 migration · docs 5 種同期を含む · 起案 draft 段階の 3 人日は過小評価だった) · audit_log 表 2 列追加で Phase 0 KPI-4 (schema 変更 ≤ 1 回) を消費。

2. 何を解決するか

2.1 直面していること

適合性:

  • Retention Manager (ADR-0223) の 5 module 統合 pipeline は audit_log INSERT を module 責務外とする設計で、Phase 1 で event-emitter に委譲予定である。
  • Phase 0 期間は event-emitter が VLT_AUDIT_LOG_ENABLED=false default で無効化されている (ADR-0222 §3.3 仕様)。
  • 既存 vlt/src/db/sweep.ts (all-in-one 実装 · 150 行) を 5 module 統合 pipeline に置換すると audit_log INSERT が Phase 0 期間中に欠落し、JIIMA 認証審査 (ADR-0206) の「削除経路の audit 完全性」設問に説明不能となる。

信頼性:

  • vlt/src/db/sweep.ts の sweep atomicity は ADR-0203 §9-9 KPI-9 sweep atomicity SoT に準拠し、単一トランザクション内で lock + delete + audit の 3 者を閉じ込めている。
  • vlt/tests/e2e/sweep-audit-atomicity.test.ts (KPI-9 atomicity verify) で 3 種 orphan 検出 (削除失敗検出 · audit 側孤立 · events 側孤立) が厳密検証されている。
  • 5 module 統合時にこの atomicity 保証を継承する必要がある。

2.2 放置すると起こること

適合性:

  • P3-2 (enable_retention_manager = true flag flip) 保留が長期化し、Phase 1 GA (2027 想定) 直前に sweep.ts 削除 + 5 module 有効化 + event-emitter 有効化の 3 段同時変更が必要となり fault-radius が大きくなる。
  • Phase 0 dogfood 期間の Retention Manager 実運用検証が積めず、Phase 1 一斉切替時のリスクが集中する。

保守性:

  • 5 module 実装が Phase 1 まで dead code 気味に置かれ、integration test は継続稼働で protection されるが実 dispatcher が無い状態が続く。

2.3 潜在リスク

適合性:

  • Phase 0 期間の暫定措置として lock-transaction-boundary の責務が「lock + delete + audit」の 3 者に拡大するが、Phase 1 で event-emitter 移送するまでの一時的な責務肥大であり実害は未発生である。
  • 移送タイミングを §8 撤退条件連動に明示することで長期化リスクを抑える。

信頼性:

  • GCS deleteObject は Postgres COMMIT に参加しない外部 I/O のため、GCS 削除成功後の Postgres COMMIT 失敗時に「GCS オブジェクト消失 · receipt 行残存」の逆向き孤立が発生しうる (本 ADR は既存 sweep.ts の tx 境界順序をそのまま継承)。§9 Confirmation KPI 3 で検出、§11 実装配線 に手動介入 runbook を明記。この失敗方向の恒久解決 (GCS delete を COMMIT 後の補償ワーカーに移管) は別 ADR (draft_adr_gcs-delete-after-commit-compensation.md として起票予定 · 本 ADR §11 実装配線の起案フロー配線に含める) で扱う。
  • Phase 1 event-emitter 有効化時のローリングアップデート (Cloud Run 新旧 revision 併存 · ~30 秒) 中に、旧 revision (lock-transaction-boundary INSERT) と新 revision (event-emitter INSERT) の両経路が並行稼働し得る。§3.3 仕様 で AUDIT_LOG_CALLER runtime env var による排他ゲート (旧 revision は起動時に env var を再読込しないため runtime 切替不能) を導入し、両経路の同時稼働を 30 秒未満に抑制する。

3. 採用したい方針

3.1 対策方針

lock-transaction-boundary の deleteReceiptWithLock 関数内に audit_log INSERT を Phase 0 期間限定で追加する。ADR-0223 §3.3 仕様の責務外条項を Phase 0 限定で amend し、Phase 1 event-emitter 有効化時 (VLT_AUDIT_LOG_ENABLED=true 切替時) に本 boundary から audit_log INSERT を除去して event-emitter 経路に完全移送する。段階的移送を追跡可能とするため audit_log 表に phase 列と caller 列を追加する。両経路が並存する期間の二重 INSERT を根本的に防ぐため、VLT_AUDIT_LOG_ENABLED=true 切替と lock-transaction-boundary 内 audit_log INSERT 削除は同一 PR で同梱することを本 ADR §8 撤退条件 (1) で強制する。加えて runtime env var AUDIT_LOG_CALLER (値 = lock-transaction-boundary or event-emitter) を導入し、両実装が起動時に env var を読んで排他的に INSERT を実行する構成にする (ローリングアップデート中は Cloud Run 新旧 revision が最大 30 秒程度共存するが、env var は起動時読込のため revision 切替後に混在は自然解消する)。

  • 適合性: audit_log INSERT の caller は Phase 0 は lock-transaction-boundary · Phase 1 は event-emitter として段階的に移送する。
  • 適合性: 両 Phase を通じて audit 痕跡は audit_log 表に集約し SoT 単一化を維持する。
  • 信頼性: 単一トランザクション境界内で record + delete + audit の 3 者を閉じ込め、ADR-0203 §9-9 KPI-9 sweep atomicity SoT 準拠を継承する。
  • 効率性: 実装工数は schema 2 列追加 + 補正 migration + lock-transaction-boundary 内 audit_log INSERT 追加 + AUDIT_LOG_CALLER runtime gate + e2e test 対象 module 差替 + 4 新規 case + integration test 13 case への assertion 追加 + vlt-schema-lint 4 rule 追加 + docs 5 種同期 + vlt/src/db/sweep.ts 削除 で合計 5 人日 (draft 段階の 3 人日は schema/migration/lint/docs を過小評価していたため 5 人日へ改訂)。

3.2 守るべき設計制約

  • 適合性: audit_log の SoT は Postgres 表のみとし、caller が変わっても SoT 表は不変とする。
  • 適合性: ADR-0223 §3.3 仕様の責務外条項の amend は Phase 0 期間限定として書き、Phase 1 移送を本 ADR §8 撤退条件で明示する。
  • 適合性: VLT_AUDIT_LOG_ENABLED=true 切替と lock-transaction-boundary 内 audit_log INSERT 削除は必ず同一 PR で同梱し、両経路並存による二重 INSERT を発生させない。
  • 信頼性: sweep atomicity は単一トランザクション境界内で維持し ADR-0203 §9-9 KPI-9 に準拠する。
  • 信頼性: GCS 削除は Postgres COMMIT に参加しない外部 I/O であるため、COMMIT 直前クラッシュによる「GCS オブジェクト消失 + receipt 行残存」の孤立方向を e2e test で明示的にカバーする。
  • 効率性: vlt/src/db/sweep.ts (all-in-one 150 行) は本 ADR 受理後の PR で削除する。

3.3 仕様

  • 信頼性: lock-transaction-boundary の実装 (Phase 0 暫定) は下記の通り、単一トランザクション内で lock 取得 · hold gate 評価 · GCS 削除 · deletion_events INSERT · audit_log INSERT · receipt 削除の 6 手順を閉じ込める。
// runtime env var (Cloud Run env) で INSERT 経路を排他制御
// Phase 0: AUDIT_LOG_CALLER='lock-transaction-boundary'
// Phase 1: AUDIT_LOG_CALLER='event-emitter' へ切替 (VLT_AUDIT_LOG_ENABLED=true 同時)
const AUDIT_LOG_CALLER = process.env.AUDIT_LOG_CALLER ?? 'lock-transaction-boundary';

export async function deleteReceiptWithLock(receipt: Receipt): Promise<Result> {
  return await db.transaction(async (tx) => {
    await tx.selectForUpdateSkipLocked(receipt.id);
    await tx.evaluateHoldGate(receipt.id);
    await storage.deleteObject(receipt.gcs_path);
    await tx.insertDeletionEvent(receipt.id);
    if (AUDIT_LOG_CALLER === 'lock-transaction-boundary') {
      await tx.insertAuditLog({
        action: 'retention_sweep_delete',
        target_id: receipt.id,
        phase: '0',
        caller: 'lock-transaction-boundary',
      });
    }
    // Phase 1 で AUDIT_LOG_CALLER='event-emitter' へ切替後は event-emitter 経路が INSERT を担う
    await tx.deleteReceipt(receipt.id);
    // TODO(phase1-migration): ADR-0225 §8 撤退条件 1 項
    // — VLT_AUDIT_LOG_ENABLED=true 切替と同一 PR で本 boundary の insertAuditLog を削除必須
  });
}

GCS 削除失敗方向の孤立 (§2.3 潜在リスク 信頼性): 上記 code は GCS 削除を tx 内に配置しているため GCS 削除成功 → Postgres COMMIT 失敗時に「GCS 消失 · receipt 行残存」が発生しうる。本 ADR は既存 sweep.ts の tx 境界順序を継承し §9 Confirmation KPI 3 で新規 case (GCS 削除成功 → Postgres COMMIT 失敗) の e2e 検出のみ担保する。恒久解決 (GCS delete を COMMIT 後の補償ワーカーへ移送) は本 ADR §11 起案フロー配線 に別 ADR 起票 (draft_adr_gcs-delete-after-commit-compensation.md) を計上する。

  • 適合性: audit_log 表に 2 列 (phase VARCHAR(8) NOT NULL · caller TEXT NOT NULL) を追加する。既存行は ALTER 時の DEFAULT で 'pre-0' / 'sweep.ts' に自動補正し、直後に DROP DEFAULT で新規 INSERT の DEFAULT 依存を封じる (Phase 1 で event-emitter が caller を明示指定し忘れた場合の「phase='1' かつ caller='lock-transaction-boundary'」というモデル矛盾を DB 制約エラーで即時検出する)。CHECK 制約は 3 値許容 ('pre-0' は列追加以前の既存行専用、新規 INSERT は '0' / '1' のみ)。
-- Step 1: 既存行 back-fill 用に DEFAULT 付きで列追加
ALTER TABLE audit_log
  ADD COLUMN phase   VARCHAR(8) NOT NULL DEFAULT 'pre-0'
             CHECK (phase IN ('pre-0', '0', '1')),
  ADD COLUMN caller  TEXT       NOT NULL DEFAULT 'sweep.ts';

-- Step 2: 新規 INSERT の DEFAULT 依存を封じる (明示指定漏れを制約エラーで検出するため)
ALTER TABLE audit_log
  ALTER COLUMN phase  DROP DEFAULT,
  ALTER COLUMN caller DROP DEFAULT;

-- (DEFAULT DROP 後の 'pre-0' 保持行は列追加以前の既存行のみ · JIIMA 審査における
--  「過去行の caller 誤表記」を防ぐ · §6.2 参照)

CREATE INDEX idx_audit_log_phase_caller_action
  ON audit_log (phase, caller, action);

4. 判断基準

4.1 評価軸

#軸 (Q42)日本語軸名重要度 (係数)案件特有の解釈
1#suitable適合性Must (×2.0)JIIMA 認証審査 (ADR-0206) の audit 完全性設問に説明可能か
2#reliable信頼性Must (×2.0)sweep atomicity (ADR-0203 §9-9 KPI-9) を継承するか
3#maintainable保守性High (×1.0)audit_log の caller 遷移 (Phase 0 → Phase 1) を追跡可能か
4#efficient効率性Medium (×0.5)実装工数と Phase 1 移送工数の合算が sweep.ts 単独継続比で優位か

K.O. criterion: Must 軸 (適合性 / 信頼性) score < 3 は不採用。

4.2 評価軸 × 案スコア表

各案 0-5 で評価。加重和 = (Σ score × 係数) / (満点 × Σ 係数) で正規化。

係数案 1 (採用)案 2 (Phase 0 sweep.ts 継続)案 3 (併存維持)
適合性×2.0543
信頼性×2.0551
保守性×1.0421
効率性×0.5422
加重和 (正規化)0.9450.7270.400
K.O. 通過 (Must ≥3)❌ (信頼性=1)

5. 検討した代替案

5.1 案 1 (採用): lock-transaction-boundary 内 audit_log INSERT 暫定追加

内容は §3.3 仕様に同じ。ADR-0223 §3.3 仕様の責務外条項を Phase 0 限定で amend し、Phase 1 で event-emitter に移送する。

  • 適合性: audit 完全性を Phase 0 期間中も 100% 維持する。
  • 信頼性: sweep atomicity を単一トランザクション境界内で継承する。
  • 保守性: phase / caller 列で Phase 0 → Phase 1 の遷移を SQL で追跡できる。

5.2 案 2 (不採用): Phase 0 sweep.ts 単独稼働継続 · Phase 1 まで統合を延期

  • 適合性: Phase 0 期間中の audit_log 完全性は 100% 維持できる。
  • 信頼性: sweep.ts の atomicity は既存のまま維持される。
  • 効率性: Phase 1 一斉切替時に sweep.ts 削除 + 5 module 有効化 + event-emitter 有効化の 3 段同時変更が必要となり fault-radius が大きい。
  • 効率性: P3-2 flag flip 保留が長期化し Phase 0 dogfood 検証が積めない。

5.3 案 3 (不採用): sweep.ts と 5 module 統合 pipeline の併存維持

  • 信頼性: 2 経路が同一 receipt を並行処理する race condition が発生し、sweep.ts の SELECT FOR UPDATE SKIP LOCKED と 5 module 側の同種 lock で lock 衝突または二重 audit_log INSERT のリスクを持つ (K.O.)。
  • 保守性: どちらの経路で処理されたかの trace が必要となり運用が複雑化する。

5.4 案 4 (不採用): GCS 削除を Postgres COMMIT 後に移動する at-least-once 補償パターン

  • 信頼性: GCS 削除を COMMIT 後に移動すると Postgres 側の atomicity は完全に保たれるが、COMMIT 成功後 GCS 削除失敗時に「receipt 論理削除済み・GCS オブジェクト残存」の逆向き孤立が発生する。この孤立方向は補償ワーカー再試行で解決するが、Phase 0 期間中に追加ワーカーを新設する工数 (推定 5 人日) が Phase 0 予算枠を超過する。
  • 信頼性: 代替として案 1 (採用) では sweep-audit-atomicity.test.ts に「GCS 削除成功 · Postgres COMMIT 失敗」シナリオを新規 orphan 検出 case として追加し、COMMIT 直前クラッシュ時の GCS オブジェクト消失を検出可能とする (§9 Confirmation (2) · §11 実装配線に明記)。Phase 1 event-emitter 移送時に at-least-once 補償パターンへの移行を別 ADR で改めて評価する。

6. 影響

6.1 正の影響

  • 適合性: P3-2 (enable_retention_manager flag flip) が unblock され、Cloud Tasks per-tenant queue + Cloud Monitoring alert 2 種の生成経路が有効化される。
  • 適合性: audit_log の完全性を Phase 0 期間中も 100% 維持し、JIIMA 認証審査 (ADR-0206) で説明可能な状態を保つ。
  • 信頼性: ADR-0223 5 module 統合 pipeline の Phase 0 実運用検証が可能となり Phase 1 GA 直前の fault-radius が縮小する。
  • 保守性: vlt/src/db/sweep.ts (all-in-one 150 行) を削除でき Retention Manager 5 module 分解 SoT を単一化できる。

6.2 負の影響 / リスク

  • 適合性: ADR-0223 §3.3 仕様の責務外条項の Phase 0 限定 amend が発生し、Phase 1 で amend 解除の作業が必要となる。
  • 適合性: ALTER TABLE で追加した phase / caller 列に対し、本 ADR 適用以前に sweep.ts 経路で INSERT 済みの audit_log 既存行を phase='pre-0' / caller='sweep.ts' に UPDATE 補正する migration が必要となる (§3.3 DDL 参照)。JIIMA 審査用のデータ来歴説明書に「列追加以前の行は sweep.ts 経路であり、後付け列は補正済み」を明記する。
  • 信頼性: lock-transaction-boundary の責務境界が「lock + delete + audit」の 3 者に一時的に拡大する (Phase 1 で event-emitter 移送により解消)。
  • 信頼性: VLT_AUDIT_LOG_ENABLED=true 切替と lock-transaction-boundary 内 audit_log INSERT 削除を別 PR にすると両経路から同一 receipt_id への二重 INSERT が発生するため、§8 撤退条件 (1) で同一 PR 同梱を明文化し §11 実装配線に対応 lint rule を追加する。
  • 効率性: audit_log 表への 2 列追加で Phase 0 KPI-4 (schema 変更 ≤ 1 回) を本 ADR で消費する (現時点残 1 回)。Phase 0 残期間中に未発見の schema 不整合 (hold gate 条件列 · tenant 分離列等) が露見して追加 ALTER TABLE が必要になった場合は §8 撤退条件 (4) の判定手順に従う。

6.3 中立・トレードオフ

  • 保守性: audit_log の phase / caller 列は Phase 1 移送完了後も残置し、履歴追跡用途として保持する (削除しない)。

7. コスト試算

7.1 前提条件

項目根拠
Phase 0 tenant 数1ADR-0223 Phase 0 定義
retention sweep 頻度月 10-50 件期限到来 receipt が発生する都度
実装工数5 人日schema (0.5) + 補正 migration (0.5) + lock-transaction-boundary 実装 + AUDIT_LOG_CALLER runtime gate (0.5) + e2e test 差替 + 4 新規 case (1.0) + integration test 13 case assertion (0.5) + vlt-schema-lint 4 rule (0.5) + sweep.ts 削除 (0.5) + docs 5 種同期 (0.5) + PR review (0.5) の合計 · Claude 主導 · 代表取締役 review 含む

7.2 月コスト試算

  • storage 追加分: audit_log 2 列 (phase 5 char 上限 + caller 30 char 想定) × 月 10-50 件 = <2 KB (無視できる規模)
  • 実装工数: 5 人日 (Phase 0 全体予算 ADR-0203 §7 総額 $33 に対し追加 <1% · 予算内収まる)
  • 合計: Phase 0 全体で 5 人日 + <2 KB/月の storage 増

8. 撤退条件

  1. Phase 1 event-emitter 有効化時 (VLT_AUDIT_LOG_ENABLED=true 切替時) に lock-transaction-boundary 内 audit_log INSERT を削除し event-emitter 経路に完全移送、ADR-0223 §3.3 仕様の amend を解除する。両操作は同一 PR で必ず同梱する (別 PR 分離時は両経路から同一 receipt_id への二重 INSERT が発生するため)。scripts/vlt-schema-lint.mjs に「VLT_AUDIT_LOG_ENABLED=true かつ lock-transaction-boundary に audit_log INSERT が残存する場合 CI 失敗」rule を追加する (§11 実装配線)。
  2. 本 ADR 受理から 6 ヶ月経過時点で以下のいずれかに該当する場合、Phase 0 期間短縮判断または別戦略再検討を別 ADR で議論する (起票期限 = 6 ヶ月経過から 2 週間以内):
    • P3-2 (enable_retention_manager flag flip) が未実施のまま
    • Phase 0 dogfood の retention sweep 実行が 3 ヶ月連続で月 10 件を下回り実運用検証が積めない
  3. 「暫定の恒久化」防止として、本 ADR 受理日から 12 ヶ月経過 (= 6 ヶ月レビュー + Phase 1 GA 予定 2027-01 頃 + 追加猶予 3 ヶ月) を超えても phase='0' の audit_log が新規 INSERT され続けている場合、scripts/vlt-schema-lint.mjs の date-based check で CI を red にする。lint 解除には Phase 1 移送完了または本 ADR の supersede が必須。ただし Phase 1 GA スケジュールが遅延した場合は代表取締役 approver による書面承認 + 期限延長 amend PR を追加で 3 ヶ月まで許容する (再延長は別 ADR)。
  4. audit_log 表への phase / caller 列追加が既存 audit_log schema と衝突した場合、別列名に変更する (受理 PR 内で対応)。Phase 0 残期間中に他の schema 変更が必要になった場合は KPI-4 超過となるため、代表取締役 approver による追加承認 + ADR-0203 §9-9 KPI-4 説明義務履行の 2 手順を経る。

9. Confirmation

  1. audit_log INSERT の完全性 (孤立検出): deletion_events に対し audit_log の retention_sweep_delete 記録が欠落した孤立行数を SQL で検出。週次実行。SLO 常時 0 件。違反時は lock-transaction-boundary の tx 境界 review と INSERT 経路の実装確認を行う。
SELECT COUNT(*) FROM deletion_events de
  LEFT JOIN audit_log al
    ON al.target_id = de.receipt_id
    AND al.action = 'retention_sweep_delete'
  WHERE de.created_at > NOW() - INTERVAL '7 days'
    AND al.id IS NULL;
  1. audit_log INSERT の二重化検出: 同一 receipt_id · action='retention_sweep_delete' に対し audit_log 行が 2 件以上存在する状態を SQL で検出。週次実行 + Phase 1 移送 PR merge 前に必ず 1 回実行。SLO 常時 0 件。違反時は VLT_AUDIT_LOG_ENABLED gate 状態と lock-transaction-boundary の INSERT 残存有無を即時確認し、両経路並存を解消する。
SELECT target_id, COUNT(*) AS dup_count
  FROM audit_log
  WHERE action = 'retention_sweep_delete'
    AND created_at > NOW() - INTERVAL '7 days'
  GROUP BY target_id
  HAVING COUNT(*) > 1;
  1. sweep atomicity 継承 + 5 module 固有障害シーケンス: vlt/tests/e2e/sweep-audit-atomicity.test.ts の 3 種 orphan 検出テストが全 green + 新規追加 3 case (GCS 削除失敗 · hold gate 評価失敗 · audit_log INSERT 失敗 · GCS 削除成功 → Postgres COMMIT 失敗) が全 green。CI 各 push で自動走行 + 週次 pattern 確認。違反時は実装再設計と sweep.ts 復活 (feature flag による切戻し可能状態) を行う。5 module 統合 pipeline 固有 case が追加されていない PR は merge 不可 (§11 実装配線 review checklist)。

  2. Phase 0 → Phase 1 caller 遷移追跡: audit_log の phase / caller 分布を SQL で確認。Phase 0 期間中は phase='0' · caller='lock-transaction-boundary' 100%。Phase 1 移送時は phase='1' · caller='event-emitter' に切替。Phase 0 期間中は月次 + Phase 1 移送時に 1 回。違反時は event-emitter 有効化後も lock-transaction-boundary が INSERT 継続している状態として移送漏れを検出する。

SELECT DISTINCT phase, caller, COUNT(*)
  FROM audit_log
  WHERE action = 'retention_sweep_delete'
    AND created_at > NOW() - INTERVAL '30 days'
  GROUP BY phase, caller;

10. 参照

10.1 関連 ADR

  • Amends: ADR-0223 (Retention Manager 運用実装) — §3.3 仕様の「audit_log INSERT は 5 module 責務外」条項を Phase 0 期間限定で amend する。Phase 1 event-emitter 移送時 (本 ADR §8 撤退条件 1 項) に amend を解除する。
  • ADR-0208 (Retention Manager framework) — 補完。5 module 分解 SoT を継承する。
  • ADR-0203 (schema 基礎) — 補完。§9-9 KPI-9 sweep atomicity SoT を継承 · audit_log 2 列追加で KPI-4 (Phase 0 中 schema 変更 ≤ 1 回) を本 ADR で消費する。
  • ADR-0175 (3 号真実性ルート 4 要素) — 適合。#3 append-only の完全性 (audit_log 表への INSERT を Phase 0 でも継続) を維持する。
  • ADR-0206 (JIIMA 認証並行申請) — 適合。audit 完全性設問への説明可能状態を Phase 0 中も維持する。
  • ADR-0222 (append-only 監査ログ物理担保) — 適合。event-emitter 経路の VLT_AUDIT_LOG_ENABLED gate と本 boundary 経路の Phase 段階的移送 SoT を統合する。

10.2 関連 PR/Issue

  • PR #5447 (ADR-0223 P3-2 sweep.ts refactor 意思決定 memo) — 元起点 · 3 選択肢 A/B/C 比較 · 本 ADR は選択肢 A 採用の正式 ADR 化。
  • PR #5439 (retention-manager main entrypoint integration test) — 5 module 統合 pipeline 完備の shipping · 本 ADR で dispatcher を有効化する。

10.3 外部資料

  • 電子帳簿保存法施行規則 第 4 条第 1 項第 3 号 (訂正削除履歴 · 3 号ルート)
  • PostgreSQL Transaction 仕様 (単一トランザクション境界の atomicity · COMMIT / ROLLBACK 保証)
  • Martin Fowler, "Feature Toggles (aka Feature Flags)", 2017 — 暫定 feature flag の長期放置による「暫定の恒久化」アンチパターン。§8 撤退条件 (3) の date-based lint rule 設計根拠。

11. 実装配線

  • lint 配線: scripts/vlt-schema-lint.mjs (= vlt 用 schema 検証 script) に 4 rule 追加 — (a) audit_log 表に phase 列 (VARCHAR(8)) と caller 列 (TEXT) が存在し phase CHECK 制約が ('pre-0', '0', '1') の 3 値許容 + DROP DEFAULT 適用後の状態 (INSERT 省略時の DB 制約エラー発生) を検証 · (b) deletion_events INSERT した receipt_id が同一トランザクション内で audit_log の retention_sweep_delete action 記録と 1:1 対応することを検証 (孤立 + 二重 INSERT の双方向検出) · (c) AUDIT_LOG_CALLER=event-emitter かつ vlt/src/retention-manager/lock-transaction-boundary.tsinsertAuditLog 呼び出しが残存する場合 CI 失敗 (runtime gate と code の整合検証) · (d) 本 ADR 受理日 + 12 ヶ月経過後も phase='0' の audit_log が新規 INSERT され続けている場合 CI red (§8 撤退条件 3 · 期限延長 amend PR による書面延長は許容)。
  • 起案フロー配線: 本 ADR 受理後、下記を同一 PR に同梱 — vlt/src/db/schema.sql (= vlt スキーマ定義) に audit_log 2 列 (Step 1 DEFAULT 付き ALTER + Step 2 DROP DEFAULT) + 複合 index 追加 / vlt/src/retention-manager/lock-transaction-boundary.ts (= lock 保持中の delete 手続き) の deleteReceiptWithLock 内に AUDIT_LOG_CALLER runtime gate + audit_log INSERT + TODO(phase1-migration) コメント追加 / vlt/terraform/modules/vlt-cloud-run/main.tf (= vlt Cloud Run 定義) に env var AUDIT_LOG_CALLER='lock-transaction-boundary' 追加 / vlt/tests/e2e/sweep-audit-atomicity.test.ts の対象 module を 5 module 統合 pipeline に差替 + 4 case 新規追加 (GCS 削除失敗 · hold gate 評価失敗 · audit_log INSERT 失敗 · GCS 削除成功 → Postgres COMMIT 失敗) / vlt/src/db/sweep.ts 削除 / vlt/tests/integration/retention-manager-pipeline.test.ts (8 case) と vlt/tests/integration/retention-manager-main.test.ts (5 case) に audit_log INSERT の assertion 追加 / PR review checklist に「5 module 固有障害 4 case の追加が無い場合 merge 不可」を明記。加えて tasks/prompts/draft_adr_gcs-delete-after-commit-compensation.md を新規起票し、GCS delete を COMMIT 後の補償ワーカーへ移送する恒久解決を別 ADR で扱う (§2.3 潜在リスク 信頼性の恒久解決)。Phase 1 event-emitter 有効化と amend 解除の実装 SSoT は ADR-0222 §3.3 · ADR-0223 §3.3 · 本 ADR §8 撤退条件 (1) の 3 者連動とする。
  • docs SSoT 同期: docs/_internal/06_ops/adr-<受理後 NNNN>-observation-log.md (= 本 ADR の週次観測ログ) を新設 / docs/_internal/06_ops/jiima-audit-data-lineage.md (= JIIMA 審査用データ来歴説明書) に「audit_log の phase / caller 列は本 ADR 受理時追加、既存行は pre-0 / sweep.ts に補正済み」を明記 / docs/adr/adr-index.json + docs/adr/INDEX.md を再生成 / docs/adr/0223-specify-retention-manager-operational-implementation.md §3.3 仕様に「Phase 0 期間限定で lock-transaction-boundary 内 audit_log INSERT を許容する」amend note を追加 / docs/architecture/arch_jtbd012_voucher_saas_draft.md の Retention Manager 節に「Phase 0 lock-transaction-boundary → Phase 1 event-emitter」の caller 遷移を追記。