# 官方 KBDB 誤寫清理 SOP（issue #3 待辦 1）

> **狀態**：runbook 已備妥，**待官方運營方（leo）親自執行**。
> **為什麼不由 CC 直接跑**：對官方 prod D1 執行不可逆 `DELETE`，需官方憑證 + 人類明示確認
> （mindset §7「絕不代替人類做有風險的確認」；rule 06）。CC 只備妥可審、防誤刪的腳本，DELETE 由人按下。
> **來源**：issue #3（leo 2026-06-24 拍板，14-E 遷移善後）。根因 bug 已修（issue #2，commit 9c4333d）。

---

## 背景

14-E 遷移期間（issue #2 的 `KBDB_BASE_URL` fallback bug 修好**之前**），mira 的 `_kbdb_client.py`
把 **~11 萬筆 `owner_id='leo'`** 的資料誤寫進**官方 prod kbdb**（`arcrun-kbdb`，非 leo21c self-hosted）。

- **歸屬**：官方 SaaS 庫的清理 = arcrun 官方運營方的事，不是 mira（用戶）。讓用戶拿官方憑證 DELETE 官方 prod 本身違反隔離。
- **重要性**：SaaS 尚未營運，這批誤寫資料不重要 → 可刪。但**官方 prod DELETE 不可逆 → 必須防誤刪**。

---

## 目標庫（精確座標）

| 項目 | 值 |
|------|-----|
| Worker / DB name | `arcrun-kbdb` |
| D1 database_id | `0c580910-e00b-4f8e-9c57-ac54ea52242f`（官方 prod，見 `kbdb/wrangler.toml:13`） |
| 官方 CF account | `58309bb9…`（記憶 [[cf-account-official-vs-loadtest]]） |
| 誤寫標記 | `entries.owner_id = 'leo'` |

⚠️ **帳號對齊**：執行前確認本機 wrangler 對的是**官方帳號**（不是 leo21c）。
`wrangler whoami` 應顯示官方 uncle6.me account。誤寫在官方庫，所以這次**就是要對官方帳號**操作
（與 self-hosted 部署相反，那邊要避開官方——見記憶 [[selfhosted-deploy-account-override-trap]]）。

---

## 表關係（決定刪除範圍）

base 三表（`kbdb/migrations/0001_base.sql`）：
- `entries`：主表，誤寫資料在這（`owner_id='leo'`）。
- `entry_values`：slot-link，`entry_id REFERENCES entries(id)`。若 leo 資料含 record（用 template 組的結構化資料），
  其 slot 連結在這。**刪 entries 會留下孤兒 entry_values** → 要一併清。
- `templates`：`created_by` 可能是 `'leo'`。**先確認** leo 有沒有建 template（待辦 2 步驟會查），
  template 較可能是共享/誤建，刪前單獨核實。

---

## SOP（逐步，每步有 gate，防誤刪）

> 全程用 `wrangler d1 execute arcrun-kbdb --remote --command "..."`。**`--remote` 不可漏**（漏了打本地空庫，假綠）。

### 步驟 0：帳號 + 庫核實（gate）

```bash
wrangler whoami                       # 確認 = 官方 uncle6.me account（58309bb9）
wrangler d1 info arcrun-kbdb          # 確認 database_id = 0c580910...
```

### 步驟 1：備份（整庫導出，防誤刪的底氣）

```bash
wrangler d1 export arcrun-kbdb --remote --output backup-before-cleanup-2026-06-24.sql
ls -lh backup-before-cleanup-2026-06-24.sql   # 確認檔案非空、大小合理
```

### 步驟 2：標記 / 核實刪除範圍（**最關鍵的 gate**）

```bash
# 2a. 誤寫總數（應 ~11 萬）
wrangler d1 execute arcrun-kbdb --remote --command \
  "SELECT count(*) AS leo_entries FROM entries WHERE owner_id='leo';"

# 2b. 關鍵確認：owner_id='leo' 是否只有這批誤寫，有沒有別的 leo 真資料混入
#     看 entry_type 分布 + 時間範圍（誤寫應集中在 14-E 遷移那段時間）
wrangler d1 execute arcrun-kbdb --remote --command \
  "SELECT entry_type, count(*) AS n, min(created_at) AS first_at, max(created_at) AS last_at \
   FROM entries WHERE owner_id='leo' GROUP BY entry_type ORDER BY n DESC;"

# 2c. 孤兒 entry_values（會被 entries 刪除留下的）
wrangler d1 execute arcrun-kbdb --remote --command \
  "SELECT count(*) AS leo_entry_values FROM entry_values \
   WHERE entry_id IN (SELECT id FROM entries WHERE owner_id='leo');"

# 2d. leo 建的 template（單獨核實，勿盲刪——可能是共享/系統 template 誤標）
wrangler d1 execute arcrun-kbdb --remote --command \
  "SELECT id, name, created_by FROM templates WHERE created_by='leo';"
```

**Gate 判定**（人類看數字決定）：
- 2a count ≈ 11 萬、2b 時間集中在遷移期 → 範圍乾淨，可進步驟 3。
- 若 2b 出現非遷移期、或 entry_type 異常 → **停手**，逐筆核實，別整批刪。
- 2d 若有 template → 個別判斷是否該刪（template 通常想保留，除非確認是誤建）。

### 步驟 3：刪除（確認範圍乾淨後）

```bash
# 3a. 先刪孤兒 entry_values（外鍵指向即將被刪的 entries）
wrangler d1 execute arcrun-kbdb --remote --command \
  "DELETE FROM entry_values WHERE entry_id IN (SELECT id FROM entries WHERE owner_id='leo');"

# 3b. 再刪 entries
wrangler d1 execute arcrun-kbdb --remote --command \
  "DELETE FROM entries WHERE owner_id='leo';"

# 3c.（可選，僅當步驟 2d 確認某 template 是誤建才刪）
# wrangler d1 execute arcrun-kbdb --remote --command \
#   "DELETE FROM templates WHERE created_by='leo' AND id='<確認過的 id>';"
```

### 步驟 4：驗證（刪後核實）

```bash
# 4a. leo 誤寫歸零
wrangler d1 execute arcrun-kbdb --remote --command \
  "SELECT count(*) AS remaining_leo FROM entries WHERE owner_id='leo';"   # 應 = 0

# 4b. 孤兒 entry_values 歸零
wrangler d1 execute arcrun-kbdb --remote --command \
  "SELECT count(*) AS orphan_ev FROM entry_values \
   WHERE entry_id NOT IN (SELECT id FROM entries);"   # 應 = 0

# 4c. 官方庫其餘資料不受影響（總 entries 數應 = 刪除前總數 - 11 萬）
wrangler d1 execute arcrun-kbdb --remote --command \
  "SELECT owner_id, count(*) AS n FROM entries GROUP BY owner_id ORDER BY n DESC;"
```

### 步驟 5：收尾

- 4a/4b/4c 符合預期 → 清理完成。
- 備份檔（`backup-before-cleanup-2026-06-24.sql`）可棄（SaaS 未營運，無長期保留必要）。
- 在 issue #3 comment 回報：刪除數量 + 驗證 count + 確認官方庫其餘不受影響。

---

## 為什麼這樣設計（防誤刪三道閘）

1. **備份先行**（步驟 1）：DELETE 不可逆，先有 d1 export 的整庫快照當底氣。
2. **核實再刪**（步驟 2 gate）：不盲信「~11 萬都是誤寫」，看 entry_type + 時間分布確認範圍乾淨，
   排除別的 leo 真資料混入（issue #3 待辦 1 步驟 2 的「關鍵確認」）。
3. **驗證歸零**（步驟 4）：刪後客觀證據（count=0 + 孤兒=0 + 其餘不受影響），不靠「跑完了」口頭宣布
   （mindset §7 完成=客觀證據）。

## 待辦 2（願景）落點

`acr migrate` 一等公民雙向遷移已記頂層 `docs/1-vision/product-wishlist.md` C7 + 本 repo backlog，不急做。
詳見 issue #3 待辦 2。
