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>
This commit is contained in:
uncle6me-web
2026-06-27 17:53:37 +08:00
parent 934b9265d9
commit 558e80b4da
28 changed files with 3147 additions and 0 deletions
@@ -0,0 +1,34 @@
# 架構決策
> 桶子索引(容器:只連不重寫)。一決策一卡,自包含 + trade-off。
> 新決策由 /wiki-capture 寫進本桶並在此 append 一行。
> 權威全文:`system-dev/docs/2-architecture/decisions/DECISIONS.md`;本桶是 AI 改寫過的可檢索版。
## 零件 / 工作流 / 能力補丁
- [[工作流是default零件是例外]] — 自用→工作流,全生態重用→才零件;「能包」≠「該包」
- [[自力救濟階梯-缺能力怎麼補]] — 自家API缺→補API/第三方→workflow補丁/純計算→code-node
- [[薄殼原則-能力長在API]] — CLI/MCP/lib 只暴露,齊的單位是「能力」不是「端點」
- [[薄殼規則晚於實作-MCP漂移是歷史債]] — 為何 MCP/CLI 不一致:紀律 2026-06-07 才補、補前漂移
## 串接 / 部署
- [[service-binding-vs-cypher-binding]] — 零件走 HTTP URL,禁新增 service binding
- [[same-zone-1042用flag解不用binding]] — self-hosted cypher 加 global_fetch_strictly_public
- [[R2用途-平台零件不從R2讀]] — 平台零件 bundle 進 WorkerR2 只 Phase 5 用戶自製
- [[self-hosted部署-共享install加指紋跳過]] — root 共享 install + manifest content-hash 跳過未變動
- [[不依賴CI-執行鏈路vs零件投稿]] — 執行鏈路走 local script,零件投稿才走 PR/CI
## credential / recipe
- [[多worker-ENCRYPTION_KEY同步]] — 各 Worker secret put 同份 key,漂移→解密失敗表現為 401/403
- [[Recipe-UUID市場模型]] — canonical+uuid+author 多維身份,per-uuid 市場數據
## kbdb
- [[embedding是base-optional模組]] — binding 開才啟用,預設關降級 LIKE,精耕只 embed:true
- [[碰舊Mira需求先查頂層覆寫]] — mira 蒸發→當普世缺口,先查頂層 mira-dissolve 覆寫
## 平台目標
- [[Haiku能驅動是設計目標]] — Haiku 過=設計達成;撞牆是介面缺陷訊號非換模型
@@ -0,0 +1,39 @@
---
tags: [平台原則, 架構決策]
gloss: arcrun 能用 Haiku 驅動才算設計達成。撞牆不是換模型,是信號要改介面。
---
# Haiku 能驅動是設計目標
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`Haiku 就能搞定是設計目標)、`.claude/rules/06-mindset.md §1`
**最後更新**2026-06-27
## 摘要
arcrun 的價值主張是「比直接開發容易 + 省 token + 省時間」。只有 Sonnet 能驅動時,「省」就不成立。Haiku 通過才是設計目標達成的證明;反之,Haiku 撞牆=介面缺陷信號,應改介面而非換模型。
## 重點
- **設計思想**:Haiku 作為基礎模型,若某操作需要 Sonnet 才行,代表介面太難、太多隱含上下文、需要手工智能補齊——這是架構欠債。
- **測試邏輯**
- Haiku 通過 ✅ → 設計目標達成,介面夠清晰
- Haiku 不通 → 升 Sonnet
- Sonnet 通過 → 介面缺陷,要改介面白痴化
- Sonnet 也不通 → 功能 bug,要改邏輯
- **不要的做法**:「Haiku 過不了就換 Sonnet」= 放棄設計目標、接受介面複雜度只有豪華模型才能應付。
- **改進信號**:Haiku 哪個步驟卡住(參數寫法?狀態機理解?錯誤訊息太模糊?),就是該改哪個介面。
- **誠實限制**mindset §7):Haiku 撞牆不要假綠(mock 迴應),要誠實標「需要 Sonnet」或「無法 resolve」,作為改進指標。
## 實體
- **Haiku**Claude 3.5 Haiku)— 輕量級語言模型,arcrun 驅動的基準。
- **設計達成**design achievement)— 介面清晰度高到 Haiku 能自主操作,無需人工介入。
- **介面缺陷**interface defect)— Haiku 無法理解但 Sonnet 能理解的操作,表示介面隱含前置知識過多。
- **功能 bug**functional bug)— Sonnet 也無法做到的操作,表示後端邏輯確實有問題。
## 關聯
### 內文知識關係
- 設計達成 >> 以 Haiku 通過 >> 衡量
- Haiku 不通 >> 信號 >> 介面缺陷
- 介面缺陷 >> 應該 >> 改介面
- 功能 bug >> 應該 >> 改邏輯
### 卡片關係
- [[Haiku能驅動是設計目標]] >> 對應 mindset >> [[工作流是default零件是例外]]
@@ -0,0 +1,33 @@
---
tags: [零件架構, 部署, 架構決策]
gloss: 平台內建零件 WASM 已 bundle 進各自 Worker binary,不從 R2 動態讀。R2 只在 Phase 5(用戶自製零件)啟用。
---
# R2 用途 — 平台零件不從 R2 讀
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`R2 的用途)、`.claude/rules/03-component-architecture.md §2`
**最後更新**2026-06-27
## 摘要
平台內建零件的 WASM 文件在部署時已 bundle 進各自 Worker 的 binary(透過 codeload tarball);R2 存儲的機制只為 Phase 5(用戶上傳自製零件)保留。
## 重點
- 平台零件 = 獨立 Worker,已部署完成,走 HTTP URL 呼叫;沒有 R2 動態載入那一步。
- WASM 文件的位置:`registry/components/{name}/` 是原始碼,`.component-builds/{name}/component.wasm` 是編譯產物,commit 進 repo 後作為 codeload 部署來源。
- R2 存儲的唯一用途:Phase 5 啟用時,用戶透過 `acr component submit` 上傳自製 .wasm → R2 存儲 → runtime 動態執行(當時 API 才支援 `GET /user/{id}/components/{name}/wasm`)。
- 當前「怎麼從 R2 取 WASM」是典型誤問——错误假设的标志。
## 實體
- **平台內建零件**platform components)— 由 arcrun 官方維護、已編譯成 .wasm、獨立部署成 Worker 的零件(gmail、telegram、http_request 等)。
- **R2**Cloudflare R2)— 對象存儲服務,當前未用於平台零件;Phase 5 起用於存放用戶自製零件。
- **codeload 部署**tarball deployment)— 透過 GitHub codeload tarball 或 git clone + local build,把平台零件連同依賴打進 Worker binary 的部署方式。
- **用戶自製零件**user-submitted components)— Phase 5 後用戶上傳的自定義 WASM 零件(動態來源)。
## 關聯
### 內文知識關係
- 平台內建零件 >> 不從 >> R2
- R2 >> 保留用於 >> 用戶自製零件
- 平台內建零件 >> 透過 codeload 部署 >> 獨立 Worker
### 卡片關係
- [[R2用途-平台零件不從R2讀]] >> 架構前提來自 >> [[service-binding-vs-cypher-binding]]
@@ -0,0 +1,44 @@
---
tags: [recipe, 平台原則, 架構決策]
gloss: 多作者同 canonical recipe 用 UUID 並存,市場統計 per-uuid 區分版本,用戶 pull 時自動選最佳版本或按 author 指定。
---
# Recipe UUID 市場模型
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`Recipe UUID 模型)、`docs/3-specs/arcrun/kbdb-base/` design.md §7.5
**最後更新**2026-06-27
## 摘要
Recipe 的身份由 canonical_id + uuid + author 三維度組成。同一 canonical 的多個版本用不同 uuid 存在 D1,市場統計(成功率)per-uuid 記錄,用戶 pull 時自動選市場最佳版本或明確按 author 指定。
## 重點
- **舊模型(已廢)**canonical_id 是唯一鑰匙,新版覆蓋舊版,多作者無法共存。
- **新模型(UUID**
- `canonical_id`:服務名標籤(gmail_send
- `uuid`:版本身份(Leo 的 gmail_send ≠ John 的 gmail_send
- `author`:誰寫的
- 真正的 KV/D1 鑰匙 = `recipe:{uuid}`
- **App Store 設計**
- `acr recipe pull gmail_send` → 後端搜 canonical_id=gmail_send 的所有 uuid → 按市場統計排序 → 回傳成功率最高的版本(自動選)
- `acr recipe pull gmail_send --author john` → 明確指定 John 的版本
- **市場統計 per-uuid**:每個 uuid 的 execution stat 分開記錄(success/fail/latency)→ 才能區分作者貢獻
- **向後相容**:舊 workflow 用 canonical_id → resolveRecipe 要能找到;migrate-uuid endpoint 把舊 key 轉新 uuid(冪等)
- **暴露警示**mindset §6):新 `submit-recipe` 需要用戶明示同意暴露(讓市場統計追蹤效果)
## 實體
- **Canonical ID**(规范ID)— 服務名標籤(gmail_send、slack_post),用於搜索,不是 KV 鑰匙。
- **UUID**(通用唯一標識符)— Recipe 版本身份,真正的 KV/D1 鑰匙 `recipe:{uuid}`
- **Author**(作者)— Recipe 的創作者身份,用於區分多作者同 canonical 的版本。
- **Market stat**(市場統計)— per-uuid 的成功率/失敗率/延遲,用於自動版本選擇。
- **App Store**(應用市場)— 公開 recipe 倉庫及版本選擇機制。
## 關聯
### 內文知識關係
- Canonical ID >> 標籤 >> UUID 群組
- UUID >> 記錄 >> Market stat
- Market stat >> 驅動 >> 自動版本選擇
- Author >> 並存於 >> 同 canonical 不同 UUID
- 舊 workflow >> 需要 >> 向後相容
### 卡片關係
- (相關 memory`recipe-trust-via-market-not-verification` — recipe 信任靠市場非人力防偽;屬 auto-memory 非 card
@@ -0,0 +1,43 @@
---
tags: [kbdb, 架構決策]
gloss: embedding 機制歸 KBDB base 的 optional 模組,有 Vectorize binding 才啟用;無則降級 LIKE keywordAPI 不變。
---
# Embedding 是 Base Optional 模組
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`embedding 是 base optional 模組)、`docs/3-specs/arcrun/kbdb-base/` Phase 12
**最後更新**2026-06-27
## 摘要
語義查詢/embedding 不是獨立模組或圖插件,而是 KBDB base 層的 optional 擴展。Vectorize + AI binding 存在時啟用;缺少時降級 keyword search + capability hint(發現閉環,不假綠)。
## 重點
- **歸屬**embedding 機制屬 base 層(核心表 entries/templates/records),不是 graph 層(triplet 插件)、不是 ingest 層(爬蟲)。
- **啟用機制**
- 部署時檢查 KBDB binding 是否含 `[ai]` + `[[vectorize]]` → 有則啟用 semantic search
- 無則 API 降級為 keyword LIKE + `capability_hint`(告知使用者「語義搜尋未啟用」)
- **API 簽名不變**(無論啟用否,查詢端點同一個 `/search`),降級轉換在後端
- **部署陷阱**:部署注入 `[ai]`/`[[vectorize]]` binding 必須在 `stripOfficialOnlyBindings` **之後**(否則 [ai] 被清)。
- **精耕策略**(非地毯式):只 embed 標 `metadata_json.embed:true` 的 entrywiki 段落 + gloss),不對每個 block 灌。
- **無白名單**base 用通用 flag 判 embed:true,不寫死 entry_type 白名單(守解耦,base 對內容語意無知)。
- **向量存儲位置**:向量獨存 Vectorize,三表(entries/templates/records)不改動。
- **發現閉環**:無 embedding 時不假綠(回傳空結果假裝查到),而是明確 hint「語義未啟用」→ capability-driven 發現系統能自動 degrade。
## 實體
- **Base 層**base layer)— KBDB 核心表(entries/templates/records)及其基本操作。
- **Optional 模組**optional module)— 功能開/關不拆 repo,降級機制在 API 內部。
- **Vectorize**Cloudflare Vectorize)— 向量資料庫 binding,儲存 embedding。
- **Semantic search**(語義搜尋)— 基於 embedding 的向量相似度查詢。
- **Capability hint**(能力提示)— API 回傳中明確標示「此能力未啟用」,供 client 判斷。
- **Embed flag**embed:true)— 在 entry metadata_json 中設置,標示是否參與 embedding。
## 關聯
### 內文知識關係
- Vectorize binding >> 啟用 >> Semantic search
- 無 Vectorize >> 降級為 >> Keyword search
- Embed flag >> 控制 >> 哪些 entry 參與 embedding
- API >> 不變 >> 無論 embedding 啟用否
- Capability hint >> 發現 >> 語義搜尋未啟用
### 卡片關係
- [[embedding是base-optional模組]] >> 涉及部署 >> [[self-hosted部署-共享install加指紋跳過]]
@@ -0,0 +1,37 @@
---
tags: [cypher, 部署, 架構決策, 踩坑]
gloss: self-hosted cypher 和 component 同在 workers.dev zone 觸發 CF 1042,用 global_fetch_strictly_public flag 解,不是 Service Binding。
---
# Same-Zone 1042 用 flag 解不用 Binding
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`(Cypher 怎麼調用零件避開 same-zone 1042)、`docs/incidents/2026-05-13-cypher-outbound-522.md`
**最後更新**2026-06-27
## 摘要
Cloudflare Workers 的 same-zone fetch 防護(HTTP 522)在 self-hosted 部署中會踩坑,因為 cypher 和 component 都在 `{sub}.workers.dev`。解法是在 cypher-executor 的 wrangler.toml 加 `global_fetch_strictly_public` flag,讓 same-zone fetch 走公網前門——而非用 Service Binding(那樣更死)。
## 重點
- **官方部署無此問題**:cypher 在 `cypher.arcrun.dev`、component 在 `*.arcrun.dev``arcrun-*.workers.dev`,跨 zone,不踩防護。
- **Self-hosted 部署踩坑**:都在 `{sub}.workers.dev`,CF 防止同 zone 自循環死鎖,cypher fetch component 返 522。
- **為什麼不用 Service Binding**Binding 靜態、修改要 redeploycomponent 清單動態(用戶 workflow 決定),改變架構必要性低;flag 無副作用(official 加也沒影響,本就跨 zone)。
- **Flag 部署法**`cypher-executor/wrangler.toml` 加一行 `compatibility_flags = [ "nodejs_compat", "global_fetch_strictly_public" ]`,然後 cypher 和 auth worker 間的 HTTP fetch 都能通。
- **驗證**:若仍 522 → 檢查 flag 是否生效(某些 Wrangler 版本可能有 bug)。
## 實體
- **Same-zone 防護**Cloudflare same-zone protection)— CF Workers 內建的防護機制,禁止 Worker 在同一 zone 內 fetch 另一個 Worker 以防自循環。
- **HTTP 522**522 Connection Timed Out)— Cloudflare 自有錯誤,表示 fetch 因防護被擋。
- **global_fetch_strictly_public flag**Cloudflare compatibility flag)— 讓 same-zone fetch 從內部網路改走公網前門的相容旗標,無副作用。
- **cypher-executor**Cypher 執行引擎)— 調度 workflow 並呼叫零件的 Worker,需加 flag 才能在 self-hosted fetch component。
- **動態 component 清單**dynamic component list)— workflow 執行時的零件集合,由用戶定義、會變動,無法靜態綁定。
## 關聯
### 內文知識關係
- same-zone 防護 >> 在 self-hosted >> 觸發 HTTP 522
- global_fetch_strictly_public flag >> 解決 >> HTTP 522
- global_fetch_strictly_public flag >> 不改變 >> 官方部署
- Service Binding >> 不能解決 >> 動態 component 清單
### 卡片關係
- [[same-zone-1042用flag解不用binding]] >> 迴避 >> [[service-binding-vs-cypher-binding]]
- [[same-zone-1042用flag解不用binding]] >> 涉及事件 >> [docs/incidents/2026-05-13-cypher-outbound-522.md]
@@ -0,0 +1,43 @@
---
tags: [部署, 架構決策]
gloss: 23 個 component worker 不各裝依賴,改成 root 共享一次 install + manifest 內容指紋跳過未變動——減 23× node_modules 膨脹。
---
# Self-Hosted 部署 — 共享 install 加指紋跳過
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`self-hosted 部署)、`cli/src/lib/deploy.ts` §2.5
**最後更新**2026-06-27
## 摘要
tarball 解開後不每個 component worker 各裝 node_modules23× 重複 ~324MB),改成 root 共享一次 install;同時用 content hash manifest 記錄部署狀態,相同 hash 跳過已部署 worker,只部署內容變動者。
## 重點
- **問題**23 個 component worker 的 runtime dep 全是 honotier2 另需 zod/mcp-sdk/yaml),各裝 ~324MB node_modules = 23× 重複;且冪等重跑被誤解成「每次重做全部」,22 個沒變的白跑一遍。
- **共享 install 機制**
- tarball 根目錄裝一次 hono + wrangler + tier2 deps
- 各 worker 靠 Node.js 往上搜索路徑 resolve dependencies
- `wrangler deploy --dry-run` 驗證 tier1+tier2 都被 bundle(確保包完整)
- 結果:207MB×1 取代 324MB×23
- **Manifest 跳過**
- `~/.arcrun/deploy-manifest.json` 記錄 content hash + 上次成功時間戳
- 新解開 tarball → 算 content hash 與上次成功者比較 → 相同跳過、不同重部
- Hash 含 accountId(換帳號/KV namespace → 自動重部,不誤跳)
- `--force` flag 清空 manifest → 全部重新部署
- **失敗恢復**:共享 install 失敗 → 自動退回各 worker 自裝(不破壞既有路徑)
- **只記成功**:manifest 只追蹤成功部署者,失敗者下次必重試
## 實體
- **共享 install**shared installation)— root 目錄的單一 node_modules,所有 worker 共享。
- **Manifest**(部署清單)— `~/.arcrun/deploy-manifest.json`,記錄 content hash 和成功部署狀態。
- **Content hash**(內容雜湊)— 本次 tarball 內容的唯一指紋,用於比較是否變動。
- **Component worker**(零件 Worker)— `.component-builds/{name}/` 下各個獨立部署單位。
## 關聯
### 內文知識關係
- 共享 install >> 減少 >> 重複 node_modules
- Manifest >> 跳過 >> 未變動 component worker
- Content hash >> 相同時 >> 自動跳過部署
- accountId >> 不同時 >> 強制重新部署
### 卡片關係
- [[self-hosted部署-共享install加指紋跳過]] >> 優化 >> [[不依賴CI-執行鏈路vs零件投稿]]
@@ -0,0 +1,36 @@
---
tags: [零件架構, cypher, 架構決策]
gloss: 零件串接用 Cypher bindingYAML URL 清單)not Service Bindingwrangler.toml)——後者靜態、不適合動態 workflow。
---
# Service Binding vs Cypher Binding
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`Service Binding vs Cypher Binding)、`.claude/rules/03-component-architecture.md §1`
**最後更新**2026-06-27
## 摘要
Cypher binding 是 YAML/KV 裡的 URL 清單(用戶定義、動態);Service Binding 是 wrangler.toml 的 `[[services]]`(平台內建、靜態)。workflow 層一律走 HTTP URL,禁止新增 Service Binding。
## 重點
- Cypher binding 用 HTTP 呼叫,workflow 是用戶定義的動態圖,不能要求每改 workflow 就 redeploy。
- Service Binding 靜態、修改 wrangler.toml 要重新部署——完全違反 workflow 靈活性,故只保留 13 個邏輯零件間的歷史遺產綁定、禁止擴展。
- 新零件部署流程:`registry/components/{name}/main.go` → 編譯 `.wasm` → 包進 `.component-builds/{name}/` WorkerHono + WASI shim)→ 獨立部署成 `arcrun-{name}.{sub}.workers.dev`(對內)+ `{name}.arcrun.dev`(對外)→ cypher 透過 HTTP fetch 呼叫。
- self-hosted 同 zone 1042 不是加 Service Binding 解——那樣更死。改用 flag 解(見卡「same-zone-1042用flag解不用binding」)。
## 實體
- **Cypher binding**workflow binding)— YAML/KV 裡存的 URL 清單,workflow 執行時動態查表呼叫零件。
- **Service Binding**Cloudflare service binding)— wrangler.toml 的 `[[services]]` 區塊,靜態綁定、修改須重新部署。
- **零件串接**component invocation)— workflow 在執行時呼叫零件的過程。
- **HTTP 呼叫**HTTP fetch)— 用 fetch() 透過公網 URL 呼叫零件,cypher 實踐零件串接的標準方式。
- **新零件**new component)— 新部署到 arcrun 系統的 WASM 零件(如新的第三方服務串接)。
## 關聯
### 內文知識關係
- Cypher binding >> 用 HTTP 呼叫 >> 零件串接
- Service Binding >> 靜態化 >> 零件串接
- 新零件 >> 禁止用 >> Service Binding
- Cypher binding >> 支撑 >> workflow
### 卡片關係
- [[service-binding-vs-cypher-binding]] >> 架構前提 >> [[R2用途-平台零件不從R2讀]]
- [[service-binding-vs-cypher-binding]] >> 相關事件 >> [[same-zone-1042用flag解不用binding]]
@@ -0,0 +1,36 @@
---
tags: [部署, 平台原則, 架構決策]
gloss: 執行鏈路(workflow/recipe/API 呼叫)不依賴 CI,走本地 script 部署。零件投稿(稀有)才走 PR/CI 讓人做閘門。
---
# 不依賴 CI — 執行鏈路 vs 零件投稿
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`(不依賴 CI)、`.claude/rules/05-deploy-convention.md`
**最後更新**2026-06-27
## 摘要
用戶操作(workflow、recipe、API 呼叫)頻繁,走 CI 會有分鐘級延遲,不可接受。故執行鏈路用本地 script(`scripts/local-deploy.sh`)同步到 Hetzner。零件投稿稀有、走 PR/CI 把關,人 merge=天然閘門,CI 驗收 WASI 沙箱。
## 重點
- **執行鏈路**(高頻):用戶實時操作(`acr run``acr workflow push`、API call),不能等 CI 跑(分鐘延遲 unacceptable)→ 用本地 script 直接同步。
- **本地部署機制**`scripts/local-deploy.sh` 透過 `scp` / `rsync over ssh` 把更新推到 Hetzner `/opt/mira` 等目錄,直接、快速。
- **零件投稿**(稀有):幾周到幾月一次新零件 → 走 GitHub PR → 人 review + merge → CI 驗收(WASI 沙箱、Gherkin、假零件檢測)。
- **為什麼 CI 驗收零件**CI 容器支援 runtime 執行 wasm、檢驗行為;CF Worker 沒有 wasm runtimesandboxed 沒有 syscall),無法驗證。
- **人 merge 的價值**GitHub merge 需人類 approve,無法被 AI 偽造,天然的人類閘門。
- **不走 GitHub Actions 執行鏈路**Actions 容易被濫用(當初 richblack 被 flag 的成因),且分鐘延遲。
## 實體
- **執行鏈路**execution path)— 用戶實時操作的頻繁路徑:workflow 建立、recipe 推送、API 呼叫。
- **本地部署**local deployment)— 透過 ssh/scp 直接從本機同步到伺服器,無 CI 介入。
- **零件投稿**component submission)— 新零件上傳的稀有操作,走 GitHub PR。
- **PR/CI 把關**PR/CI gatekeeping)— 零件投稿需人 merge 且 CI 驗收,雙層防護。
## 關聯
### 內文知識關係
- 執行鏈路 >> 高頻且實時 >> 不走 CI
- 本地部署 >> 支撑 >> 執行鏈路
- 零件投稿 >> 稀有且一次性 >> PR/CI 把關
- 人 merge >> 提供 >> 人類閘門
### 卡片關係
- [[不依賴CI-執行鏈路vs零件投稿]] >> 同源於避免被flag鐵律 >> [[self-hosted部署-共享install加指紋跳過]]
@@ -0,0 +1,35 @@
---
tags: [credential, 部署, 架構決策, 踩坑]
gloss: 多個 Workerauth primitive + cypher-executor)共享 ENCRYPTION_KEY,用 wrangler secret put 手動設進各 Worker secret store,不用 KV。
---
# 多 Worker ENCRYPTION_KEY 同步
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`(多 Worker ENCRYPTION_KEY 同步)、`.claude/rules/01-tech-stack.md` 加解密規範
**最後更新**2026-06-27
## 摘要
Auth static_key / Auth service_account / cypher-executor 三個 Worker 都需 ENCRYPTION_KEY 來解密 credential。用各 Worker 的 secret store(非環境變數)存放;初期化時 `acr init` 生成一份 key,用 `wrangler secret put ENCRYPTION_KEY` 各設一次。
## 重點
- **Secret 存儲位置**:各 Worker 的 secret storeCloudflare 原生機制),不是環境變數、不是 KV。環境變數會洩漏到日誌;KV 的 list 操作可能外洩。
- **初始化流程**`acr init` 生成一份 32 字節隨機 key → 印出一次 → 用戶自己跑 `wrangler secret put ENCRYPTION_KEY --path <path>` 分別設進三個 Worker。
- **冪等性問題**:目前 `acr init` 多跑幾次會生成不同 key(不冪等)。長期應改成「init 檢查現有 config → 若存在 key 則重用舊的、否則生成新的」。
- **漂移陷阱**:若某個 Worker 的 key 遺漏或與其他 Worker 不同 → credential 解密失敗 → 表現為 401/403(用戶困惑,難debug)。
- **驗證方法**init 完成後做一個 test workflow 打一個認證過的 API(如 gmail),確認三個 Worker 的 key 一致。
## 實體
- **ENCRYPTION_KEY**(加密密鑰)— 32 字節 AES-GCM 密鑰,用來解密用戶的 credential。
- **Secret store**Cloudflare 密鑰存儲)— Worker 的原生機制,用 `wrangler secret put` 設置、runtime 讀取,內容不外洩。
- **密鑰漂移**key drift)— 多個 Worker 持有不同版本的 ENCRYPTION_KEY,導致某些 Worker 解密失敗。
- **冪等性**idempotency)— `acr init` 多次運行應產生同一結果(目前不達成)。
- **解密失敗**decryption failure)— Worker 因 key 不匹配無法解密 credential,表現為 401/403 錯誤。
## 關聯
### 內文知識關係
- ENCRYPTION_KEY >> 分散存儲於 >> Secret store
- 密鑰漂移 >> 導致 >> 解密失敗
- 冪等性 >> 缺乏時 >> 重跑 init 造成 key 不一致
### 卡片關係
- (相關 memory`encryption-key-drift-trap` — 解密失敗先比 key 指紋;屬 auto-memory 非 card
@@ -0,0 +1,33 @@
---
tags: [平台原則, 零件架構, 架構決策]
gloss: arcrun 的開發預設順序——串服務先寫工作流,只有「全生態必須重用」才把能力編譯成 WASM 零件。
---
# 工作流是 default 零件是例外
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`(零件 vs 工作流)、`.claude/rules/06-mindset.md §1`
**最後更新**2026-06-27
## 摘要
新需求的預設解是寫工作流(YAML 串 HTTP);零件是稀有例外,只在「全 arcrun 生態必須重用」時才建。
## 重點
- AI 的典型走歪=把「需要一個能力」直接翻成「做一個零件」,把「能包」當成「該包」。
- 工作流構建成本低(YAML)、生命週期短(項目級);零件成本高(TinyGo/AS + WASI)、生命週期長(平台級)。
- 判準口訣:「他人會重複打這個服務嗎?」否→工作流;是且必要重用→才考慮零件。
- 反例:mira 的 `claude_api``km_writer``kbdb_upsert_block` 本是自用膠水,卻被錯做成零件。
## 實體
> 本卡內文關鍵實體(也是 graph node)。名+描述供下游 normalize。
- **工作流**workflowYAML 串接)— 用 HTTP + 流程控制 primitive 串服務的資料產物,arcrun 的預設開發單位。
- **零件**componentWASM component)— 編譯成 .wasm 的可重用能力單元,獨立部署成 Worker,稀有例外。
- **必要重用**(全生態重用)— 「全 arcrun 生態的人都會打同一服務」這個門檻,是建零件的唯一正當理由。
## 關聯
### 內文知識關係
- 零件 >> 對立於 >> 工作流
- 必要重用 >> 是建零件的前提 >> 零件
### 卡片關係
- [[工作流是default零件是例外]] >> 是其特例 >> [[自力救濟階梯-缺能力怎麼補]]
- [[工作流是default零件是例外]] >> 共享世界觀 >> [[薄殼原則-能力長在API]]
@@ -0,0 +1,44 @@
---
tags: [kbdb, 平台原則, 架構決策]
gloss: Mira 已蒸發,開的 KBDB 缺口當普世框架缺口處理;先查頂層 mira-dissolve 是否已重審覆寫,別照舊 issue 悶頭做。
---
# 碰舊 Mira 需求先查頂層覆寫
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`(碰舊 Mira/SaaS KBDB 需求先查頂層覆寫)、`docs/4-guides/kbdb-capabilities.md`
**最後更新**2026-06-27
## 摘要
Mira 作為內部 AI 操盤手(LLM interface)已蒸發(重組為頂層決策單位),遺留的 KBDB 缺口不應照舊需求表悶頭做。應先檢查頂層 mira-dissolve 是否已重審、新的優先序、甚至否決某些需求。
## 重點
- **Mira 蒸發背景**:Mira 從實裝層升到頂層架構決策者(LLM Interface 角色),其原有 dogfood 缺口現交 arcrun 框架層補齊,但優先序要頂層重審。
- **已決定執行**
- **source 過濾** ✅ 做(`json_extract` 零建表,mistakes #18
- **documents 聚合** ❌ 不做(走 graph MCP,頂層 R6 否決)
- **DELETE proxy** ⏸ 擱置(依賴頂層 T8 認可;裸 delete 無 owner 檢查 = 跨租戶刪除風險)
- **embed-on-write** → 併入 embedding 卡片(#7
- **為什麼要先查**
- 頂層 mira-dissolve 文件可能已否決或重排某些需求
- 不檢查直接做 = 做了也可能被推翻、浪費時間
- 「普世框架缺口」vs「Mira 特例缺口」要區分
- **DELETE proxy 風險**:裸 `DELETE /entries/{id}` 無 owner 檢查 → 多租戶環境下可跨租戶刪除。需頂層 T8 任務明確認可 + owner 隔離機制才能做。
## 實體
- **Mira**(內部 AI 操盤手)— arcrun 的早期 dogfood 客戶,已升級為頂層架構決策單位。
- **Mira-dissolve**Mira 蒸發重組)— 頂層決策文件,記錄 Mira 遺留需求的重審結果。
- **頂層覆寫**upstream override)— 頂層對某需求的最終決定(做/不做/改方向/等待)。
- **普世框架缺口**universal framework gap)— KBDB 對所有用戶都適用的功能缺陷。
- **Mira 特例缺口**Mira-specific gap)— 只對 Mira 的特殊工作流需要的功能。
## 關聯
### 內文知識關係
- Mira 蒸發 >> 遺留 >> 需求積壓
- 頂層覆寫 >> 決定 >> 普世框架缺口
- DELETE proxy >> 涉及 >> 跨租戶刪除風險
- source 過濾 >> 已批准 >> 執行
- documents 聚合 >> 已否決 >> 不執行
- DELETE proxy >> 擱置於 >> 頂層 T8 認可
### 卡片關係
- [[碰舊Mira需求先查頂層覆寫]] >> 涉及文件 >> [InkStoneCo/docs/3-specs/mira-dissolve]
@@ -0,0 +1,48 @@
---
tags: [平台原則, 架構決策]
gloss: 缺能力的補救階梯:自家 API 缺→補進 API;第三方 API 缺→workflow/code-node 補丁;純計算→code-node;才建零件。
---
# 自力救濟階梯 — 缺能力怎麼補
← [[decisions/00-INDEX]]
**來源**`system-dev/wiki/decisions-summary.md`(自力救濟階梯)、`.claude/rules/07-thin-shell.md §3.5`
**最後更新**2026-06-27
## 摘要
缺一個能力時的補救路徑分級。主問「那個 API 你能改嗎」:能改→補 API;改不了(第三方)→走 workflow/code-node 補丁;不入任何 API 的純計算→code-node;真需新穩定能力(極少)→零件 PR。
## 重點
- **階梯概覽**
| 情況 | 正解 | 為何 |
|---|---|---|
| 能打既有 API | **recipe**(無則建) | 單一 API 呼叫的封裝 |
| 自家 API 缺能力 | **補進 API** + 可同時發 issue | 你能改,能力該長在 API |
| 第三方 API 缺能力 | **可投稿的 workflow 補丁** | 改不了第三方,用資料方式自救 |
| 純計算(如文本轉大寫) | **code-node**(空白零件寫 JS) | 不該為此建一堆專用零件 |
| 真需新穩定能力(極少) | 自建零件 → PR | 維持零件庫最小 |
- **第三方 API 缺能力的實例**Google Sheets 一次只能倒全部、輸出前無法 filter;原廠不開此 API → arcrun 補不進去 → 正解是投稿一個 workflow 補丁「倒出後自己篩」。
- **Upsert 範例**(區分自家/第三方):
- Stripe 原廠提供 upsert API → recipe 直打
- Notion 沒 upsert API → 做一個 upsert workflow(先 GET 找、有則 PATCH 無則 POST),不是建零件
- **Hook 無法防守的**:§3.1 禁的是「介面層 TS 拼裝」(hook 7.x 擋),不是禁「資料方式補丁」。workflow/code-node 是 YAML/空白零件內的 JS,非介面層 TS → hook 範圍外、合法不被擋。
- **補丁轉正**:原廠出 API 後,補丁 workflow 因效能較差自然淘汰;不需特別移除。
## 實體
- **自家 API**arcrun-owned API)— arcrun 官方開發的 API(如 cypher-executor 端點、KBDB 基本盤),改動權掌握在 arcrun。
- **第三方 API**third-party API)— 外部服務提供的 APIGoogle Sheets、Stripe、Notion),改不了。
- **Workflow 補丁**workflow patch)— 用戶投稿的 workflow YAML,用來補第三方 API 缺的能力,可被多人重用。
- **Code-node**(代碼節點)— 空白 zero-logic 零件內直接寫 JavaScript,用於純計算。
- **零件庫污染**component bloat)— 為了單一功能、改不了的第三方 API 新建零件,長期難維護。
## 關聯
### 內文知識關係
- 自家 API 缺 >> 正解是 >> 補進 API
- 第三方 API 缺 >> 無法補 API >> 走 workflow 補丁
- 純計算 >> 應該 >> code-node
- 零件庫污染 >> 來自 >> 為第三方缺口建零件
- 補丁 workflow >> 因效能差 >> 自然淘汰
### 卡片關係
- [[自力救濟階梯-缺能力怎麼補]] >> 補充 >> [[工作流是default零件是例外]]
- [[自力救濟階梯-缺能力怎麼補]] >> 關聯 >> [[薄殼原則-能力長在API]]
@@ -0,0 +1,36 @@
---
tags: [薄殼, 平台原則, 架構決策]
gloss: 所有業務能力只實作一次放在 APIcypher-executor),CLI/MCP/SDK 全是只做介面轉換的薄殼。
---
# 薄殼原則 — 能力長在 API
← [[decisions/00-INDEX]]
**來源**`.claude/rules/07-thin-shell.md``system-dev/wiki/decisions-summary.md`(薄殼原則)
**最後更新**2026-06-27
## 摘要
能力只實作一次放在 APICLI / MCP / Python lib / JS lib 全是薄殼,只做參數解析 + HTTP 呼叫 + 格式轉換 + client 端加密,零業務邏輯。
## 重點
- 判準口訣:「這段邏輯換一個介面(CLI→MCP)要不要重寫?」要→它是能力,該在 API;不用→它是薄殼該做的。
- 違反例:CLI 迴圈 POST 多 recipeseed 該是 API 行為)、MCP 先 update 失敗再 insertupsert 該在 API)、SDK 自製 credential-injector(該在 WASM)。
- 齊的單位是「**能力**」不是「**端點**」:MCP=CLI 是出貨目標、不是任一時刻的不變量;底層 proxy 可有端點刻意不上 CLI/MCP(如 `/kbdb/entries` 裸 CRUD 消費者是 mira Python client)。
- 帳號統一:所有薄殼讀同一份身份來源(config.yaml / env),不可 CLI 連自架、MCP 連官方。
- hook 7.x 擋語法層可偵測的拼裝;藏在 helper 裡的邏輯擋不了,靠 code review。
## 實體
- **薄殼**thin shell)— CLI/MCP/SDK 介面層,只暴露能力不實作能力。
- **能力**business logic/業務邏輯)— 換介面要重寫的那段邏輯,必須下沉到 API 只實作一次。
- **API**cypher-executor HTTP 端點)— 能力的唯一真相源、所有薄殼共同呼叫的後端。
- **端點**endpoint)— API 的單一 HTTP 路由;齊的單位是能力不是端點,端點可刻意不上某介面。
## 關聯
### 內文知識關係
- 薄殼 >> 呼叫 >> API
- 能力 >> 下沉到 >> API
- 能力 >> 齊的單位是 >> 能力
- 端點 >> 不等於齊的單位 >> 能力
### 卡片關係
- [[薄殼原則-能力長在API]] >> 共享世界觀 >> [[工作流是default零件是例外]]
- [[薄殼原則-能力長在API]] >> 歷史成因見 >> [[薄殼規則晚於實作-MCP漂移是歷史債]]
@@ -0,0 +1,37 @@
---
tags: [薄殼, 平台原則, 踩坑, 案例經驗]
gloss: MCP 與 CLI 不一致的根因不是開發先後,而是薄殼紀律(rule 07)2026-06-07 才從壓測釐清,補立之前兩介面各自接後端而漂移。
---
# 薄殼規則晚於實作 — MCP 漂移是歷史債
← [[decisions/00-INDEX]]
**來源**`system-dev/docs/5-records/test-reports/壓測-recipe-library-2026-06-07.md` §5、`.claude/rules/07-thin-shell.md` 檔頭、decisions-summary(薄殼原則 2026-06-15 釐清)
**最後更新**2026-06-27
## 摘要
MCP 和 CLI 不一致的根因不是「MCP 更早開發所以舊」,而是**薄殼原則本身是 2026-06-07 壓測時才釐清的**——規則補立之前,兩個介面各自接後端、自然漂移,留下歷史債。
## 重點
- **規則晚於實作**:rule 07 檔頭自承來源是「壓測報告 §5.4/§5.5,設計者本人於壓測中釐清」。薄殼不是開局戒律,是撞牆後回頭講清楚的產物。
- **病徵(壓測 §5.1 原記)**:CLI 改了讀全域/專案/.env 身份,**MCP 沒跟上**,兩者打不同帳號。能力在兩介面各寫各的,沒有「先有 API、介面只暴露」的紀律約束。
- **化石證據**MCP `u6u_deploy_workflow``/workflows/deploy`(從不存在的端點 → 404),CLI `acr push``/webhooks/named`(活的)。兩條部署路徑底層根本不同 = 規則沒到位前各長各的(2026-06-27 issue #8 實作期挖出)。
- **同根的其他漂移**self-hosted MCP 連官方帳號([[薄殼原則-能力長在API]] §4、mistakes §5);YAML→graph 編排被寫在 CLI push.ts 介面層、MCP 期待 server 端點 → 兩邊都想自己編排。
- **校正常見誤解**:不是「先後」造成不一致,是「**紀律後補、補前漂移**」。修法是回頭把能力下沉 API(rule 07),不是怪某介面舊。
- **查證限制**`.claude/`/`docs/`/`system-dev/` 全 gitignored,git 無這些檔歷史 → 「精確定立時間」查不到,只能靠檔內日期註記(2026-06-07 壓測、2026-06-15 「MCP=CLI 是出貨目標非不變量」釐清)。
## 實體
- **薄殼原則**(rule 07/薄殼鐵律)— 能力只實作一次放 API、介面只暴露的紀律,2026-06-07 從壓測釐清。
- **規則晚於實作**(紀律後補)— 原則在介面已各自長成後才補立,是漂移的根因。
- **MCP 漂移**CLI/MCP 不一致)— 兩介面各自接後端、打不同帳號/端點的歷史債。
- **死端點 404**/workflows/deploy 不存在)— MCP 打一個從未存在的部署端點,是漂移的具體化石。
## 關聯
### 內文知識關係
- 規則晚於實作 >> 導致 >> MCP 漂移
- MCP 漂移 >> 具體表現為 >> 死端點 404
- 薄殼原則 >> 補立晚於 >> MCP 漂移
- 薄殼原則 >> 修正 >> MCP 漂移
### 卡片關係
- [[薄殼規則晚於實作-MCP漂移是歷史債]] >> 補充歷史成因於 >> [[薄殼原則-能力長在API]]
- (相關 memory`mcp-self-hosted-bug-fixed``thin-shell-capability-in-api` — MCP 後端接線與薄殼鐵律)