# CR-028　hold 事件群四項矛盾之化解（`CR-027 §5 #1～#4`）

| 項目 | 內容 |
|---|---|
| 文件編號 | CR-028 |
| 版本 | **1.0** |
| 提出日 | 2026-10-01 |
| 提出者 | WorkBuddy AI（開發團隊） |
| 緣起 | BEAM PMO 指示「批次 B（hold 事件群）動工前必須先裁定 `CR-027 §5 #1～#4`，**請一一化解**，不用急。我不求快，但求精準無誤」。本 CR 即為該四項之**化解方案 ＋ 執行紀錄** |
| 影響範圍 | `SAD-002 §3.1`／`§3.2`、`SAD-004 §5.2`、`SDD-006 §4.2`、`GOV-001 §4.4`、`SCS-002 §7.6` |
| 狀態 | **✅ 已化解並執行（文件層）**；**程式碼零影響**（見 §5） |

---

## 1. 化解方法（先查證、再擬案、後動手）

BEAM PMO 要求「**精準無誤**」，故本次**不憑文件互相比較就下結論**，而是先取**客觀證據**：

| 證據來源 | 取法 |
|---|---|
| 程式碼是否曾實作該事件 | 全庫搜尋 `authorization.hold.` |
| payload 是否實質相同 | 比對 `SDD-006 §4.2` 之兩列 |
| 權威欄位是否足以支撐 | 直接查 `user_tab_columns`（真實 Oracle） |

---

## 2. 查證結果（**四項證據，皆可複驗**）

### 證據 A｜`authorization.hold.*` **全庫程式碼零引用**

搜尋 `authorization\.hold\.` 於 `cardcore/` 全庫，命中**僅 5 處，全在文件**：

| 位置 | 內容 |
|---|---|
| `SAD-002 §3.1`（行 89） | authorization 之「事件（發布）」清單 |
| `SAD-004 §5.2`（行 302／303） | 事件清單兩列 |
| `SDD-006 §4.2`（行 409／410） | 事件 payload 兩列 |
| `GOV-001 §4.4`（行 165） | **命名範例**（非契約） |

→ **零程式引用**：`AuthorizeUseCase` 從未發射此二事件；`limit-service` 從未訂閱。
→ **故廢除之「程式影響」為零**（僅文件需修）。

### 證據 B｜`authorization.hold.created.v1` 與 `limit.hold.created.v1` **是同一件事**

`SDD-006 §4.2` 之 `authorization.hold.created.v1` payload ＝ `holdId`, `accountId`, `holdAmount`, `holdType`。
而 `SAD-002 §3.2` 稱 limit 發布 `limit.hold.created.v1`——**本專案 `#A-122` 已實作**，其 payload（依 `SCS-002 §7.6`）**逐欄相同**。

→ **同一語意、兩個前綴**；`limit.hold.created.v1` 從未被登錄於 `SDD-006 §4.2`（**清單漏列**）。

### 證據 C｜`limit_holds` 之權威在 `CC_LIM`

| 表 | 服務 | 性質 |
|---|---|---|
| `CC_LIM.LIMIT_HOLDS` | limit-service | **權威**（`SAD-002 §3.2`／`SCS-002 §8`：「本埠為唯一權威出入口」） |
| `CC_AUTH.LIMIT_HOLDS` | authorization-service | **本地投影**（`V2__read_model.sql` 明文：「權威在 `CC_LIM.LIMIT_HOLDS`」） |

→ **hold 之事實由 limit 產生**；authorization 只能**消費**，不能發布。

### 證據 D｜兩張 `limit_holds` **皆有 `AUTHORIZATION_ID`**

| 表 | 欄位（實查 `user_tab_columns`） |
|---|---|
| `CC_LIM.LIMIT_HOLDS` | `ID`／`ACCOUNT_ID`／**`AUTHORIZATION_ID`**／`HOLD_AMOUNT`／`RELEASED_AMOUNT`／`HOLD_TYPE`／`STATUS`／`EXPIRES_AT`／`CREATED_AT`／`UPDATED_AT`／`VERSION` |
| `CC_AUTH.LIMIT_HOLDS` | `ID`／`ACCOUNT_ID`／**`AUTHORIZATION_ID`**／`CURRENCY`／`HOLD_AMOUNT`／`RELEASED_AMOUNT`／`STATUS`／`EXPIRES_AT`／`CREATED_AT`／`UPDATED_AT`／`VERSION` |

→ limit **有能力**提供 `authorizationId`；authorization **需要**它以維護本地投影。

---

## 3. 逐項化解

### #1 hold 事件之方向與權威 → **定案：廢除 `authorization.hold.*`，只保留 `limit.hold.*`**

| 項目 | 內容 |
|---|---|
| **矛盾** | `SAD-002 §3.1` 稱 authorization 發布 `authorization.hold.created.v1`／`.released.v1`；`§3.2` 稱 limit 發布 `limit.hold.created.v1`／`.released.v1`——**同一事實兩個生產者** |
| **證據** | A（零程式引用）、B（payload 逐欄相同）、C（權威在 `CC_LIM`） |
| **定案** | **(a) 廢除 `authorization.hold.created.v1`／`authorization.hold.released.v1`**；hold 事件**一律由 limit-service 發布**（`limit.hold.created.v1`／`limit.hold.released.v1`） |
| **理由** | ① `authorization.hold.created.v1` 與 `limit.hold.created.v1` **是同一件事**——保留兩個名字必然造成「同一事實兩則事件」之重複消費；② authorization **不是 hold 之權威**（`CC_LIM.LIMIT_HOLDS` 才是），由其發布「佔用已建立」是**事實歸屬錯誤**；③ authorization 對 limit 之佔用請求是**同步 API**（`POST /limit-holds`）——那是**命令**，不是事件（`GOV-001 §4.4`：事件為**過去式既成事實**） |
| **⚠️ 一併釐清** | `authorization.hold.released.v1` 之 payload 為 `holdId, releasedAmount, reason`（**含 `reason`**），與 `limit.hold.released.v1`（`holdId, accountId, releasedAmount`）**不同欄**。惟「**帶原因**」正顯示其為**命令**（「請釋放，原因為 X」）——原因屬**請求參數**，非事件事實。**故一併廢除** |
| **影響** | 文件 4 處（`SAD-002 §3.1`、`SAD-004 §5.2`、`SDD-006 §4.2`、`GOV-001 §4.4` 範例）；**程式 0 處** |

### #2 authorization 訂閱清單 → **定案：以 `SAD-002` 為準，auth 訂閱 `limit.hold.created.v1`**

| 項目 | 內容 |
|---|---|
| **矛盾** | `SAD-002 §3.1` 之 auth 訂閱清單**含** `limit.hold.created.v1`；`SAD-004 §5.2` 之事件清單**無**此事件（僅有 `limit.hold.released.v1`） |
| **證據** | C（auth 有本地投影 `CC_AUTH.LIMIT_HOLDS`，需被餵養） |
| **定案** | **以 `SAD-002` 為準**——auth 訂閱 `limit.hold.created.v1`；**並補登**至 `SAD-004 §5.2` |
| **理由** | 若只訂閱 `released` 而不知 `created`，消費者會「**釋放一筆不存在的佔用**」——本地投影將永遠缺列（**與 `card_status_caches` 曾恆空之缺陷同型**） |

### #3 `limit.hold.released.v1` 之消費者 → **定案：消費者為 authorization（與 #2 同批處理）**

| 項目 | 內容 |
|---|---|
| **矛盾** | `SAD-004 §5.2` 載消費者＝authorization；`SAD-002 §3.1` 之 auth 訂閱清單**未含**它 |
| **定案** | **補進 `SAD-002 §3.1`**——auth 訂閱 `limit.hold.created.v1` **與** `limit.hold.released.v1` |
| **理由** | 二者為**同一本地投影之生命週期**（建立→釋放），拆開訂閱必造成投影狀態落後（如 `released_amount` 永不更新） |

### #4 `limit.hold.created.v1` payload 缺 `authorizationId` → **定案：補為「可選」欄位，不升版**

| 項目 | 內容 |
|---|---|
| **矛盾** | `SDD-006 §4.2` 之 `authorization.hold.created.v1` payload 為 `holdId, accountId, holdAmount, holdType`——**無 `authorizationId`**，惟 authorization 之本地投影有 `AUTHORIZATION_ID` 欄位 |
| **證據** | D（**兩張表皆有該欄位**） |
| **定案** | **(b) 補 `authorizationId` 為可選欄位**（`SAD-004 §5.3`：「新增**可選**欄位 → **不升版**」）；併**登錄 `limit.hold.created.v1` 之完整 payload**（含 `authorizationId`） |
| **理由** | ① limit **有能力**提供（權威表有該欄）；② authorization **需要**它（否則無法把 hold 對應回自己的授權）；③ 為**可選**——`stand-in` 等無對應授權之情境可為 `null`，故**不升 `v2`**、無雙軌成本 |
| **影響** | `SCS-002 §7.6`（改「刻意不納入」→「可選」）＋ `LimitEventFactory`（**下一輪實作**，屬批次 B） |

---

## 4. 需同步修訂之文件（**本 CR 已執行**）

| # | 文件 | 修訂 |
|---|---|---|
| 1 | `SAD-002 §3.1` | 「事件（發布）」**移除** `authorization.hold.created.v1`／`.released.v1`；「事件（訂閱）」**補** `limit.hold.created.v1`、`limit.hold.released.v1` |
| 2 | `SAD-002 §3.2` | 「事件（發布）」**補** `limit.hold.created.v1`（原已列 `.released.v1`） |
| 3 | `SAD-004 §5.2` | **刪**兩列 `authorization.hold.*`；**補** `limit.hold.created.v1` 一列（生產者 limit、消費者 authorization） |
| 4 | `SDD-006 §4.2` | **刪**兩列 `authorization.hold.*`；**補** `limit.hold.created.v1`（含 `authorizationId` 可選） |
| 5 | `GOV-001 §4.4` | 命名範例 `authorization.hold.created.v1` → **`limit.hold.created.v1`** |
| 6 | `SCS-002 §7.6` | `authorizationId` 由「**刻意不納入**」改為「**可選欄位**」；移除「`SAD-002 §3.1` 矛盾待裁定」之註記 |

---

## 5. 對程式碼之影響：**零**

| 項目 | 說明 |
|---|---|
| `authorization-service` | **無**——從未發射 `authorization.hold.*`（證據 A） |
| `limit-service` | 已實作 `limit.hold.created.v1`／`limit.hold.released.v1`（`#A-122`）→ **本 CR 不動程式**；僅 `authorizationId` 之補入屬**批次 B** |
| 其他服務 | **無** |

> ⚠️ 故本 CR **無需重編譯、無需真機驗證**——**這是文件層之化解**。
> （符合 `GOV-003` 條件 9：「先有規格、才寫程式」——此處**連程式都不需改**。）

---

## 6. 化解後之批次 B 藍圖（供下一輪動工）

| 步驟 | 內容 |
|---|---|
| ① 規格先行 | `SCS-002 §7.6` 補 `authorizationId`（**已於本 CR 完成**）；`SCS-001 §7.15` 新增「佔用事件消費」 |
| ② 生產端 | `LimitEventFactory.holdCreatedEnvelope` 補 `authorizationId`（**可選**，由 `LimitApplicationService.hold` 傳入） |
| ③ 消費端 | `authorization-service` 消費 `cc.limit.limit.hold.created`／`.released` → upsert 本地 `limit_holds`（**冪等＋亂序**，沿用 `consumed_events`） |
| ④ 真機 | 建立佔用 → `limit_holds` 本地投影落列；部分釋放 → `released_amount` 更新 |

---

## 7. 未竟（誠實揭露）

1. **本 CR 未動任何程式**——批次 B 之實作（含 `authorizationId` 之補入）**尚待動工**。
2. **`limit.hold.created.v1` 之消費者尚未實作** → 現階段 `CC_AUTH.LIMIT_HOLDS` **仍無餵養者**（僅 stand-in 路徑寫入）。
3. **`CR-027 §5` 其餘 8 項**（#5～#12）**仍待裁定**——其中 **#5（事件清單權威）** 與本 CR 相關：
   本 CR 已將 `limit.hold.created.v1` **補登**至 `SAD-004 §5.2`，惟**兩份清單是否合併為單一權威**仍待 BEAM PMO 裁定。

---

## 8. 修訂記錄

| 版本 | 說明 |
|---|---|
| 1.0 | 初版：hold 事件群四項矛盾之化解——**先取四項客觀證據**（零程式引用／payload 逐欄相同／權威在 `CC_LIM`／兩表皆有 `AUTHORIZATION_ID`），再逐項定案；**已執行 6 份文件之同步修訂**；**程式碼零影響** |
