feat(workflow-store): workflow 真相源 KV → KBDB API(entry_type=workflow)
SDD kbdb-base §8.3/§8.4 P2+P3。總管裁定 2026-07-06: ① upsert 走 KBDB base PUT /entries(by owner_id+page_name+entry_type) ② cron 掃 KBDB、不保留 KV cron-idx ③ 一 workflow=一筆 entry_type=workflow;content=description(保 embed)、 graph+config+cron_expr 進 metadata_json KBDB: - entry-crud.ts 加 upsertEntry(read-then-write,無 UNIQUE、表不變) - entries.ts 加 PUT /entries(upsert,回 created 旗標,content 變動時 embed-on-write) cypher-executor: - 新增 lib/workflow-store.ts(走 kbdbBase,禁直連 D1) - webhooks-named register/trigger/list/delete 切雙軌(讀先 KBDB miss fallback KV、 寫 KBDB 為主+暫雙寫 KV);writeWorkflowSearchEntry 上收進 putWorkflow(修重複 entry bug); backfill 升級為 KV→KBDB 遷移入口;移除 migrate-cron-index 端點 - scheduled 改掃 KBDB(listCronWorkflows);刪除 lib/cron-index.ts - component-loader(trigger_workflow)、executions 擁有權檢查切雙軌讀 tsc 0 error;kbdb vitest 6/6;cypher vitest 41/42(1 失敗為 pre-existing,與本 PR 無關)。 不 merge、不部署、不動 production KV workflow。搬遷步驟見 MIGRATION.md。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJiLCRUU2o3aSpPEzVCt2o
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
# MIGRATION — workflow 真相源 WEBHOOKS KV → KBDB API(entry_type=workflow)
|
||||
|
||||
> 分支:`feat/workflow-store-kbdb` SDD:`system-dev/docs/3-specs/arcrun/kbdb-base/design.md` §8.3/§8.4
|
||||
> 總管裁定(2026-07-06):① upsert 走 KBDB base `PUT /entries`;② cron 掃 KBDB、不保留 KV cron-idx;
|
||||
> ③ 一 workflow = 一筆 `entry_type=workflow`,`content=description`(保 embed),graph+config+cron_expr 進 `metadata_json`。
|
||||
>
|
||||
> **本 PR 不 merge、不部署、不動 production 那 7 筆 KV workflow。** data 搬遷是下一階段(leo 寫入閘)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 這個 PR 改了什麼
|
||||
|
||||
### KBDB(新增能力)
|
||||
- `kbdb/src/actions/entry-crud.ts`:新增 `upsertEntry(db, input)` — by `(owner_id, page_name, entry_type)`
|
||||
read-then-write(無 UNIQUE 約束、表不變鐵律;命中最舊一筆更新,重跑收斂)。
|
||||
- `kbdb/src/routes/entries.ts`:新增 `PUT /entries`(upsert,回 `{ success, entry, created }`;content 變動時 embed-on-write)。
|
||||
|
||||
### cypher-executor(workflow store + 六路 + 兩路切雙軌)
|
||||
- **新檔** `cypher-executor/src/lib/workflow-store.ts`:`getWorkflow / listWorkflows / putWorkflow /
|
||||
deleteWorkflow / listCronWorkflows`,全走 KBDB base API(`kbdbBase` = KBDB_BASE_URL + 選用
|
||||
KBDB_INTERNAL_TOKEN),owner_id=namespace 租戶隔離。禁直連 D1/SQL。
|
||||
- `routes/webhooks-named.ts`:
|
||||
- register:寫 `putWorkflow`(KBDB 為主)+ 暫雙寫 KV;移除 `writeWorkflowSearchEntry`(上收進 putWorkflow,
|
||||
修掉每次 POST 堆重複 entry 的 bug);移除 KV cron-idx 維護。
|
||||
- trigger / list / delete:讀先 KBDB、miss fallback KV;list = KBDB ∪ KV(同名 KBDB 為準);delete 兩邊都刪。
|
||||
- `backfill-search-entries`:升級為 **KV→KBDB 完整遷移入口**(putWorkflow upsert,冪等可重跑)。
|
||||
- 移除 `migrate-cron-index` 端點(KV cron-idx 已退場)。
|
||||
- `scheduled.ts`:cron 掃描改 `listCronWorkflows`(掃 KBDB、記憶體濾 cron_expr);不再讀 KV cron-idx。
|
||||
- `lib/component-loader.ts`(trigger_workflow 子流程)、`routes/executions.ts`(擁有權檢查):切雙軌讀。
|
||||
- **刪除** `lib/cron-index.ts`(KV cron-idx 模型退場)。
|
||||
|
||||
### 綠燈
|
||||
- `kbdb` tsc 0 error、vitest 6/6 綠。
|
||||
- `cypher-executor` tsc 0 error、vitest 41/42 綠(1 個 `executor.test.ts > 不存在的零件` 失敗為
|
||||
**pre-existing**,main 上同樣失敗,與本 PR 無關——component-not-found 錯誤訊息措辭斷言,非 workflow 路徑)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 部署後驗證計畫(對真端點 curl,禁 miniflare 假綠)
|
||||
|
||||
> 前提:部署到 **leo21c**(auto 模式本輪不做;下述為部署後的驗收步驟)。
|
||||
> 端點:cypher `https://arcrun-cypher-executor.leo21c.workers.dev`、KBDB `https://arcrun-kbdb.leo21c.workers.dev`。
|
||||
> **全程只用測試 namespace(如 `ak_wfmigtest`),不碰現有 7 筆 production workflow。**
|
||||
|
||||
設 `NS=ak_wfmigtest`、`CY=https://arcrun-cypher-executor.leo21c.workers.dev`、`KB=https://arcrun-kbdb.leo21c.workers.dev`。
|
||||
|
||||
1. **KBDB upsert 端點本身**
|
||||
```
|
||||
curl -XPUT $KB/entries -H 'content-type: application/json' \
|
||||
-d '{"entry_type":"workflow","owner_id":"'$NS'","page_name":"t1","content":"desc A","metadata_json":"{\"graph\":{\"id\":\"g\"}}"}'
|
||||
# → { success:true, created:true }
|
||||
# 再打一次(改 content:"desc B")→ { success:true, created:false }(同一 entry 更新,非新增)
|
||||
curl "$KB/entries?entry_type=workflow&owner_id=$NS&page_name=t1" # → 恰 1 筆、content="desc B"
|
||||
```
|
||||
2. **register(部署)→ 恰一筆、redeploy 不重複**
|
||||
```
|
||||
curl -XPOST $CY/webhooks/named -H "X-Arcrun-API-Key: $NS" -H 'content-type: application/json' \
|
||||
-d '{"name":"wf_a","description":"測試工作流","graph":{"id":"wf_a","nodes":[]}}'
|
||||
curl "$KB/entries?entry_type=workflow&owner_id=$NS&page_name=wf_a" # → 恰 1 筆
|
||||
# 同名再 POST 一次(改 description)→ 仍恰 1 筆、content 更新(驗 upsert 冪等,無重複 entry bug)
|
||||
```
|
||||
3. **list / trigger / delete 走 KBDB**
|
||||
```
|
||||
curl $CY/webhooks/named -H "X-Arcrun-API-Key: $NS" # → 含 wf_a,欄位齊
|
||||
curl -XPOST $CY/webhooks/named/wf_a/trigger -H "X-Arcrun-API-Key: $NS" -d '{}' # → 執行(讀到 KBDB graph)
|
||||
curl -XDELETE $CY/webhooks/named/wf_a -H "X-Arcrun-API-Key: $NS" # → {deleted:true}
|
||||
curl "$KB/entries?entry_type=workflow&owner_id=$NS&page_name=wf_a" # → 0 筆
|
||||
```
|
||||
4. **cron 掃 KBDB**:部署一個首節點為 cron 的 workflow(cron_expr 每分鐘),等 1–2 分鐘看 `wrangler tail`
|
||||
`[scheduled] scanned N KBDB cron workflows, k triggered` 有觸發。
|
||||
5. **雙軌 fallback(不寫 KBDB 的舊 KV workflow 仍可 trigger)**:手動塞一筆 `{NS}:wf:legacy` 進 WEBHOOKS KV
|
||||
(只 KV、不進 KBDB),打 `/webhooks/named/legacy/trigger` → 應 fallback KV 成功執行;`GET /webhooks/named`
|
||||
應同時列出 KBDB 的與這筆 KV-only 的(union)。
|
||||
|
||||
**驗收標準**:上述每步 HTTP status + 回傳體符合預期;特別是「redeploy 同名恆為 1 筆」(upsert 生效)
|
||||
與「cron 從 KBDB 觸發」。缺任一即不算綠,回報而非假綠(mindset §7)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 剩餘 data 搬遷步驟(下一階段,leo 寫入閘,本輪別做)
|
||||
|
||||
現有 7 筆 production workflow 仍只在 WEBHOOKS KV。雙軌讀讓它們仍可 trigger,但:
|
||||
- **cron 掃描只看 KBDB** → 若這 7 筆裡有 cron workflow,未搬遷前它們的排程會**停止觸發**(⚠️ 高優先)。
|
||||
- semantic 搜尋也只看 KBDB entry。
|
||||
|
||||
**搬遷程序(冪等、可重跑)**:
|
||||
1. 逐 namespace 呼叫 `POST /workflows/backfill-search-entries`(帶該租戶 `X-Arcrun-API-Key`)。
|
||||
它 list KV 的 `{ns}:wf:*`、對每筆 `putWorkflow`(upsert)遷進 KBDB。回傳 `backfilled` / `needs_description`。
|
||||
2. 驗證:`GET $KB/entries?entry_type=workflow&owner_id={ns}` 每筆都有對應 entry(graph 在 metadata)。
|
||||
3. `needs_description` 清單交操盤 CC re-deploy 時據實補描述(不自動編造)。
|
||||
4. 全部驗穩後,才由 leo 決定**拆掉 register 的 KV 雙寫 + trigger/list/delete 的 KV fallback**(另一次閘)。
|
||||
拆除前 KV 是安全網,不要急著拿掉。
|
||||
|
||||
> 誰有幾筆、哪些是 cron,需先盤點(`WEBHOOKS.list` 或 console)再排搬遷順序(cron 的先搬)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 回滾
|
||||
|
||||
本 PR 不動 production 資料,且 register 仍雙寫 KV、讀有 KV fallback → 直接 revert 分支即可回到「KV 為真相源」。
|
||||
KBDB 端只**新增** `PUT /entries`(不改既有端點行為),revert cypher 側即停用;KBDB 側可留(無副作用)。
|
||||
唯一不可逆的取捨:本 PR 刪了 `cron-index.ts` 與 `migrate-cron-index` 端點(KV cron-idx 退場);
|
||||
如需回滾 cron 到 KV,從 git 歷史取回即可。
|
||||
Reference in New Issue
Block a user