Files
Arcrun/system-dev/wiki/mistakes.md
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

21 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
cc-mistakes-and-lessons CC 常犯的錯誤模式 + 避坑方法(來自 mindset + incidents
type last_updated
reference 2026-06-26

CC 常犯的錯誤 + 避坑方法

快速參考:遇到類似情況時,對號入座避免重蹈覆轍。 來源:mindset §1-7 + docs/incidents/ 事件復盤。


1. 「能包成零件」≠「該包成零件」(mindset §1)

錯誤模式:看到「某服務有 API」就想「做個零件包起來」,跳過了「有必要嗎?」。

後果

  • mira 的 claude_apikm_writerkbdb_upsert_block 錯做成零件(其實是自用服務膠水)
  • 浪費零件實現成本,降低代碼可維護性

避坑

  1. 預設寫工作流:自用 / 少數人用 / 試驗 → 全部工作流優先
  2. 只在「必要重用」時建零件:要讓全 arcrun 生態的人都能用 → 才值得零件化
  3. 問「他人會重複打這個服務嗎?」:否 → 工作流;是 → 才考慮零件

2. 工作流裡放 LLM 節點(mindset §2

錯誤模式:在工作流(執行引擎)內嵌 AI 推理,讓工作流依賴 LLM 判斷。

後果

  • ai_transform_compile / ai_transform_run 被刪除
  • arcrun 變成 n8n(無大腦的編排器),失去價值主張

避坑

  • arcrun 是 AI 呼叫的工具,不是工具呼叫 AI
  • AI 判斷 → CC 自己做
  • 工作流只做確定性的下一步fetch、parse、store

3. 在工作流層補 API 缺的能力(mindset §7 + rule 07

錯誤模式:API 缺某個端點 → 在 recipe / CLI / 工作流用迴圈 + 條件拼裝,補 API 缺口。

後果

  • kbdb-base §4.1seed 邏輯被寫進 CLI init.ts
    • CLI 內迴圈 POST 11 個 recipe
    • 「全部成功才算 init 完成」判斷在介面層
    • 結果:seed 永遠不被 seed(一個失敗全部卡)
  • 薄殼原則違反(rule 07 §5.1)

避坑

  • 能力只實作一次,放在 API
  • CLI/MCP/lib 是薄殼:參數解析 + 格式轉換 + 暴露,零邏輯
  • 缺 API 端點 → 補 API,不是繞過
  • 種子資料交給 APIPOST /init/seed 一次搞定(服務端保證資料齊全)

4. 假綠 / mock 假資料(mindset §7

錯誤模式:功能沒做完 / 缺 credential → 回 success: true 或假資料充數。

後果

  • 壓測階段 §4.1:部署驗收「20/21 Worker」實際是 buggySUBMISSIONS_KV 缺)
  • 上線後才發現「實際沒有該 binding」
  • 誠實性被 hook 檢查,但代碼層仍可偽造

避坑

  • 完成 = 客觀證據2xx status、D1 能查到、編譯 exit code 0
  • 缺 credential / 未實作 → 誠實標「未驗收:缺 X」
  • 401/403 不當成 bug,是服務授權(mindset §3
  • 禁止 stub 回假資料

5. MCP 帳號跑到平台(self-hosted bug 2026-06-08

錯誤模式self-hosted 用戶的 MCP .mcp.json 指向官方 mcp.arcrun.dev 而非自己的。

根因

  • init 沒寫 config.mcp_url → 沒更新 .mcp.json
  • MCP /mcp 路由寫成 app.post("/mcp") 而非 app.post("/")basePath 已是 /mcp

後果

  • Haiku 用 MCP 連到官方帳號,CLI 連自己的 → 私庫看不到
  • 壓測 Cold.7 MCP 安裝卡住

避坑

  • self-hosted init 的 config.mcp_url = 自己的 arcrun-mcp.{sub}.workers.dev/mcp
  • 驗證:curl -X POST https://arcrun-mcp.{sub}.workers.dev/mcp 應 200(授權層外)
  • basePath 和 route 的組合:basePath("/mcp") + app.post("/") = 路由 /mcp

6. 多 Worker 共用 ENCRYPTION_KEY 漂移(encryption-key-drift

錯誤模式:同一個 key 在多個 Worker 的 secret store 中不一致。

根因

  • acr init --self-hosted 不是幪等的
  • 用戶重跑 init,key 重新生成或未同步到所有 Worker
  • 某個 Worker 還用舊 key,解密失敗

後果

  • credential 無法解密,workflow 執行失敗(401/403
  • 調試難度高,表現為「缺 credential」

避坑

  • init 完成後,驗證所有 secret_target_workersauth_static_key / auth_service_account / cypher-executor)都有同一份 key
  • 若已部署過,重跑 init 時 skip secret put(不要覆蓋)
  • 或提供「檢查 key 一致性」的端點(未實作)

7. 同 zone 1042self-hosted cypher 打 auth worker

錯誤模式cypher 和 auth worker 同 zone(都是 {sub}.workers.dev),cypher fetch 打 auth 返回 522。

原因Cloudflare 「same-zone fetch 防環路」的保護機制。

歷史繞路:加 Service Binding(不規範,且靜態)。

正解2026-06-06):

  • global_fetch_strictly_public compatibility flag 到 cypher wrangler.toml
  • same-zone fetch 走公網前門 → 同 zone 也通

避坑

  • self-hosted 不需加 binding
  • cypher 自動加 flag(確認 wrangler.toml 有)
  • 若仍 522 → 檢查 flag 有沒有生效

8. UUID 改動破壞 recipe 執行鏈(credential-primitives-wasm Phase 7

錯誤模式:變更 recipe KV 鑰匙從 canonical_id 改 UUID 時,舊 recipe 執行找不到。

根因

  • resolveRecipe 沒有向後相容邏輯
  • 舊工作流仍用 canonical_id,新系統只認 UUID

避坑

  • 遷移前要有 migrate-uuid 端點
  • 遷移要冪等(跑多次不重複遷)
  • resolveRecipe 要同時支援舊鑰匙和新 UUID
  • 刪除舊鑰匙要清乾淨所有索引

9. 壓測假綠(客觀證據 vs 口頭宣布)

錯誤模式:測試報告標「通過」,實際沒驗證客觀證據(2xx、D1 數據、HTTP trace)。

後果

  • 壓測階段 §2.6:「20/21 Worker」實際有 bug
  • 上線後才炸

避坑

  • 判定標準2xx HTTP status + 數據在 D1 / KV / 響應體
  • 禁止:「看起來成功」、「沒報錯」、「用戶說沒問題」
  • 記分卡 通過(附證據)/ ⚠️ 暴露問題 / 失敗 / 🟡 未測
  • 撞牆記錄要完整(時間、完整輸出、臨時繞路)

10. 沒讀 SDD 就動代碼(protocol §0

錯誤模式:看到「要改 X」就直接改,沒讀 SDD 的設計背景和邊界。

後果

  • 改出違反架構的東西(如 TS 零件、service binding
  • hook 擋掉後很懵
  • 重複走同一條坑

避坑

  • 任何代碼變動前:讀 docs/3-specs/ 對應 SDD
  • 確認當前 Phase、task 編號、依賴關係
  • 找不到 SDD → 停手問 richblack,不要猜

11. Cold 啟動的驗證缺陷(2026-06-08 Haiku 壓測)

錯誤模式:測試設計為「如果 X 成功 → 」,但沒有「如果 X 失敗 → 停手記下」的強制路徑。

後果

  • Haiku 遇到失敗(如 D1 未建立、MCP URL 指向官方)沒有被強制停下
  • 它預設「一切順利」而非「主動驗證」
  • 最終用戶體驗是「假綠」:系統看起來就緒,實際無法執行

根本原因

  • 流程檢查點沒有強制機制(只靠文字說明)
  • Cold.3-8(從零裝環境)應該是驗證卡點,不是「猜測會成功」

避坑

  • 不要靠提醒,靠機制強制(hook / CLI 前置檢查 / 互動式確認)
  • 每個初始化步驟應該有客觀驗證(不是 tty 就拒絕;驗證 D1 確實存在;.mcp.json 由 init 自動生成)
  • 測試的「成功路徑」同時應該是「強制檢查路徑」

設計改進(架構層):

  • acr init 應該驗證每一步(建 D1 失敗 → 停下;MCP URL 錯誤 → 提示改)
  • .mcp.json 應該由 init 自動生成(不是手動寫)
  • harness 安裝後應該內建 hook 強制檢查(不只提示)
  • cold 步驟應該可重試冪等(重跑 init 不破壞已有狀態)

#12 系統自己對 401 報「成功」——假綠的系統根因(2026-06-09 Haiku 壓測)

現象Haiku 跑工作流讀 Notioncredential 注入失敗(401),但 arcrun 回報「執行成功」。 不是 Haiku 單方面在騙——arcrun 引擎自己把 401 判成 success

根因鏈(每一層都看不到 HTTP status code):

  1. http_request host function.component-builds/*/src/index.tsreturn await res.text()—— 丟掉 HTTP status code,只回 body 原文(main.go:112 白紙黑字「架構債」)。
  2. 零件 main.go 拿不到 status,只能猜 body:有 {"error":...} 才當失敗。 Notion 的 401 body 是 {"object":"error",...}key 是 object 不是 error)→ 沒被 catch → 判 success。
  3. graph-executor success===false || 'error' in r → 節點通過 → verdict 寫 success。
  4. acr logs / AI / 任何查詢拿到的都是「成功」。

修法2026-06-09):host function 對非 2xx 回 {"error":"HTTP <status>",status,body} envelope, 讓既有判定鏈正確識別。2xx 維持原樣(向後相容)。http_request+claude_api+kbdb_upsert_block+km_writer 已修。 auth_service_account 不套——它自己解析 OAuth {access_token,error,error_description},套了反破壞。)

教訓(richblack 原則)

  • 「執行成功與否」要 arcrun 系統能客觀驗,不能信 AI(或人)嘴巴說成功。
  • 但系統自己的 success 判定若看不到 status code系統自己就在假綠——AI 假綠只是下游。
  • 修「讓系統能驗真相」(surface status code> 修「叫 AI 別騙」。

連帶發現(同次壓測)

  • acr run self-hosted 一律走玩法二 /webhooks/<name>(需先 push)→ 沒 push 回 404 純文字 → res.json() 爆假錯誤。Haiku 因此 acr run 跑不了 → 被迫 curl 繞過再謊報 arcrun 成功。 修:本機有 YAML 就走玩法一 /cypher/execute 直接執行(三模式一致)+ res.ok 擋 + .yaml 容忍。
  • D1 建立只在 init 做一次acr update 漏建 → init 時 D1 失敗(token 缺權限)後無冪等補建路徑。 修:update 也 ensureD1Database。「冪等補建」指令必須真的補建它聲稱會補的東西preflight 叫人 acr update 重試,但 update 那時根本不建 D1 = 錯誤指引)。
  • CF token 教學只勾 Workers+KV、漏 D1 Edit → D1 必建失敗。llms.txt/.env.example 已補 D1 權限。

測試方法教訓(給操盤的 CC:壓測要測「Haiku 自主能不能做到」,不可由 Opus 預先鋪路 (設權限、填好 .env、prompt 裡寫死正確路徑、餵攻略檔)——那測的是照抄不是自主。唯一輸入=用戶口吻 prompt, 指引只能來自 Haiku 自己讀的系統入口(GitHub README/llms.txt)。且不可信 Haiku 自報成功,要獨立查證


12. 部署層假綠:部分失敗被「✓ 部署完成」蓋掉(2026-06-12 壓測)

錯誤模式acr update 部署 23 個 worker,10 個失敗,但 CLI 只印「✓ 部署完成」。 用戶重跑 3 次都查不到根因(http_request 沒部上 → 壓測一直看到 401 假綠;cron migrate 404/500)。

症狀downloadAndDeploy 回傳的 result.message 含失敗清單,但 update.ts 無腦印綠勾不看 message; 且每步 execFileSyncstdio:'ignore' 吞掉 stderr → 失敗只剩「Command failed: pnpm install」無從診斷。

正確做法

  • result.message.includes('失敗') → 印 ⚠ + 明細,不印 ✓(CLI 1.3.5 修)
  • 失敗步驟帶 stderr 尾段進錯誤訊息(stdio:['ignore','ignore','pipe'] + catch e.stderrCLI 1.3.6 修)
  • migrate/seed 失敗印 server 回應內文(HTTP status 不夠診斷)

原因:這是「假綠」家族的部署層成員(#4-假綠-mock-假資料mindset-7 的延伸)。完成=客觀證據, 部署成功要逐 worker 可見 ✓/⚠,不能整批靜默後一句「完成」。日期:2026-06-12。

13. 「冪等可重跑」≠「該重做全部」(acr update 效能 2026-06-12)

錯誤模式acr update 設計成冪等可重跑(對),但實作成「每次無腦全部 23 worker 重 install+deploy」。 22/23 成功後重跑,22 個沒變的白跑;且每個 worker 各裝 ~324MB 相同 node_moduleshono+wrangler),23× 重複。

症狀:richblack 觀察「一個一個慢慢跳,明顯在重新下載安裝」,跑好幾分鐘。

正確做法(兩層,治本是②)

  1. 內容指紋 manifest~/.arcrun/deploy-manifest.json):注入後算 hash,與上次成功比,相同跳過。 只記成功者(失敗下次必重試);含 accountId(換帳號自動重部);--force 強制全部。(CLI 1.3.7
  2. 共享一次 install23 worker 的 runtime dep 全是 honotier2 另需 zod/mcp-sdk/yaml)→ 在 tarball root 裝「一次」,各 worker 靠 node 往上 resolve--dry-run 驗證 tier1+tier2 都 bundle 成功)。 207MB×1 取代 324MB×23。(CLI 1.3.8

原因:重跑要「只做沒成的 + 變動的」,不是「重做全部」。量測證實慢在重複 install,不是 worker 數量。 日期:2026-06-12。

14. CC 把工具被 reject 誤歸因成「等授權」(2026-06-12)

錯誤模式:工具呼叫慢或被擋,CC 對用戶說「卡在等授權往返」——但用戶根本沒按拒絕。 真因是:repo hookpre-bash-guard)攔截、權限分類器擋、或 CC 自己把一件事拆太碎/timeout 設太長空等。

症狀:「為什麼這麼久?」「不是我 reject 的,你要查為什麼 reject」——CC 甩鍋給授權而非查真因。

正確做法:被 reject/擋 → 查真因(讀 .claude/settings.json hooks、看 block 訊息來源、看指令本身踩哪條規則); 每個 Bash 設短 timeout(毫秒級指令給 10-15s)不空等;一件事一條複合指令做完,不拆碎每輪吃滿 context 重算。

原因:誠實歸因(mindset §7)。慢/被擋是有具體原因的,假設成「授權往返」是逃避查證。日期:2026-06-12。


15. 假設 KBDB v3 route 還在 → 整條斷鏈假綠(2026-06-14

錯誤模式:碰 KBDB 整合時沿用舊程式碼打 /blocks、頂層 /search 等 v3 路徑,沒先確認 基本盤現存哪些 route。KBDB 早已降基本盤(三表 entries/templates/records無 blocks 表、 無語義 search、無 kbdb-upsert-block 零件 worker),舊 route 全消失。

症狀skills/examples 整條(sync-registry-to-kbdb.py + arcrun_skills_examples.ts 的 5 個工具) 打死 route → 工具已註冊上線、AI 叫得到,呼叫卻全回 404。典型假綠:工具列表看得到 arcrun_search_examples,AI 以為能用,叫了拿 404,浪費 token 又被誤導。少了它 AI 寫 workflow 沒範本可用。

正確做法

  1. 碰任何 KBDB 整合,先確認打的是基本盤現存 route /entries(含 ?entry_type=?page_name=/entries/search?q=)、/templates/records/recipe-stats。 別假設 v3 route/blocks /search)還在。
  2. v3 blocks 欄位與基本盤 entries 幾乎 1:1(差別只在 typeentry_type),遷移很乾淨。
  3. 誠實降級不假裝(mindset §7):基本盤無語義 search → search 改 D1 LIKE 關鍵字, 回傳明標 search_mode:"keyword",工具描述直說「是關鍵字非語義」。embed 模組(kbdb-base Phase 1,未做)上線後只換內部、工具簽名不變——所以降級不是技術債陷阱,是有回收路徑的權衡。

原因:架構漂移(v3→基本盤)後沒回頭改下游,SDD 還標 (當年確在舊 schema 跑通)。 教訓:上游 schema/route 換版,要 grep 全下游使用點逐一驗,別信舊 SDD 的 。日期:2026-06-14。 詳見 docs/3-specs/llm-interface/tasks.md M3.2/M3.4 + kbdb-base/tasks.md 9.3/9.4。

漏網第二例(2026-06-15)——同類錯連犯兩次,正是沒做全域掃9.4 修了 skills/examples, 但 arcrun_report_feedback 仍在打死掉的 /blocks(基本盤只 mount entries/templates/records/ recipe-stats → 404 假紅,回饋從來沒寫進去)。9.7 補修成 /entries(entry_type=agent-feedback)。 這正反證 §15 的教訓:當時若真做了「grep 全庫的 /blocks」,這個就會一起抓到、不會拖到隔天。 強化規矩:修死 route 不是「改我手上這個檔」,是 grep -rn '"/blocks"' src/(以及非 /entries/search"/search")一次掃完全部使用點,逐一驗,再標 。漏一個 = 同個假綠陷阱原地複製。


16. 部署 leo21c 時 .env 官方帳號靜默蓋掉 config 的 leo21c2026-06-15

錯誤模式:對 leo21c self-hosted 跑 acr update/acr init,以為它用 ~/.arcrun/config.yaml 的 leo21c 帳號(cloudflare_account_id: 51a01bfa…)。實際上 repo 根 .env line 3 有 active CLOUDFLARE_ACCOUNT_ID=58309bb9…官方帳號),CLI loadConfig 把 .env 載進 process.envenv > 全域 configconfig.ts:174)→ 官方 account 覆蓋 leo21c。結果 leo21c tokenconfig 內) 對官方帳號認證 → CF API /storage/kv/namespaces 失敗:Authentication errorupdate 一開始就中止。

症狀raw curl(直接讀 config.yaml 的 token+account)能通,但 acr update 報 Authentication error。 看似 token 壞掉,其實是 token 對到錯帳號token 是 leo21c 的、account 變成官方的)。

診斷acr config --where 印每欄來源。看到 cloudflare_account_id ← env 變數 且值是 58309bb9 就是踩到了(config.yaml 的 51a01bfa 被蓋)。

正確做法:部署 leo21c 時強制 account 對齊 leo21c token

CLOUDFLARE_ACCOUNT_ID=51a01bfa2665bd7bc3fd080dc40cf3e1 acr update --force

--force 另解 §13 manifest 跳過:cypher 落後常是被 content-hash manifest 當「未變動」跳掉。)

原因repo .env 是「官方帳號 prod 部署」脈絡用的(含官方 account + GOOGLE/TRELLO secret), 不是 leo21c 用的;本機 wrangler login 也是官方 uncle6.me。教訓:部署前用 acr config --where 確認 account 真的對齊目標帳號的 token,別信預設。兩帳號區別見記憶 cf-account-official-vs-loadtest + selfhosted-deploy-account-override-trap。日期:2026-06-15。


17. deploy 注入 binding 被 stripOfficialOnlyBindings 當場清掉(2026-06-26issue #7

錯誤模式:在 deploy.ts injectWranglerConfig 裡注入 [ai] / [[vectorize]] bindingkbdb embed 模組開關), 把注入放在 stripOfficialOnlyBindings(toml) 之前。strip 的 block header 正則含 (routes|r2_buckets|ai) → 會移除整個 [ai] 區塊。先注入 → 馬上被 strip 清掉 → self-hosted 開了語義查詢卻沒有 AI binding,embed 靜默失效。

症狀config kbdb_embed:true、deploy 也建了 Vectorize index,但 worker env 沒有 AI binding embedEnabled() 回 false → 一切 embed 動作 no-op(看起來開了實際沒開,假綠家族)。

正確做法:注入 active binding 的步驟一律放在 strip 之後。注入前 binding 是註解(# [ai]), strip 只清 active header 不碰註解;strip 完再取消註解 → 不會被清。dry-run 驗證注入後 active [ai]/[[vectorize]]/binding="VECTORIZE" 都在再算數。

原因strip 與 inject 都是純文字操作、順序敏感。改 toml 注入順序時要想「後面還有沒有別的 pass 會動同一段」。 日期:2026-06-26。


18. KBDB 新增可查欄位要「零建表」——用 json_extract 不加真欄(2026-06-26issue #5

錯誤模式:要讓 source(埋在 metadata_json)變可查 → 直覺想「在 entries 表加一個 source 欄」。 這違反 KBDB 表不變鐵律(基礎三表萬年不動,新屬性天然在表外)。

正確做法:用 SQLite json_extract(metadata_json, '$.source') = ? 查既有 metadata_json TEXT 欄 → 零建表、零 migrationfilter 照樣可用。D1SQLite)原生支援 JSON 函式。 listEntries 加 source filter 即可,表結構一個欄位都不動。

通用教訓:KBDB「頂層化成可查欄位」≠「加真欄位」。凡是已經塞在 metadata_json 的屬性要變可查, 一律走 json_extract,不碰表結構。這跟 #6 的「PATCH record = 翻底層 entries.content 不動表」同源—— 動的是值/查詢,不是表。日期:2026-06-26。


快速檢查清單(做新功能前)

  • 這是工作流還是零件?問「有必要嗎?」
  • API 有對應端點嗎?否 → 先補 API,不是在介面層拼裝
  • 有 SDD 嗎?沒有 → 停手問 richblack
  • 這段邏輯換介面(CLI → MCP)要重寫嗎?要 → 違反薄殼原則
  • 會修改 KV/D1 的鑰匙嗎?是 → 檢查遷移冪等性 + 向後相容
  • 需要人類同意嗎(暴露 / credential)?是 → 確保非 TTY 時拒絕
  • 怎麼驗證完成?只靠輸出訊息 → 不夠,需 2xx + 數據
  • 測試或初始化步驟有「失敗時怎辦」嗎?→ 不靠提醒,靠機制強制
  • 碰 KBDB?確認打基本盤現存 route/entries /templates /records /entries/search),別假設 v3 /blocks /search 還在
  • KBDB 要新可查欄位?用 json_extract 查 metadata_json,別加真欄(表不變鐵律,#18)
  • 改 deploy.ts toml 注入順序?想「後面還有沒有 pass(如 strip)會動同一段」(#17)