# Tasks: KBDB Base — atomic universal table

> 對應 design.md。design 待 richblack review 後才動 code。每完成一個 task 立刻標 [x]，不批次。
> 來源：壓測報告暴露 self-hosted 無資料層；richblack 2026-06-07 拍板 KBDB 基礎開源 + 插件模式。

---

## 狀態：SDD 草稿，待 review design + 拍板開放問題後才動 code

- [x] Q1 專案維度[拍板 2026-06-07]：richblack 授權 CC 決定，參考 KBDB 既有實作（已有 project/parent 做法）
- [x] Q2 KV vs D1 分工[拍板 2026-06-07]：KV 不全廢，按特性分——workflow YAML+recipe+成功記錄進 D1；session/verdict/credential 留 KV
- [x] Q3 triplet 接點[拍板 2026-06-07]：triplet = template + 函式。手動（給文字回三元組）或自動（block 存入時叫起，安裝時同意）；提供 MCP（AI-friendly，交貨帶 MCP）
- [x] Q4 D1 免費不綁卡[查證 2026-06-07 CF docs]：Workers Free 可用、不需信用卡（不像 R2）。額度 5M 讀/日、100K 寫/日、5GB。用量大時 CF 自會提醒用戶。

## Phase 0：基礎抽取與解耦

- [x] 0.1 三表 migration 抽出 arcrun/kbdb/migrations/0001_base.sql（entries/templates/entry_values + recipe_stat template seed），無 triplet/entity/vectorize 表
- [x] 0.2 萬年不動：新基礎 schema 本就不含 entity_type（那是 triplet 衍生欄位）→ 基礎 entries 無此欄，衍生屬性天然在基礎表外。原 kbdb 的 UPDATE blocks SET entity_type 屬 triplet 模組（獨立 repo），不影響 arcrun 基礎
- [x] 0.3 解耦：arcrun/kbdb 從零寫乾淨基礎（entry/template/record CRUD），不 import 任何 embed/triplet（grep 證）；不去動原 kbdb repo（官方版仍用，動它有風險）
- [x] 0.4 templates/records route 純淨：只依賴 record-crud（D1），無 vectorize/triplet
- [x] 0.5 基礎 search = D1 LIKE（GET /entries/search?q=，mode:keyword）；語義層留給 embed 模組（未在基礎）

## Phase 1：embed 模組（CF 內建，binding 開/關，不拆 repo）

> 狀態：基礎已與 embed 完全解耦（不 import）。embed 模組本身（Vectorize upsert + 語義 search）尚未實作——基礎不依賴它即可，embed 待要用時再加。以下 1.x 未做。

- [ ] 1.1 embed 改可選 hook：有 env.VECTORIZE 才掛
- [ ] 1.2 search 語義層：有 Vectorize 啟用，否則降級 LIKE，API 不變
- [ ] 1.3 wrangler.toml：Vectorize/AI binding 可選

## Phase 2：import 進 arcrun（如 MCP pattern）

- [x] 2.1 基礎 KBDB Base 搬進 arcrun/kbdb/（Worker + D1）：kbdb/ 已建（三表 CRUD + recipe-stats + migration），納入 find wrangler.toml deploy 掃描
- [x] 2.2 cf-api.ts 加 D1 建立（listD1Databases/ensureD1Database）；deploy.ts 注入 d1DatabaseId 到 kbdb wrangler.toml
- [x] 2.3 self-hosted init 建 KV + D1（Q4 已確認免綁卡）：init ensureD1Database 建空 D1 → deploy 部署完對 D1 套 migrations/0001_base.sql（CF /d1/query API，idempotent）建三表 + recipe_stat seed + 注入 wrangler.toml database_id
- [ ] 2.4 cypher 改用 KBDB 存專案/工作流歸屬（依 Q1/Q2，漸進）— 未做（後續）

## Phase 3：triplet 模組（獨立 repo，本 SDD 只定接點）

- [ ] 3.1 確認基礎不依賴 triplet（Phase 0.3 後驗證）
- [ ] 3.2 定義 triplet 掛上基礎的接點（依 Q3）
- [ ] 3.3 triplet 本身 → 獨立 repo，不在本 SDD

## Phase 4：官方共用基礎（方向，搬遷另排）

- [ ] 4.1 官方版改用同一份基礎 KBDB（binding 全開）→ 不維護兩套

## Phase 5：recipe 投稿 + 帶 KBDB 成功記錄（免官方驗證）—— 本次要做（design 7）

- [x] 5.1 recipe 成功記錄落地 D1：判定單位=工作流執行（n8n execution）。GraphExecutor 收集本次用到的 recipe key（usedRecipeKeys=API recipe uuid，§7.5.h 改 per-uuid；舊資料 fallback canonical_id）；executeWebhookGraph 執行結束後一次性 POST KBDB /recipe-stats/record（整體成功→各+1成功、真錯非paused→各+1失敗，fire-and-forget via waitUntil）。cypher tsc exit 0
- [x] 5.2 投稿端點（cypher recipes.ts）：POST /recipes/submit（需 exposure_consent、stat 存證不當門檻）。**已修正**（7.5.f）：原「覆蓋同 canonical_id」→ app-store/UUID 模型「領新 uuid 新增作者版本」，同 canonical 多作者並存。cypher tsc exit 0
- [x] 5.3 CLI 接點（= 7.5.b/c/e）：acr recipe push（私庫）/ pull（公→私）/ submit-p（公共庫帶暴露同意）。cli tsc exit 0
- [x] 5.4 市場機制（靠量/星數模型，design §7.3）：stat 存證已做（5.2 recipe_submission entry）+ per-uuid 市場數據（5.1+7.5.h）。先市場優先、暫不強防偽（5.6 後續）
- [ ] 5.5 市場同步（跨環境聚合層，§7.3 主體）：self-hosted 各用戶真實成功/失敗匯總回公共市場 → recipe 星數/信用。5.1 已在各環境記錄，聚合另排
- [ ] 5.6 強防偽（只在市場失靈才做）：官方 in-process 重跑投稿者工作流拿官方自己 2xx / 第二投稿者獨立回報；版本並存/舊版下架

## Phase 7.5：公庫/私庫雙向機制（CHANGE，design §7.5）—— richblack 2026-06-07 review 通過，可執行

> richblack 指示「把兩庫所有情境想好」+「這些細節想好是可以執行」→ §7.5 review 通過，開始動 code。
> **7.5.5 拍板：recipe = 具名作者作品（app-store 模型）**——同 canonical_id 多作者並存、只有作者+admin 能改、
> 投稿=新增作者版本（非覆蓋）、向後相容不刪舊、選擇由市場數據驅動、無「公庫改私庫要不要跟」問題。
> **7.5.6 拍板：搜尋落空=創作入口**——公庫沒有→回 found:false 創作引導→CC 現做成為作者→投稿補上（閉環）。

- [x] 7.5.g 落空創作引導：GET /public-recipes(?q=) 與 GET /public-recipes/:canonical_id 落空回 { found:false, hint } 創作引導，不回空陣列。cypher tsc exit 0
- [x] 7.5.a 公庫只讀端點（cypher recipes.ts）：GET /public-recipes?q=&limit=&offset=（list/search 多作者+per-uuid 市場星數）、GET /public-recipes/:canonical_id?author=（pull 取全文，多作者選市場最佳）。cypher tsc exit 0
- [x] 7.5.h **市場星數 per-uuid（§7.5.h）**：5.1 改收集 API recipe uuid（resolveRecipe，非 resolveAuthRecipe）；usedRecipeServices→usedRecipeKeys；KBDB 星數記 per-uuid；public-recipes 選最佳 per-uuid 查 → 真正區分 Leo/John 版。cypher tsc exit 0

- [x] 7.5.f **recipe UUID 身份模型（app-store 核心，§7.5.5 拍板）**：recipes.ts RecipeDefinition 加 uuid/author/derived_from；installRecipeRecord helper（recipe:{uuid} + idx:canonical 清單 + idx:installed + idx:hash）；POST /recipes=私庫沿用 installed uuid 就地更新；POST /recipes/submit=領新 uuid 新增作者版本（非覆蓋）；resolveRecipe 向後相容（uuid→installed→fallback 舊 key，不破執行鏈）；DELETE 清 uuid+索引；GET dedup；init-seed 用 UUID（author=system）；POST /recipes/migrate-uuid 一次性轉舊 key（增量寫不刪舊、冪等）。cypher tsc exit 0。重複靠市場淘汰、不做檢舉、CC 不檢舉（§7.3+mindset §7）
- [x] 7.5.b 公→私 pull（CLI 薄殼）：acr recipe pull <canonical_id> [--author] → GET 公庫 /public-recipes/:id → POST 自己私庫 /recipes（帶 derived_from 溯源）。cli tsc exit 0
- [x] 7.5.c 公庫搜尋（CLI 薄殼）：acr recipe search <q> → GET 公庫 /public-recipes?q=，印多作者+市場數據，落空印創作引導。cli tsc exit 0
- [x] 7.5.e submit-p（CLI 薄殼）：acr recipe submit-p <canonical_id> [--author] → GET 私庫取全文 → 暴露同意 → POST 公庫 /recipes/submit（新增作者版本）。config 加 DEFAULT_PUBLIC_LIBRARY_URL（公庫=官方 cypher，ARCRUN_PUBLIC_LIBRARY_URL 可覆蓋）。cli tsc exit 0
- [x] 7.5.i **MCP 薄殼補齊 recipe 工具（rule 07 §5）**：新增 mcp/src/tools/arcrun_recipe.ts 六工具 arcrun_recipe_search/pull/submit_p/push/list/delete（registerAllRecipeTools 註冊進 registry.ts），全用 cypherFetch 薄殼模式（無業務邏輯）。與 CLI 六能力對齊，MCP 不再落後。submit_p 帶 exposure_consent 把關。mcp tsc exit 0。註：MCP 連平台 cypher（§5.2 account-source 已知違反 pre-existing，沿用既有模式不一併修）

## Phase 8：KV list 上限威脅免費承諾 → 高頻 list 遷 D1（CHANGE，design §8）—— 待 richblack review 分期顆粒度，勿動 code

> 2026-06-08 richblack CF 帳號收到 KV list 超標信（每日 1000 上限，429）。威脅免費核心承諾。
> 方向拍板：高頻 list 遷 D1（對齊 §6 Q2）。根因①cron 每分鐘 list（1440/日單獨就爆）②§7.5 新加的 recipe 搜尋 list。
> 待 review §8 分期顆粒度後才動 code。

- [x] 8.P0 **緊急止血 cron**（§8.2）：scheduled.ts 每分鐘 WEBHOOKS.list('cron-idx:') → 改單一固定 key（cron-idx:_all 存 {apiKey}:{name}→cron_expr map）只 get 一次。新增 lib/cron-index.ts（readCronIndex/updateCronIndexEntry，單 key read-modify-write）；webhooks-named POST/DELETE 改維護單 key；新增一次性 POST /webhooks/named/migrate-cron-index 把舊 per-key 折進集中 key（冪等、不刪舊）；acr update 部署後自動呼叫 migrate（接在 seed 後，冪等、失敗不致命）→ 既有 cron 不必手動重 push。1440 list/日 → 0 list。cypher+cli tsc exit 0。**已完成 2026-06-09**
- [ ] 8.P1 recipe 查詢遷 D1（§8.3）：public-recipes 搜尋 + GET /recipes list（§7.5 新加的元兇）改 D1 query（LIKE/WHERE，多作者/市場排序天生適合 SQL）
- [ ] 8.P2 workflow list 遷 D1：webhooks-list/webhooks-named 的 list 改 D1 query（entry_type=workflow）
- [ ] 8.P3 recipe/workflow 本體完整遷 D1：resolveRecipe / KV→D1 讀寫 + migration + 雙寫過渡（KV 舊資料相容）。session/credential/exec-context 留 KV（§6 Q2）

## Phase 9：KBDB 資料層薄殼補 MCP/CLI（HANDOFF §2，design §4「交貨帶 MCP」）—— 本次做

> 核實：CLI/MCP 現在完全沒 KBDB 資料層能力（既有 arcrun_skills_examples 打的是舊 /blocks /search
> v3 schema，非本基本盤三表）。基本盤 API 已完整（templates/records/entries/search）。
> 本 Phase = **薄殼暴露**，不重寫能力（rule 07）。
> **KBDB 鐵律（leo 2026-06-14）**：不提供建表/SQL tool，AI 只有「建 template(name+slots) + 填 record(slot→content)」
> 可用（類 Supabase 萬用表）；薄殼只經 KBDB service binding 調基本盤 HTTP API，不直連 D1、不寫 SQL。

- [x] 9.1 **MCP 薄殼**（AI 用，插件也走這條）：mcp/src/tools/kbdb_data.ts，6 工具
  `kbdb_create_template`(name+slots)、`kbdb_list_templates`、`kbdb_create_record`(template+values)、
  `kbdb_get_record`(record_id)、`kbdb_query`(by-template 列 records)、`kbdb_search`(entries LIKE q)。
  全走既有 kbdbFetch（KBDB binding）薄殼模式，無業務邏輯。registerAllKbdbDataTools 註冊進 registry.ts。
  **不含建表/SQL tool**（鐵律，grep 證 code 無 CREATE TABLE/.prepare/env.DB/SQL）。**mcp tsc exit 0**（2026-06-14）。
- [x] 9.5 **cypher KBDB proxy（9.2 的前置，2026-06-14）**：CLI 是 client 只認證到 cypher，達不到獨立
  KBDB worker（MCP 走內部 service binding 可達，CLI 不行）。故在 cypher 開 `cypher-executor/src/routes/kbdb-proxy.ts`
  純轉發 `/kbdb/templates|records|search` → KBDB 基本盤（沿用 KBDB_BASE_URL HTTP fetch + KBDB_INTERNAL_TOKEN，
  **不新增 service binding** rule02 §3.1）。**租戶隔離（leo 拍板選項①）**：X-Arcrun-API-Key 自動當 owner_id 注入
  records/entries（強制覆寫 caller 自帶 owner_id 防跨租戶寫）；**templates 全域共享**（虛擬表定義是 schema 非資料）。
  無 SQL/建表/業務邏輯（純 proxy）。掛進 index.ts。cypher tsc exit 0。
- [x] 9.2 **CLI 薄殼**（人用，2026-06-14）：`cli/src/commands/kbdb.ts` — acr kbdb template create/list、
  record create/get、query、search，透過 9.5 的 cypher proxy 打基本盤（與 MCP kbdb_* 同能力，差異只來自介面慣例
  rule07 §3.4）。註冊進 index.ts（`acr kbdb`）。無業務邏輯（薄殼）。cli tsc exit 0。
  **未驗收**：端到端需 cypher 部署 + KBDB_BASE_URL 可達後實測（acr kbdb template create → query 回得到）。
- [x] 9.3 **基本盤 entries 加 page_name 讀過濾**（2026-06-14）：listEntries 加 `page_name` 過濾 +
  GET /entries 接 `?page_name=`（既有欄位的便利查詢，不動表結構、不違反「表不變」鐵律）。
  用途：skills/examples 用 page_name 當 idempotency key 做 get-by-key。kbdb tsc exit 0。
- [x] 9.4 **修復 LI M3 斷鏈**（2026-06-14，連動 llm-interface M3.2/M3.4）：skills/examples 整條從
  舊 v3 `/blocks` `/search` 改打基本盤 `/entries`（entry_type 對應）。5 個已上線的 MCP 工具原本
  對死 route 回 404（假綠），現修正；sync-registry-to-kbdb.py 改打 /entries idempotent upsert。
  誠實降級：基本盤無語義 search → search_examples 改 LIKE 關鍵字（embed 模組 Phase 1 上線再換回語義）。
  mcp + kbdb tsc exit 0。

- [x] 9.6 **cypher proxy 補 `/kbdb/entries` CRUD（HANDOFF §2 缺口①，2026-06-15）**：9.5 proxy 只轉發
  templates/records/search，**漏了基本盤的 `/entries` CRUD**——這正是 mira `_kbdb_client.py` 主線遷移
  （ingest/create_block/get_by_id/get_by_page_name/patch_block）要打的端點。補 POST/GET(list)/GET(:id)/PATCH(:id)
  `/kbdb/entries` 純轉發到 KBDB 基本盤 `/entries`。**租戶隔離同 9.5 選項①**：寫入強制注入 owner_id、list 強制
  以本租戶 owner_id 過濾（防跨租戶讀）、PATCH 剝除 caller 自帶 owner_id（防認領/踢走）；by-id GET 沿用既有
  records by-id 慣例（require-key）。**刻意不開 DELETE**（基本盤 delete-by-id 無 owner 檢查，經 proxy 暴露 =
  跨租戶刪除風險；mira 也不需要）。無 SQL/業務邏輯（純 proxy）。**cypher tsc exit 0** + **端到端 prod 驗收綠**（2026-06-15）：
  無 key→401；A POST→owner_id 自動=A；GET by id 回得到；A list count=1、B list 同 type **count=0**（跨租戶隔離）；
  PATCH 改 content 成功且 owner_id hijack→B 被剝除（仍=A、B 看不到）；page_name lookup count=1（mira idempotency 路徑）；
  POST 帶 caller owner_id=B→覆寫成 A。已部署 arcrun-cypher-executor（官方 58309bb9）。smoke 資料已清。
- [x] 9.7 **修 `arcrun_report_feedback` 死 route（HANDOFF §3b 連帶，9.4 漏網，2026-06-15）**：9.4 把
  skills/examples 從舊 v3 `/blocks` 改打基本盤 `/entries`，但 `arcrun_report_feedback` 仍 POST 死掉的 `/blocks`
  （KBDB 基本盤只 mount entries/templates/records/recipe-stats，無 /blocks → 404 假紅）。改打 `/entries`
  （entry_type=agent-feedback、owner_id=用戶 namespace、source/api_key 併入 metadata_json、tags_json 不變）。
  回傳 id 從基本盤 `{entry:{id}}` 取（兼容舊 `{id}`）。薄殼模式不變（kbdbFetch）。**mcp tsc exit 0** +
  **端到端 prod 契約驗收綠**（2026-06-15）：確認舊 `/blocks`→**404**（正是原 bug、report_feedback 假紅根因）；
  用 9.7 實際送的 payload（entry_type=agent-feedback + metadata_json/tags_json）POST `/entries`→success、欄位保留；
  經 cypher proxy 讀回 count=1。已部署 arcrun-mcp（官方 58309bb9）。注：MCP service-binding hop 由既有 kbdb_* 工具
  （9.1，同 kbdbFetch 路徑，2026-06-14 已驗）佐證 binding 活；本次只修死 URL。smoke 資料已清。

## Phase 10：base 補 record PATCH（mira-dissolve T2 `[→arcrun]`，issue #6）—— 本次做

> 來源：頂層 SDD `docs/3-specs/mira-dissolve/`（T2.1/T2.2），總管經本 repo GitHub issue #6 交辦。
> 用途：graph 插件的精耕「取代」語意（同來源檔重萃 → 舊版 deprecate）需要「改既有 record 的 slot 值」。
> **三表 append-only 不破**：deprecate = 翻 record 的 slot 值（改底層 `entries.content`），不動表結構、不加欄、不刪 row。
> **base 不知 triplet（解耦鐵律 0.3，grep-proven）**：故 base 端 **不建 `/templates/triplet` 專屬端點**。
> issue 描述的「PUT /templates/triplet 加 status/superseded_by」拆兩半（與頂層 tasks T2.2/T3.2 一致）：
> base 只提供**通用** template 增改 slots 能力（既有 `PATCH /templates/:id` 已替換 slots_json，本就支援）；
> triplet 專屬的 `status`/`superseded_by` 兩 slot 由 **T3.2 在 kbdb-graph-plugin 的 `TRIPLET_SLOTS` 補**（另一 repo，非本次）。

- [x] 10.1 **`PATCH /records/:id`（T2.1，前置硬依賴）**：改既有 record 的 slot→content 值。底層更新對應
  `entries.content`（既有 slot 找 entry_value→entry 改 content）；slot 不存在則補建 entry + entry_value（用 record 的
  template 推算 slot 是否合法）。**三表結構一個欄位都不加**。`updateRecord` 在 record-crud.ts、route 在 records.ts。
- [x] 10.2 **template 增改 slots = 既有 `PATCH /templates/:id`（T2.2，base 端）**：核實 `updateTemplate` 已支援
  `slots` 替換（record-crud.ts:44 既有）→ base 端通用能力已就緒，**無需新 code、不建 triplet 端點**。triplet 兩 slot
  歸 T3.2（plugin repo）。本條＝核實 + 文檔釐清，非改 code。
- [ ] 10.3 **（選）`DELETE /records/:id`（T2.3）**：暫不做。依賴頂層 T8.3「死資料自動刪除原則」（design 待觀察未定）；
  且 deprecated record 查詢 `where status=active` 本就濾掉 → 無此端點不阻擋 deprecate 落地。待頂層拍板再補。

## Phase 11：self-hosted KBDB 查詢能力補缺（issue #5，普世框架視角）—— 本次做

> 來源：issue #5（原 Mira dogfood 開，Mira 已蒸發 → 當「未來任何 self-hosted 用戶都會撞的框架缺口」處理）。
> 四點分流依 leo 2026-06-26 修正指示 + 頂層 mira-dissolve 重審：
> 規則判準見 07-thin-shell §3.5（issue #4 自力救濟階梯）：source/DELETE 屬**自家 API 缺能力 → 補 API**。

- [x] 11.1 **source 過濾（#5 第1點，✅ 做，普世成立）**：`listEntries` 加 `source` filter，
  GET /entries 接 `?source=`。**零建表/零 migration**：用 SQLite `json_extract(metadata_json,'$.source')`
  查既有 metadata_json TEXT 欄（**不加欄、表不變鐵律**）。cypher proxy `GET /kbdb/entries` 白名單加
  `source`（隨租戶 owner_id 一起篩）。按來源篩 + 語義/關鍵字查詢都會用，ingest envelope 帶 source.uri。
  kbdb+cypher tsc exit 0。**端到端待 leo21c 部署驗**（需寫一筆帶 metadata.source 的 entry → ?source= 篩回）。
- [ ] 11.2 ~~documents 聚合 GROUP BY page_name（#5 第2點）~~ **不做**（舊河道頁特例）：新架構「跨 vault 的圖」
  走 graph MCP traverse/neighbors，不靠 KBDB 出 SQL 聚合端點。普世用戶也不需要。頂層 R6 已否決。
- [ ] 11.3 **cypher proxy DELETE（#5 第3點）⏸ 暫擱置**：依賴頂層「死資料自動刪除原則」（mira-dissolve T8，
  待觀察未定）。等 T8 拍板再補。**註**：#6 已對 records proxy 同理擱置 DELETE（裸 delete-by-id 無 owner 檢查，
  經 proxy 暴露=跨租戶刪除風險，補時要先讀 entry 驗 owner_id==本租戶才放行）。
- [→#7] 11.4 **embed-on-write（#5 第4點）併入 #7**：開 embed module + `kbdb_embed:true` 後寫入是否自動
  embed，由 #7 embed 模組定義（module 職責，前端不該手動戳 process-page）。不在 #5 單獨處理。
- [x] 11.5 **能力對照文件（#5 第二部分）**：`docs/4-guides/kbdb-capabilities.md` —
  self-hosted arcrun-kbdb 現有查詢能力清單 + 端點對照。**不寫「documents/process-page 待移植」**
  （舊河道視角，新架構不移植）。

## Phase 12：optional embed 模組 + vectorize 開關 + 語義查詢（issue #7 / mira-dissolve T2.4）—— 本次做

> 來源：issue #7（總管，全包 4 件）+ 頂層 mira-dissolve T2.4 系列。普世框架能力（任何 self-host 用戶可選開語義查詢）。
> 鐵律：embedding 屬 **base optional 模組**（非 graph/ingest）；CF Vectorize+AI binding 開/關，不拆 repo；
> **不裝保持輕**（free-tier 友善）；不對每個 block 地毯式 embed（精耕：只 embed 標 embed:true 的 entry）。
> 解鎖下游：ingest embed（第二階段）+ graph 詞+gloss 語義 normalize（T3.2c）等此。

- [x] 12.1 **base embed 模組（T2.4，從零做）**：`kbdb/src/embed.ts`——`embedEnabled()`（VECTORIZE+AI binding 都在才算開）、
  `embedOnWrite()`（寫入時對標 `metadata_json.embed:true` 的 entry 做 Workers AI `@cf/baai/bge-base-en-v1.5`
  768 維 → `VECTORIZE.upsert`，標 is_embedded=1，**不動表結構**）、`semanticSearch()`（query 向量 + metadata filter
  owner_id/source）。**base 對內容語意無知**：用通用 `embed:true` flag 而非寫死 entry_type 白名單（不破解耦）。
  wrangler.toml 加註解版 `[[vectorize]]+[ai]`（deploy 開時取消註解）。
- [x] 12.2 **entries route 接 embed（含 #5 第4點 embed-on-write）**：POST/PATCH 用 `executionCtx.waitUntil`
  fire-and-forget embed（模組開 + entry embeddable 才做，失敗不致命）；DELETE 連帶刪向量（避孤兒）；
  **GET /entries/search 加 `mode=keyword|semantic`**：semantic 需模組開，未開→**誠實降級 keyword + `capability_hint`
  告知「叫 CC 幫開 vectorize」**（發現閉環，#7 第4點 + 不假綠）。kbdb tsc exit 0。
- [x] 12.3 **vectorize 開關從零做（T2.4c）**：`.arcrun.yaml`/config 加 `kbdb_embed`（config.ts interface + env override
  ARCRUN_KBDB_EMBED 布林）；`deploy.ts` 加 `DeployContext.kbdbEmbed`：開時 `ensureVectorizeIndex`（REST
  `POST /vectorize/v2/indexes` dims=768/cosine，冪等）+ `injectWranglerConfig` 取消 kbdb toml 的 vectorize/ai 註解
  （**置於 stripOfficialOnlyBindings 之後**，否則 [ai] 被 strip 清掉——已驗）；`acr init` 互動加問「要不要開語義查詢」
  （預設關）；存進 config 讓 acr update 維持一致。cli tsc exit 0。
- [x] 12.4 **KBDB MCP 加語義查詢（T2.4b）**：`kbdb_search` 加 `mode`/`source` 參數透傳 + 把 base 的 `capability_hint`
  當 next-step 回給 AI（語義/關鍵字同一 KBDB MCP，D17 邊界）。薄殼模式不變（kbdbFetch）。mcp tsc exit 0。
- [x] 12.5 **CC 幫開 vectorize（T2.4d，第一版）**：路徑＝CC 寫 config `kbdb_embed:true` + `acr update`（已接 kbdbEmbed
  → 建 index + 注入 binding redeploy）。base 查詢回應的 `capability_hint` 是發現入口。Pages 設定頁不做（leo 排未來）。
- [ ] 12.V **端到端驗收 ⏳ 待 leo21c 部署驗**（需官方/leo21c 帳號開 Vectorize index）：開 kbdb_embed → acr update →
  寫一筆帶 `metadata.embed:true` 的 entry → `?mode=semantic` 搜回；未開時 `?mode=semantic` 回 keyword+capability_hint。
  本次只到 **tsc exit 0（kbdb/cypher/cli/mcp 全綠）+ toml 注入 dry-run 驗證**，不假裝端到端綠（mindset §7）。

## 驗收

- [ ] V1 純 D1（無 Vectorize/AI）能 CRUD entries/templates/records + LIKE search
- [ ] V2 開 Vectorize 語義啟用、關掉降級 LIKE，API 不變
- [ ] V3 基礎 blocks/templates/records grep 無 vectorize/triplet import
- [ ] V4 self-hosted init 建 D1 成功、不綁卡
- [ ] V5 列出某專案下所有工作流可行
- [ ] V6 各 worker tsc exit 0

## Notes

- 插件模型對標 PostgreSQL：基礎核心=psql core；embed=PGVector；triplet=Apache AGE。
- embed 不拆 repo（CF 內建、binding 開關）；triplet 拆獨立 repo（IP、action 多）。
- 官方與 self-hosted 共用同一基礎，差別只在 binding → 只維護一套。
- recipe 投稿入口是另一條線（registry），不在本 SDD。
