Files
Arcrun/system-dev/wiki/decisions-summary.md
T
uncle6me-web 558e80b4da chore(wiki): wiki-init 補骨架 + system-dev-template 安裝/更新腳本
wiki 已初始化過(push 檔活躍維護),本次補從沒建的 pull 層 + arcrun 化範本:
- cards/decisions/ 14 張決策原子卡(含 gloss/實體/typed-edge 三元組):
  從 decisions-summary 全量改寫 13 + 新增「薄殼規則晚於實作-MCP漂移是歷史債」1
- TAXONOMY 從 PKM 範本換成 arcrun 軸(子系統 零件架構/cypher/credential/recipe/kbdb/
  薄殼/部署/平台原則 + 形態 架構決策/踩坑/機制說明/禁令/案例經驗)
- principles 填 13 條跨全局原則(從 rules/ + mindset 蒸餾)
- INDEX 真實視圖(子系統角度 + 決策角度,指向 cards)
- system-dev/scripts/ + scripts/ install/update 安裝腳本(template 接入)

純基建/文檔,無業務 code(功能 code 見前一 commit)。
raw source(docs/)0 異動、wiki 卡際連結無斷鏈。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 17:53:37 +08:00

15 KiB
Raw Blame History

name, description, metadata
name description metadata
decisions-summary 架構決策快速查 — 做選擇時的關鍵 trade-off(不是全文,是導引 + 連結)
type last_updated
reference 2026-06-26

架構決策快速查

用途:遇到「X 該怎麼設計?」時,快速找到已有決策的 trade-off。 來源DECISIONS.md(權威記錄)+ rule 02-07(執行細節)。


零件 vs 工作流(mindset §1

Q:新需求該包零件還是寫工作流?

決策:零件是稀有例外,工作流是 default。

工作流 零件
何時用 自用 / 少數人 / 試驗 全 arcrun 生態必須重用
構建成本 低(YAML 高(TinyGo/AS + WASI
生命周期 短(項目級) 長(平台級)
例子 RSS→Sheet、郵件分類、GitHub issue bot gmail、telegram、http_request

避坑:「有 API 可包」≠「該包」。mira 錯做成零件的 claude_api、km_writer、kbdb_upsert_block 本該是工作流。

詳見DECISIONS.md §1、mindset §1


Service Binding vs Cypher Bindingrule 03 §1

Q:零件之間怎麼串接?

決策

  • Cypher binding = YAML 裡的 URL 清單(用戶 workflow 定義)→ HTTP 呼叫
  • Service Binding = wrangler.toml 的 [[services]](平台內建邏輯零件之間效能優化)→ 禁止新增

Why

  • workflow 是用戶定義的動態圖,不能要求重新部署
  • Service binding 靜態,修改 toml 要 redeploy → 違反 workflow 靈活性
  • 13 個現有的 SVC_* binding(邏輯零件)是歷史遺產,保留但不擴展

新零件怎麼部署

  1. registry/components/{name}/main.go
  2. 編譯 .wasm
  3. 包進 .component-builds/{name}/ WorkerHono + WASI shim
  4. 部署成獨立 Workerarcrun-{name}.{sub}.workers.dev(對內) + {name}.arcrun.dev(對外)
  5. cypher 透過 HTTP fetch 呼叫

避坑

  • 不是「用 service binding 快」就用,那違反架構
  • self-hosted 同 zone 1042 不是加 binding 解,是加 flag(§ same-zone-1042

詳見rule 03 §1-3、2026-05-13-cypher-outbound-522.md


R2WASM_BUCKET)的用途(rule 03 §2

QWASM 零件怎麼存儲和部署?

決策

  • 平台內建零件bundle 進各自 Worker 的 binary.wasm commit 進 repo)→ 部署時用 codeload tarball
  • 用戶自製零件(Phase 5 以後):動態上傳到 R2runtime 執行

現在狀態

  • 平台零件不從 R2 讀
  • R2 只在 Phase 5 啟用

避坑

  • 不要問「怎麼從 R2 取 WASM」(錯誤假設)
  • 平台零件的 WASM 已 deploy 進 Worker,沒有 R2
  • 用 HTTP URL 呼叫,不是動態讀 R2

詳見rule 03 §2、rule 05 WASM 來源


Cypher 怎麼調用零件避開 same-zone 10422026-06-06

Qcypher 打 component worker 返回 522

決策

  • 官方cypher 在 cypher.arcrun.dev、component 在 {name}.arcrun.dev)→ 跨 zone,不踩
  • Self-hosted(都在 {sub}.workers.dev)→ 踩 same-zone 防護

解法(不是 service binding):

# cypher-executor/wrangler.toml
compatibility_flags = [ "nodejs_compat", "global_fetch_strictly_public" ]

這讓 same-zone fetch 走公網前門 → 同 zone 也通。

為什麼不用 binding

  • binding 靜態(toml 改要 redeploy
  • component 清單動態(用戶 workflow 決定)
  • 改變 architecture 必要性低(flag 無副作用)

避坑

  • self-hosted cypher 務必有 global_fetch_strictly_public
  • 若仍 522 → 檢查 flag 是否生效(某些 Wrangler 版本可能有 bug

詳見2026-05-13-cypher-outbound-522.md、2026-06-06 commit、rule 03 §3


多 Worker ENCRYPTION_KEY 同步(2026-05-29

Qauth_static_key / auth_service_account / cypher-executor 都需 ENCRYPTION_KEY,怎麼保持一致?

決策

  • secret 存在各 Worker 的 secret store(非環境變數,避免洩漏)
  • wrangler secret put ENCRYPTION_KEY 手動設進各 Worker
  • 初始化:acr init 生成,展示一次,user 自己 secret put

為什麼不用 KV

  • secret 是敏感內容,不應在 KV 存(會被 list 洩漏)
  • secret store 是 Cloudflare 的原生機制

冪等性

  • acr init 多跑幾次,生成不同 key(目前不冪等)
  • 若要冪等,init 應檢查現有 config → reuse 舊 key

避坑

  • init 完成後驗證所有三個 Worker 都有同一份 key
  • 若某個 Worker 的 key 遺漏或不同 → credential 解密失敗(會表現為 401/403)
  • 重跑 init 不要覆蓋舊 secret(目前沒有 checkneed improvement

詳見2026-05-29-encryption-key-drift.md、rule 01 加解密


Recipe UUID 模型(kbdb-base §7.5

Q:多作者同 canonical recipe 怎麼並存?

決策

  • 舊模型(已廢):canonical_id 是唯一鑰匙,新版覆蓋舊版
  • 新模型UUID):recipe:{uuid} 是真正鑰匙,同 canonical 多 uuid 並存

app-store 設計

  • canonical_id:服務名(gmail_send
  • uuid:版本身份(Leo 的 gmail_send ≠ John 的 gmail_send
  • author:誰做的
  • market_stat:per-uuid 的成功/失敗率(區分作者)

pull 邏輯

  • 公庫搜找 canonical → 回多 uuid
  • acr recipe pull gmail_send → 自動選市場最佳版本(成功率高)
  • acr recipe pull gmail_send --author john → 明確指定

向後相容

  • 舊 workflow 用 canonical_id → resolveRecipe 要能找到
  • migrate-uuid 端點把舊 key 轉新 uuid(冪等)

避坑

  • 不是簡單的 key 重構,是多維度身份(canonical + uuid + author
  • D1 per-uuid 記錄,不是 per-canonical(區分市場數據)
  • 新 submit-p 要幌露警示(mindset §6

詳見kbdb-base design §7.5、credential-primitives-wasm


薄殼原則(rule 07

QCLI / MCP / SDK 應該怎麼設計?

決策

  • 所有能力只實作一次,放在 APIcypher-executor
  • 介面層全是薄殼:參數解析 + HTTP 呼叫 + 格式轉換,零邏輯

違反例

  • CLI 迴圈 POST 多個 recipeseed 邏輯不該在介面)
  • MCP 先 update 失敗再 insertupsert 邏輯應在 API
  • SDK 自製 credential-injector(應在 WASM primitive

同一 API 在不同介面的簽名

  • 差異來自介面慣例(CLI 讀檔案、MCP 讀 JSON
  • 底層實作分歧(CLI 連自架、MCP 連官方 = 差不同帳號)

帳號統一

  • CLI / MCP / SDK 必須讀同一份身份設定(config.yaml / env
  • self-hosted 的已知問題:MCP 可能指官方(mcp-account-source §5.2

避坑

  • 介面間差異一大就改,不是「某個介面功能不全」就在那加邏輯
  • seed / upsert / 複雜編排 → 都去補 API,不是繞過

MCP「等於」CLI 嗎?——齊的單位是「能力」不是「端點」(2026-06-15 釐清)

Q:MCP 和 CLI 該不該完全一致?API 有的端點是不是都要兩邊各開一個?

結論

  • rule 07 §5 的「CLI + MCP 覆蓋同一組 API 能力」指該暴露給人/AI 的能力——不是「API 的每個端點都要上兩邊」。
  • 「介面進度暫時不一致」不是 bug(§5 明寫);bug 是「底層能力不齊 / 介面含不該含的邏輯 / 帳號來源不統一」這三者。所以 MCP=CLI 是出貨目標,不是任一時刻的不變量
  • 底層 API/proxy 可以有端點刻意不上 CLI/MCP——那是分層,不是疏漏。判準看「這端點的消費者是誰」。

實例(KBDB 資料層)

  • 該齊的齊了:MCP 6 個 kbdb_* 工具 ↔ CLI acr kbdb 6 個子命令,一一對應(template/record/query/search)。差異只來自介面慣例(MCP 吃 JSON、CLI 吃 --slots a,b,c),底層同一條 cypher /kbdb/* proxy。
  • /kbdb/entries 裸 CRUD 刻意兩邊都不接:它的消費者是 mira 的 Python client(直連 HTTP 遷移用),不是人/AI。CLI/MCP 給的抽象是更高階的 template/recordKBDB 鐵律:不暴露裸表操作)。硬把 entries CRUD 塞進 CLI/MCP 反而違反鐵律

附帶釐清(驗收邊界)MCP 工具經 env.KBDB service binding 那一跳是 worker 間內部專線,curl 從外面驗不到(無公開 URL)。要驗它得在真 MCP client 裡 call;或靠「同 kbdbFetch 路徑的既有工具已驗過」佐證 binding 活。

詳見:rule 07(完整)、壓測報告 §5.1 seed 反例、kbdb-base/tasks.md 9.1/9.2/9.6


Haiku 就能搞定是設計目標(mindset §1 + 壓測)

Q:為什麼要用 Haiku 壓測,不直接用 Sonnet

決策

  • arcrun 價值主張:比直接開發容易 → 省 token + 省時間
  • 若只有 Sonnet 能驅動,「省」就不成立(Sonnet 貴)
  • Haiku 就能搞定才是設計達成度的證明

測試邏輯

  • Haiku 過 → 設計目標達成
  • Haiku 不過 → 判別:是介面缺陷還是模型本質限制
    • 升 Sonnet,Sonnet 過 → 介面太難,要改介面
    • Sonnet 也不過 → 功能 bug,要改邏輯

避坑

  • 不要「Haiku 過不了就換 Sonnet」,那是放棄設計目標
  • Haiku 撞牆 = 發現「使用者自己搞不定」的點 → 要磨介面白痴化

詳見test_case.md 第一段、mindset §7 誠實限制


不依賴 CI(執行鏈路 vs 零件投稿)

Q:為什麼部署走 local script,不是 GitHub Actions

決策

  • 執行鏈路(高頻:workflow、recipe、API)→ 不依賴 CI(用戶實時操作)
  • 零件投稿(稀有:新零件)→ 走 PR/CI(人 merge = 閘門,CI 驗收)

執行鏈路不能走 CI

  • acr run 用戶實時呼叫,不能等 CI 跑(分鐘級延遲不可接受)
  • workflow 是 YAML,不是程式碼編譯

零件投稿走 CI

  • 零件投稿稀有(幾周一次)
  • PR check 能 runtime 跑 wasmCI 容器支援,CF Worker 不支援)
  • 把關(WASI 沙箱驗證、Gherkin 全綠、假零件檢測)由 CI 跑

避坑

  • 執行鏈路不要加 CI gate(會卡用戶)
  • 部署走 local script,不是 workflow dispatch
  • 零件投稿不要 self-service(要人 review

詳見rule 05 部署慣例、mindset §4


self-hosted 部署:共享 install + 內容指紋跳過(2026-06-12

結論acr update 不每個 worker 各裝依賴、不每次全部重部。改成「root 共享一次 install」+ 「內容指紋 manifest 跳過未變動」。

原因23 個 component worker 的 runtime dep 全是 honotier2 另需 zod/mcp-sdk/yaml),各裝 ~324MB node_modules 是 23× 重複;且冪等重跑被誤解成「每次重做全部」,22 個沒變的白跑。

機制

  • 共享 installtarball root 裝一次(hono+wrangler+tier2 deps),各 worker 靠 node 往上 resolve。 207MB×1 取代 324MB×23wrangler deploy --dry-run 驗證 tier1+tier2 都 bundle 成功)。
  • manifest~/.arcrun/deploy-manifest.json):注入後算 content hash,與上次成功比 → 相同跳過。 只記成功者(失敗下次必重試);hash 含 accountId(換帳號/KV 自動重部,不誤跳);--force 清空全重部。
  • 共享失敗自動退回各 worker 自裝(不破壞既有路徑)。

詳見cli/src/lib/deploy.tsdownloadAndDeploy §2.5 共享、deploy 迴圈 manifest)、mistakes §13


自力救濟階梯——缺能力怎麼補(2026-06-26issue #4

Q:缺一個能力,該補 API 還是別的?

主界線一句話「那個 API 你能不能改?」

  • 能打既有 API → recipe
  • 自家 API 缺 → 補進 API
  • 第三方 API 缺(gsheets filter / 無 upsert)→ 可投稿的 workflow 補丁(不是建零件!)
  • 純計算 → code-node(空白 code 零件寫 JS
  • 真需新穩定能力(極少)→ 零件 PR

避坑:§3.1 禁的是「介面層 TS 拼裝 + 亂建零件污染零件庫」,不是禁任何補丁。第三方 API 改不了, 不能被「只能 recipe」卡死。hook 7.x 只擋介面層 TSworkflow/code-node 是資料產物、不被擋。 code-node 規則已定、零件實作屬 wishlist C1 另案。詳見DECISIONS §9、07-thin-shell §3.5。


embedding 是 base optional 模組(2026-06-26issue #7

Q:語義查詢/embedding 放哪、怎麼開?

決策embedding 歸 base 的 optional 模組(非 graph/ingest)。binding 開/關不拆 repo。

  • VECTORIZE+AI binding 才啟用;沒有 → 降級 LIKE keywordAPI 不變
  • 預設關(free-tier 友善);acr init 問、kbdb_embed:true + acr update 開(deploy 建 Vectorize index)。
  • 精耕非地毯式:只 embed 標 metadata_json.embed:true 的 entrywiki 段落+gloss),不對每個 block 灌。
  • base 用通用 flag 不寫死 entry_type 白名單(守解耦,base 對內容語意無知)。
  • 不動三表(向量存 Vectorize);?mode=semantic 沒開 → 降級 keyword + capability_hint(發現閉環,不假綠)。

模型 @cf/baai/bge-base-en-v1.5768/cosine)。避坑deploy 注入 [ai]/[[vectorize]] binding 必須在 stripOfficialOnlyBindings 之後(否則 [ai] 被清,見 mistakes #17)。詳見DECISIONS §10、kbdb-base SDD Phase 12。


碰舊 Mira/SaaS KBDB 需求先查頂層覆寫(2026-06-26issue #5

Qmira dogfood 開的 KBDB 缺口還要做嗎?

決策Mira 已蒸發 → 當普世框架缺口處理,且先查頂層 mira-dissolve 是否已重審覆寫(別照舊 issue 悶頭做)。

  • source 過濾 做(json_extract 零建表,mistakes #18
  • documents 聚合 不做(走 graph MCP,頂層 R6 否決)
  • DELETE proxy ⏸擱置(依賴頂層 T8;裸 delete 無 owner 檢查=跨租戶刪除風險)
  • embed-on-write → 併 #7

詳見DECISIONS §11、docs/4-guides/kbdb-capabilities.md


快速決策樹

新功能怎麼做?
├─ 自用 / 少數人?
│  └─ 工作流(YAML)
├─ 全生態必須重用?
│  ├─ 已有服務提供串接?
│  │  └─ HTTP 調用 + recipe
│  └─ 要深度集成?
│     └─ 零件(TinyGo)
│
新零件怎麼部署?
├─ 平台內建?
│  └─ .component-builds/{name}/ Worker + HTTP URL
├─ 用戶自製?
│  └─ Phase 5(R2 動態讀)
│
多人協作怎麼同步?
├─ 資料(KV/D1)?
│  └─ 各自 Worker 綁定,不共用 secret
├─ 程式碼(執行邏輯)?
│  └─ 只在 APIcypher-executor),介面全薄殼
├─ 身份設定?
│  └─ 同一個 config(所有介面讀同一份)

進階參考

決策域 文件 優先閱讀
零件 rule 03、rule 05 要建新零件時
Recipe UUID kbdb-base §7.5 design.md 改 recipe 鑰匙時
Credential rule 01 、credential-primitives-wasm 加新 auth primitive 時
部署 rule 05、deploy.ts 新增 Worker 時
介面設計 rule 07、sd-and-website/design.md 改 CLI/MCP/SDK 時
事件 docs/incidents/ 遇到類似問題時