- 鋪檔(自 system-dev-template v1.15.0):SDD-LIFECYCLE.md+pending-changes.md +sdd-guard.sh(覆蓋舊版,加單一活性檢查)+sdd-check.md+sdd-active-check.sh - settings.json PreToolUse(Write|Edit|MultiEdit)掛上 sdd-guard.sh - 全部 SDD design.md 掛 frontmatter:portal-auth=active(現行 portal 線, #61 demo 四件套剛 merge);artifact-sharing=draft(零任務動工); 其餘 16 份=paused(皆有未完成任務,無明顯死件,不硬 close) - CLAUDE.md 加「SDD 生命週期鐵律」段(指向 SDD-LIFECYCLE.md+濃縮五條) +SDD 速查表改以 frontmatter status: active 為現行判準 - 驗證:sdd-active-check exit 0(恰 1 份 active);guard pipe-test code 檔 exit 0 帶現行 SDD 提示;反向測試(造 2 份 active)guard exit 2/check exit 1 全擋 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
26 KiB
status, superseded_by
| status | superseded_by |
|---|---|
| paused |
Design: KBDB Base —— 原子化萬用表(self-hosted 資料底座 + 官方核心)
2026-06-07 richblack 拍板。來源:壓測報告(
test_arcrun/docs/壓測報告.md)暴露 self-hosted 無資料層 → richblack 決定把 KBDB 的「基礎儲存」開源進 arcrun,向量/三元組走插件模式(如 PostgreSQL 的 PGVector / Apache AGE)。 架構同時影響官方版:官方也改成「基礎核心 + 可選模組」,故開源與官方共用同一基礎,不維護兩套。
0. 為什麼(壓測暴露的缺口)
壓測(第一次完整壓測,6 輪回歸)跑通了「表單 → workflow → Google Sheets」,但暴露 self-hosted 沒有資料層:
| 缺口 | 現況 | KBDB Base 如何解 |
|---|---|---|
| 無「專案」維度 | KV key 只有 {namespace}:{type}:{name},無 project;recipe 甚至全局共享 |
entries 樹狀(entry_type=project/workflow,parent_id)→ 可列「某專案的所有工作流」 |
| D1 完全空置 | self-hosted init 只建 8 KV,無 D1;grep d1_databases 全 repo 零結果 |
KBDB Base 用 D1 → 填上閒置的 D1 |
| recipe 成功無記錄 | push 時 probe 2xx 但不持久化 | 成功記錄存 entries/records → 可監控、可當投稿依據 |
| 資料碎(像 n8n) | 散落 KV,無結構 | templates 做虛擬表 → 結構化資料形態 |
recipe 成功記錄 + 投稿(帶記錄免驗證)本次一起做(見 7)。recipe 信譽/市場/排序是後續,不在本次。
1. 核心設計:原子化萬用表(萬年不動)
richblack 設計原則:「基礎表是 truth,萬年不動。新技術(embed/triplet/未來)一直增加, 它們把衍生結果記在各自的地方,不回頭改基礎表。」這正是 PostgreSQL 核心 + PGVector/AGE 插件的模型。
三張表(沿用 KBDB v3 0005_universal_table,已驗證可用)
| 表 | 作用 |
|---|---|
entries |
原子資料。entry_type:block/value/template/slot(+ 本 SDD 擴充 project/workflow)。樹狀 parent_id、多租戶 owner_id |
templates |
定義虛擬表的 slots(slots_json,如 ["display_name","gender"])→ 同一份 entries 用不同 template 呈現不同資料形態 |
entry_values |
把多個 entries 按 template 組成一筆結構化記錄(record_id + slot_name + entry_id) |
萬年不動原則(本 SDD 要修正 KBDB 現況的地方)
KBDB 現況大致已符合,但有違反處要修:
- ✅ embed:
block-embed.ts把向量 upsert 到 Vectorize(獨立),不動 blocks 表。 - ✅ triplet 主體:
triplet-crud.ts主要INSERT INTO entry_values(獨立記錄)。 - 🔴 違反處:
triplet-crud.ts:106/110UPDATE blocks SET entity_type = ?—— 抽 triplet 後回頭改基礎表。 → 修法:衍生屬性(entity_type 等)移出基礎表,記到衍生層(entry_values 或獨立 template)。基礎表只存 truth。
2. 插件模式:一套 code,三層 binding 開關(不維護兩套)
richblack:「像 PGVector/AGE 是插件。基礎模組提供原子化萬用表(這也是賣點);要向量、要圖資料庫另外裝。 我還是希望只維持一套。」
| 層 | 能力 | 依賴 | 開源 / 費用 |
|---|---|---|---|
| 基礎 KBDB Base | entries/templates/entry_values CRUD + D1 LIKE 關鍵字搜尋 | D1 only | ✅ 開源,免費,不綁卡 |
| + embed 模組 | 語義搜尋(search 升級) | CF Vectorize binding + AI | embed 是 CF 內建(程式薄)→ 不拆 repo,給「開/關」:有 Vectorize binding 就啟用,沒有就降級 LIKE。Vectorize 需用戶自開(自付費) |
| + triplet 模組 | 三元組抽取 / 知識圖譜 / 圖查詢 | AI + triplet-*.ts(8 個 action,較重) | triplet 是 richblack 的 IP、程式量大 → 獨立 repo / 獨立 worker,基礎不依賴它,要才裝 |
Q1 拍板(richblack 2026-06-07):embed 不拆、triplet 拆
- embed → 不拆 repo,binding 開/關:因為 embed 靠 CF 內建 Vectorize,程式薄(呼叫
env.VECTORIZE.upsert),沒 binding 就降級。一套 code、開關即可。 - triplet → 獨立 repo/worker:非 CF 內建、是 richblack IP、action 多(triplet-extract/normalize/embed/crud/stats/syntax/entities/update)。基礎 KBDB 不 import triplet,要才裝。
search 分層(壓測者問「search 還是要有,只是沒語義」→ 對)
KBDB search.ts 已有兩層骨架:
- 基礎版:D1
LIKE全文搜尋(KBDB 現有 fallback 邏輯,不需 Vectorize)。 -
- embed:語義搜尋(Vectorize + AI)。 API 不變,能力分層:基礎用 LIKE,裝 embed 升級語義。
3. 解耦工作(把寫死的觸發改成可選)
KBDB 現況:blocks.ts 把 ingestText/createTriplet/deleteBlockVector 寫死 import 進寫入流程(寫 block 順手 embed+抽 triplet)。
解耦目標:
- 寫 block = 只寫 block(純基礎)。
- embed = 可選 hook(Vectorize binding 在才掛)。
- triplet = 可選(triplet 模組裝了才掛)。
- 基礎
blocks.ts/templates.ts/records.ts不 import vectorize/triplet。
✅ 已查證
templates.ts/records.ts本就乾淨(只 import record-crud);主要工作在blocks.ts。
4. 落地形態:import 進 arcrun(如 MCP 的 pattern)
richblack:「像先前把 MCP import 進本專案一樣,也把 KBDB import 進來。」對,同一 pattern。
- 基礎 KBDB Base 搬進
arcrun/kbdb/(如arcrun/mcp/)。形態:獨立 Worker + D1。納入 deploy 掃描(rule 05:新目錄 + wrangler.toml 自動部署)。 - init 建 D1:
cli/src/lib/cf-api.ts加 D1 建立(如現有 KV 建立),deploy.ts注入 D1 id。self-hosted 從只建 KV → 建 KV + D1。 - cypher 改用 KBDB Base 存專案/工作流:webhook/credential/recipe 的「專案歸屬」走 KBDB entries(漸進,不一次搬完)。
- embed:隨基礎進 arcrun,Vectorize binding 開關。
- triplet:獨立 repo,self-host 用戶要才另裝(不進 arcrun 基礎)。
共用同一基礎(官方也改)
richblack 決定官方版也改成「基礎核心 + 可選模組」→ 官方與 self-hosted 共用同一份基礎 KBDB code,差別只在 binding(官方全開、self-host 基礎免費可選開)。不維護兩套。
5. 邊界(本 SDD 不做)
- recipe 投稿入口 + 帶成功記錄免驗證:✅ 本次要做,見新增 §7(與 KBDB 成功記錄嵌在一起)。
- recipe 信任 = 市場機制(靠量/星數模型):拍板用市場優先、暫不做強防偽(見 §7.3)。本次(5.2)做投稿端點(submit 私庫 / submit-p 公共庫覆蓋同 canonical_id)+ stat 存證;跨環境「靠量同步回市場」聚合層另排 task;強防偽(官方重跑/多方回報)只在市場失靈才做。
- triplet 模組本身:獨立 repo,不在本 SDD(本 SDD 只確保基礎不依賴它、可選掛上)。
- vectorize 的進階用法(suggest/entities-graph-embed):隨 triplet/進階模組,不在基礎。
- 官方版的搬遷細節:本 SDD 聚焦「基礎 KBDB 進 arcrun self-hosted」,官方共用基礎是方向,搬遷另排。
7. recipe 公庫/私庫機制 + 帶 KBDB 成功記錄(市場信任)—— 本次要做
§7.1-7.4 原只想「投稿」單向(本次最小已做:成功記錄 5.1 + submit-p 雛形 5.2)。 §7.5 是 richblack 2026-06-07 指示補的 CHANGE:把公庫/私庫的所有互動情境想全(pull/搜尋/投稿/市場同步), 待 review。下方 7.1-7.4 是已成立的基礎,7.5 是擴充全貌。
richblack 2026-06-07:本次只做「可投稿 + 帶成功記錄免驗證」,這兩件嵌在一起。 背景:官方初期那批 recipe 是一次性建立、未驗證(只為「Arcrun 推出不能空空的」)。 壓測者打通的修正版(如 google_sheets_append method/body)回不了官方 → 官方仍錯,下個用戶再撞。 不該由 AI 手改種子 recipe(治標);正解是投稿機制 + 用真實成功記錄當免驗證依據(治本)。
7.1 兩件嵌在一起
- 成功記錄(KBDB):判定單位是「工作流執行」(對標 n8n execution,非逐節點)。一條 workflow 跑完,
整體成功(到 Output、無 error)→ 把這次用到的每個 recipe 節點各記成功 +1;整體失敗 → 已跑到的 recipe 節點各記失敗 +1。
歸屬對象是 recipe(因為投稿的是 recipe),canonical_id 取 auth recipe 的
service欄位(與投稿身份一致)。 單節點工作流(cron→gdrive,一個節點一個 credential)是最簡情形。存 D1(entry/record),可累積、可查。註(術語別混):cypher-executor = 工作流引擎,用 cypher 語法(節點+邊的圖遍歷語意)把零件串成工作流, 本就在開源核心內,不是 graph DB。「可選/不在核心」的是兩個插件:① triplet(知識三元組的圖儲存, 對標 Apache AGE)② vectorize(CF 內建但可能多花錢)。recipe 成功記錄是純 D1 KBDB 核心能力,不依賴這兩個插件。
- 投稿(registry/官方):投稿 recipe 時帶上它在投稿者環境的成功記錄。官方端看記錄即信,免人工驗證——真實打通比投稿者自寫測試可信(呼應 gherkin-not-safety-sandbox-is)。
7.2 本次範圍(最小)
- recipe 成功/失敗計數落地 D1(KBDB 成功記錄)。
- 一個投稿端點:收 recipe + 其成功記錄 → 進官方 recipe 池,新增一個作者版本(app-store 模型 §7.5.5,非覆蓋同 canonical_id;同 canonical 多作者並存)。
- 投稿者目前就是 richblack,不需誘因;重點是有入口可投 + 帶記錄免驗證。
7.3 信任機制:市場優先(靠量,非人力/強防偽)—— richblack 2026-06-07 拍板
這個決策之前討論過、那時選「先不做」,導致今天又重講一次 → 故此次寫進 SDD,不再丟。
核心:recipe 信任靠「量」決定,像 GitHub 星數,不靠人力檢核、也不先做強防偽。
不可能全世界這麼多 recipe 都靠人力檢核。改用市場機制:
- recipe 的成功/失敗由全體使用者的真實執行累積(投稿者 A 的 recipe 通過 2000 次;投稿者 B 的失敗 500 次 = 像被按爛)。
- 上傳爛 recipe → 別人用會失敗 → 累積失敗數 → 投稿者信用下降 → 有動機更新自己發佈的 recipe,避免持續被按爛。
- 數據來源 = §7.1 的成功記錄(每次工作流執行把用到的 recipe 成功/失敗記到 KBDB)。市場機制 = 把這些跨使用者的記錄同步回公共市場匯總。
為什麼先市場、暫不做強防偽(誠實,mindset §7): self-hosted 投稿者 = 自己環境 root,能寫任意數字進自己 KBDB → 自報 stat 可造假。 但先信市場:實驗一段時間,若市場反應爛(造假猖獗)再考慮強防偽(官方重跑 / 多方獨立回報——證據由投稿者控制不了的一方產生)。 故本次不拿投稿者自報的數字當自動放行門檻(那會造債:未來門檻是空的)。stat 先當存證 + 法律歸責軌跡。
分期:
- 本次(5.2):投稿端點
POST /recipes/submit。submit(私庫)vs submit-p(公共庫,需 exposure_consent = 暴露同意,mindset §6)。 公共庫新增作者版本(app-store 模型 §7.5.5,非覆蓋)。投稿者帶的 stat 寫進官方 KBDB 當存證(不當門檻)。 - 後續(市場同步,§7.3 主體):跨環境「靠量同步回市場」——self-hosted 各用戶的真實成功/失敗匯總回公共市場, 形成 recipe 的星數/信用。是 §7.1 數據的跨環境聚合層。本次先留接口(5.1 已在各環境記錄,市場聚合另排 task)。
- 更後續(強防偽,只在市場失靈才做):官方 in-process 重跑投稿者工作流拿官方自己的 2xx / 第二投稿者獨立回報。 (註:app-store 模型下「版本並存」是常態、不刪舊版 §7.5.5;舊版靠市場淘汰,不做下架引導。)
7.4 與零件投稿的差異
零件投稿走 GitHub PR(人 merge + CI runtime 驗 wasm,記憶 component-submission-via-pr)。 recipe 是純文字資料(非 wasm),不需 CI 跑沙箱;它的驗證改用真實成功記錄。故 recipe 投稿機制與零件不同,不混用 component-registry-canon SDD。
7.5 公庫 / 私庫雙向機制(CHANGE,richblack 2026-06-07 指示「把兩庫所有情境想好」)—— 待 review
背景修正:原 §7.1-7.4 只想了「投稿」單向,太窄。richblack 指出: SaaS 版(standard mode)本來就只有公庫;self-hosted 版本來就只有私庫。 兩庫的互動是 overall 架構(不只為投稿):私庫是公庫的按需子集——self-hosted 也許只用 10 個, 公庫也許有 10000 個,不需每次全部拉下來,用到的再複製。投稿(submit-p)只是其中一條反向流。
7.5.1 兩個庫的物理本質(已確認現況)
| 庫 | 是誰 | 物理 | 規模 | 角色 |
|---|---|---|---|---|
| 公庫 | 官方 SaaS(cypher.arcrun.dev)的 RECIPES KV |
官方 CF 帳號 | 大(~10000,含社群投稿) | 唯一公共真相、可瀏覽/搜尋/pull |
| 私庫 | 每個 self-hosted(用戶自己帳號)的 RECIPES KV |
用戶 CF 帳號 | 小(~10,按需子集) | 自己實際用到的 + 自己改的 |
- recipe key 都是
recipe:{canonical_id}(無 owner 前綴)。同一套部署的 KV = 那套的庫。物理隔離(不同帳號不同 KV)。 mode(cli config):standard= SaaS 用戶(只連公庫,無私庫);self-hosted= 自己一套(有私庫,可選連公庫 pull/submit);local= 純本地。
7.5.2 三種使用者 × 互動矩陣
| 使用者 | 有哪些庫 | 取 recipe | 改 / 投稿 | 成功記錄 |
|---|---|---|---|---|
| SaaS 用戶(standard) | 只公庫 | 直接用公庫(公庫即他的庫) | 改了直接寫公庫(=投稿,需 consent) | 記在官方 KBDB(即市場數據本身) |
| self-hosted 用戶 | 私庫(主)+ 公庫(唯讀來源) | 公→私 pull:搜公庫→複製到私庫 | 私庫改 →(可選)submit-p 推回公庫 | 記在自己 KBDB;市場同步另排(§7.3 5.5) |
| local | 無遠端庫 | 本地 recipe 檔 | 無 | 本地 |
7.5.3 四條互動流(窮舉)
- 公→私 pull(核心,之前漏):self-hosted
acr recipe pull <canonical_id>→ 從公庫只讀端點抓該 recipe → 寫進自己私庫 KV。 按需,不全量同步(10000 不必拉)。pull 也可帶公庫的成功記錄(市場星數)供用戶判斷要不要用。 - 公庫瀏覽/搜尋:
acr recipe search <q>/list→ 打公庫只讀端點(GET /public-recipes?q=)。 self-hosted 沒搜尋能力就不知道公庫有什麼可 pull。SaaS 用戶同一端點即用。 - 私→公 submit-p(已做雛形):self-hosted 把私庫某 recipe 推回公庫(
POST /recipes/submit,需 exposure_consent)。新增作者版本(app-store 模型 §7.5.5,非覆蓋;同 canonical 多作者並存)。 - 成功記錄 → 市場同步(§7.3 5.5,另排):self-hosted 各環境真實成功/失敗匯總回公庫的市場星數。pull(流1) 帶的星數就來自這裡。
7.5.4 公庫只讀端點(公→私 + 瀏覽的基礎,待實作)
官方 cypher 開公開只讀端點(無需 api_key,因公庫本就公共):
GET /public-recipes?q=&limit=&offset=→ 搜尋/列出公庫 recipe。同 canonical_id 回多筆(多作者),各附作者 + 市場星數 success/failure,供 CC/AI 依數據選(§7.5.5)。GET /public-recipes/:canonical_id?author=→ 取單一 recipe 全文(pull 用)。不指定 author → 回市場最佳版本(成功率最高)。- 搜尋/取用落空 → 回
{ found: false, canonical_id, hint }創作引導(§7.5.6),不回空陣列乾等。讓 CC 知道下一步是「自己做一個成為作者」。
與現有
GET /recipes(已存在,列當前部署 KV)的差別:/public-recipes語意是「這是公庫,給外部 self-hosted pull/瀏覽」,且含作者維度 + 市場數據。 在官方部署上讀同一 KV;命名分開是為了語意清楚 + 公庫的多作者/市場排序不污染內部/recipes。
市場星數 per-uuid(§7.5.h 已實作):5.1 改收集 API recipe 的 uuid(非 auth service),KBDB 市場星數記 per-uuid。 故
GET /public-recipes/:canonical_id的「選市場最佳作者版本」真正能區分 Leo 版/John 版(各自 uuid 各自累積)。 舊資料無 uuid → fallback canonical_id(migration 後自然帶 uuid)。
7.5.5 recipe = 具名作者作品(app-store 模型)+ UUID 身份 —— richblack 2026-06-07 拍板
這條推翻了我原本「覆蓋同 canonical_id」的錯誤前提。正確模型如下。
每個投稿的 recipe 是一個「具名作者作品」,像 app store 上的一個 app,只有作者(+admin)能改。
- Leo 投稿
gsheets_upsert= 在 app store 放了一個 app。只有 Leo + admin 能動它。 - John 覺得 Leo 的很爛 → John 另推一個
gsheets_upsert(John 的作品)。只有 John + admin 能動。 - 公庫裡因此同 canonical_id 並存多份(Leo 版、John 版),各帶作者、各帶獨立市場數據。
所以:
- submit-p ≠ 覆蓋。投稿是新增一個作者版本,不是覆蓋同 canonical_id。(修正原 §7.2/§7.3 的「覆蓋」字眼。)
- 沒有「公庫改了 Leo 要不要跟」的問題:Leo 的作品只有 Leo 改;別人不滿意是自己推新作品,不是改 Leo 的。版本關係問題消失。
- 向後相容、不刪舊 recipe:Leo 的 5/10 不被刪,只是市場上沒人選 → 自然淘汰(長尾留著,無破壞性刪除)。
- 選擇由市場數據驅動(CC/AI 操盤手選):CC 搜
gsheets_upsert→ 找到 2 個 → 看數據(Leo 5成功/10失敗、John 100成功/0失敗)→ 選 John 的。Leo 的逐漸沒人用。
身份模型(落地)= UUID(richblack 2026-06-07 拍板,比 canonical_id+author 複合鍵更乾淨):
每個 recipe 一誕生就領一個 UUID = 唯一身份。canonical_id / author / 公私 全是屬性,不是身份。
- 身份(UUID) 與 歸屬(author 屬性) 分離 → 沒有「同 canonical_id 撞 key」問題(key 是 UUID,本就唯一)。
- KV key 設計:
recipe:{uuid}存 recipe 本體;idx:canonical:{canonical_id}→ UUID 清單(同 canonical 多 UUID); 私庫執行:idx:installed:{canonical_id}→ 該 canonical 在本庫安裝的唯一 UUID(pull/author 時定一個)→ 執行查找仍由 canonical_id 解析到唯一 recipe,不破執行。 - Leo 改 John 的:Leo 拿 John 版(UUID-J)改 → 發出去領新 UUID-L = Leo 作品。UUID-J/UUID-L 是兩個東西,不冒名、天然不衝突。
- author 屬性 = 該 UUID 誕生時的投稿者(誰投誰負責那版市場數據);可選帶
derived_from: {uuid}溯源(致謝/fork upstream)。
「Leo 跟 John 一模一樣」怎麼辦(你問的):兩個不同 UUID 都通過、各跑各的市場數據。 重複 = 市場冗餘,靠數據淘汰不靠技術阻止(先有數據的勝,複製品沒人選自然沉底,§7.3 靠量不靠人力)。
檢舉功能:本次不做。檢舉 = 人力檢核,與「市場優先、暫不強防偽」(§7.3)矛盾,提前做還用不到 = 造債。 市場失靈才考慮(§7.3 更後續)。CC 不檢舉(mindset §7:CC 是操盤手不是審查者、不代人裁決;CC 對爛 recipe 的「投票」= 不選它 = 市場數據)。
影響現有 code:5.2 的
POST /recipes/submit改「覆蓋」→「領新 UUID 新增」;recipe key 從recipe:{canonical_id}轉recipe:{uuid}+ canonical 索引;執行查找改經idx:installed:{canonical_id}拿唯一 UUID(保證不破 component-loader/auth-dispatcher/credential-injector)。列入 7.5.f task。
7.5.6 搜尋落空 = 創作入口(richblack 2026-06-07 拍板)
app-store 模型的閉環關鍵:公庫沒有的 recipe,由 CC 現做成為作者,再投回公庫補上。
CC 搜「gsheets_delete_sheet」公庫沒有 → 端點不能只回空陣列乾等,要回「可創作」訊號 → CC 說「那我做一個」→ CC 成為作者。
- 公庫只讀端點(§7.5.4)搜尋落空時的回應 = 創作引導,不是空結果:
明確回
{ found: false, canonical_id, hint: "公庫無此 recipe,可自行建立並 submit-p 投稿成為作者" }, 讓 CC(AI 操盤手)知道下一步是「自己做一個」而非卡住。 - CC 現做 → 跑成功累積市場數據(§7.1)→ submit-p 推回公庫 → 成為公庫第一個
gsheets_delete_sheet(CC 是作者)。 - 閉環:
- 公庫有 → pull 用(市場數據選最佳作者版本,§7.5.5)。
- 公庫沒有 → CC 現做成為作者 → 用 → 投稿 → 公庫從此有了。
- 呼應 mindset:CC 是大腦(arcrun 是 AI 呼叫的工具),缺能力時 CC 自己補(做 recipe / 工作流), 不停在「找不到」。工作流是 default、零件是例外(缺能力先想工作流/recipe,不是停手)。
8. KV list 上限威脅免費承諾 → 高頻 list 遷 D1(CHANGE,2026-06-08 richblack CF 收到 list 超標信)
事件:richblack 的 CF 帳號收到「Workers KV 免費等級每日 1000 次 list 上限已超」信, list API 回 429 直到隔日重置。這直接威脅 arcrun「免費可用」核心承諾(deploy-via-local-deploy-not-ci 免費優先精神)。 方向拍板(richblack 2026-06-08):高頻 list 改走 D1(對齊 §6 Q2 的 KV vs D1 分工——要列舉/搜尋的進 D1)。
8.1 根因:哪些 KV list 是高頻(grep cypher-executor/src)
| 位置 | list 操作 | 頻率 | 嚴重度 |
|---|---|---|---|
scheduled.ts:34 |
WEBHOOKS.list({prefix:'cron-idx:'}) |
每分鐘一次 = 1440/日 | 🔴 常駐單獨就爆(>1000 上限) |
recipes.ts public-recipes(§7.5 新加) |
RECIPES.list({prefix:'recipe:'}) |
每次 recipe 搜尋 | 🟠 搜尋頻繁就疊加爆 |
recipes.ts GET /recipes / listAllRecipes |
RECIPES.list |
每次列 recipe | 🟠 |
webhooks-list.ts / webhooks-named.ts |
WEBHOOKS.list |
每次列 workflow | 🟠 |
D1 額度遠寬:免費 5M 讀/日、100K 寫/日(§6 Q4),對比 KV list 僅 1000/日。要「列舉/搜尋/排序」的天生該 D1。
8.2 緊急止血(cron,今天在爆)
scheduled.ts 每分鐘 list cron-idx: → 改成不靠 list:
- 方案:維護單一固定 key(如
cron-idx:_all存所有 cron workflow 的 {apiKey,name,cron_expr} JSON 陣列), scheduled 只get一次(取代 list)。acr push 時 upsert 進這個 key。 - 1440 次/日 list → 0 次 list(改 1 次 get/分鐘 = 1440 get/日,KV get 免費額度 100K/日,遠夠)。
8.3 治本(recipe / workflow 查詢遷 D1,分期)
對齊 §6 Q2(recipe + workflow 進 D1):
- recipe:recipe 本體從 RECIPES KV 遷進 KBDB D1(entry_type='recipe' 或專表)。 public-recipes 搜尋、list 改 D1 query(LIKE/WHERE,含 §7.5 多作者/市場排序天生適合 SQL)。 執行時 resolveRecipe 也改 D1(但執行是 get-by-key 級,KV get 不爆——可漸進)。
- workflow:workflow record 從 WEBHOOKS KV 遷 D1(entry_type='workflow' 掛 project parent,§6 Q1)。 list workflow 改 D1 query。
- 保留 KV 的:session / credential / exec context(§6 Q2:短期高頻純 get,不 list)。
8.4 分期建議(待 richblack 拍板細節)
- P0 止血(§8.2):cron 改單 key get。最小改動、今天可止血。
- P1:public-recipes / recipe list 改 D1(我 §7.5 新加的 list 是疊加元兇)。
- P2:workflow list 改 D1。
- P3:recipe/workflow 本體完整遷 D1(resolveRecipe / KV→D1 讀寫)。
誠實 trade-off:遷 D1 要寫 migration + 雙寫過渡(KV 舊資料相容),工程量不小。 但不遷 = 免費承諾破功(用戶用一用就 429)。這是核心承諾必修項,不是優化。 待 review §8 + 拍板分期顆粒度後動 code。勿在 review 前動。
6. 開放問題(待 review)
- 專案維度怎麼進 entries:
entry_type='project'+ workflow 用parent_id掛專案?還是 templates 定義「專案」虛擬表?- Leo:由你決定,我記得先前 KBDB 已經有實作,參考原有做法。
- [DECIDED, CC 2026-06-07] use entry_type=project + parent_id tree (NOT templates virtual table). Matches existing KBDB: entry_type is an extensible string, entry-crud.ts already supports WHERE parent_id; no schema change (honors table-never-changes). "list workflows under project" = WHERE parent_id=PROJECT_ID AND entry_type=workflow. templates/slot reserved for structured records like recipe success stats.
- KV vs D1 分工[拍板 2026-06-07]:KV 不全廢,按特性分。workflow YAML + recipe + 成功記錄進 D1(要層級/列舉/排序);session、verdict、credential 留 KV(短期高頻純取用)。類比 PostgreSQL + Redis。workflow 變 entry(entry_type=workflow 掛 project parent)。
- Leo:同意
- triplet 獨立 repo 的接點:基礎 KBDB 怎麼讓 triplet 模組「掛上」——HTTP hook?還是 triplet worker 自己讀同一個 D1?
- Leo:triplet 其實也就是 template 加上一些函式,最簡單就是你給他一段文字抽取出三元組,但如果要自動化,就會提供 API,背後是工作流,例如 block 存入時就叫起 triplet 功能,這個功能如果自動化就是在安裝時同意,但應該也可以有 mcp 讓 AI 易於操作,如同前述,交貨時有 MCP,因為是 AI Friendly 的系統。
- init 建 D1 的冪等 + self-hosted 免綁卡確認:D1 是否如 KV 一樣免費不綁卡(需查證 CF D1 免費額度條件)。
- Leo:一樣不綁卡,除非他用量很大,屆時不是我來提醒而是 CF 會提醒他