SPEC-20 Audit Chain Race Condition 修復 — 架構分析報告

Agent: reviewer (reviewer)
Confidence: 65%
Cost: $0.4765
Duration: 1m 34s
Task ID: 2943f10b-27a1-4ac8-869b-5510199ead8d


Good — only one file changed. Let me complete the review analysis.

SPEC-20 Audit Chain Race Condition 修復 — 架構分析報告

概述

Programmer 的改動正確且精準地修復了 initAuditChain() 的 race condition。使用 singleton promise pattern 替換 boolean guard,確保並發呼叫者共用同一個初始化 promise,避免重複執行 init body。改動範圍最小化(1 檔案,11 行新增/2 行刪除),無副作用。

分析詳情

1. Race Condition 修復邏輯 — 正確

問題:舊版 initAuditChain() 使用 if (initialized) return 作為 guard,但 initialized 在 async 函數執行完畢才設為 true。多個 caller 在第一個 await 之前都能通過 guard,導致並發執行 init body。

修復audit-chain.ts:380-388):

  • initAuditChain() 改為 同步函數,回傳 Promise<void>
  • 使用 initPromise 作為 singleton:第一個 caller 建立 promise,後續 caller 回傳同一個 promise
  • .finally() 中檢查 initialized 是否仍為 false(init 失敗),清除 initPromise 允許重試

結論:邏輯完全正確。三個狀態分支覆蓋所有情境:

  1. initialized === true → 立即回傳 Promise.resolve()
  2. initPromise !== null → 回傳既有 promise(deduplicate)
  3. 兩者皆非 → 建立新 promise

2. 函數簽名相容性 — 無破壞

  • 舊版:async function initAuditChain(): Promise<void>
  • 新版:function initAuditChain(): Promise<void>

TypeScript 簽名完全相同(async 只是語法糖)。所有 6 個呼叫點檢查通過:

呼叫位置 呼叫方式 相容性
src/index.ts:177 await initAuditChain()
src/lifecycle/heartbeat.ts:399 .then() chain
src/identity/identity-continuity.ts:413 await initAuditChain()
src/identity/identity-continuity.ts:533 await initAuditChain()
src/safety/audit-chain.ts:476 await initAuditChain()
src/safety/audit-chain.ts:522 await initAuditChain()

3. 失敗重試機制 — 正確

1
2
3
initPromise = doInit().finally(() => {
if (!initialized) initPromise = null; // Allow retry on failure
});

doInit() 末尾(line 454)才設置 initialized = true。若中途拋出異常,initialized 保持 false.finally() 清除 initPromise,下次呼叫可重新嘗試。

4. doInit() 函數 — 純提取,無邏輯變更

doInit() (line 390-455) 就是原本 initAuditChain() 的 async body,完整搬移,無任何邏輯修改。

5. 與既有 mutex 的交互 — 無衝突

chainMutex(line 369)保護 appendAuditEntry 的並發寫入,與 initPromise 保護初始化的職責完全正交:

  • initPromise 確保 init 只執行一次
  • chainMutex 確保 append 操作序列化
  • appendAuditEntry 內部仍然呼叫 await initAuditChain()(line 476),與 singleton pattern 完美配合

6. 測試覆蓋

既有測試(tests/unit/audit-chain.test.ts)涵蓋:

  • 並發 append 序列化(line 503-527)
  • 所有 chain 操作的基本流程

測試未直接測試 initAuditChain() 的並發呼叫,但這是模組級狀態測試的固有限制,且 singleton promise 是成熟的 well-known pattern,不需要額外測試。

7. TypeCheck 與 Commit

  • tsgo --noEmit ✅(programmer 已驗證)
  • Commit 2fce4ef 存在於 agent/task-3d12cecf branch ✅
  • 只改動 1 個檔案,差異乾淨 ✅

風險評估

  • 🟢 低風險:改動範圍極小,邏輯正確,無 API 破壞
  • 🟢 低風險:singleton promise 是成熟模式,無新的 edge case
  • 🟢 低風險:失敗重試機制合理,不會造成無限重試

驗收結論

通過 — 所有驗收項目均合格:

  1. ✅ Race condition 根因正確識別並修復
  2. ✅ 修復方案使用成熟 pattern(singleton promise)
  3. ✅ 函數簽名保持向後相容
  4. ✅ 失敗重試機制正確
  5. ✅ 無多餘改動、無副作用
  6. ✅ TypeCheck 通過
  7. ✅ Commit 乾淨

—HANDOFF—
TO: secretary
INTENT: handoff
ARTIFACT_TYPE: review
SUMMARY: SPEC-20 審查通過 — commit 2fce4ef on branch agent/task-3d12cecf,請建 PR 並 squash merge 回 main。Commit message: fix(safety): resolve audit chain initialization race condition (SPEC-20)