leo 2026-07-20 明令:「已經改用 cf 自己的 secrets,不要再說它了」 「我希望以後再也看不到這個詞再出現」 背景:credential 早已遷移至 CF Workers per-script Secrets + D1 目錄, 舊的自管金鑰(client 端 AES-GCM + KV 密文 + crypto_decrypt)是遷移期遺留。 本次連根移除,含一併作廢的死 SaaS 碼。 移除: - 舊 KV 密文解密路徑(credential-injector.ts 整檔、dual-read fallback) 前置驗證:leo21c / youlin 兩帳號 CREDENTIALS_KV 實測 *:cred:* 皆 0 筆 - migrate-to-workers-secrets 搬家端點(回填已完成,無可回填) - /register 路由與 generateApiKey(HMAC 產 ak_ key 是 SaaS 遺物; self-hosted 走 namespace 明碼 D21,已無人使用) - platform_crypto component(三帳號實測 404 已退役,無 workflow 引用) 保留(附理由): - crypto_decrypt 保留為永遠回失敗的 stub——現役三個 auth .wasm 仍宣告該 import,缺項會讓 WASM instantiate 直接失敗。待零件重編後可真正刪除。 順帶修復(原不在範圍,但會實際壞事): - /auth/callback 有 `if (!key) redirect(server_error)` 閘,未設該 secret 的 實例會登入直接失敗 → 已移除 - OAuth 兩處把 provider token 寫進舊加密 KV(租戶鍵與實際 api_key 在 rotate 後必然分歧,已失效)→ 改導向 Workers Secrets,包 try/catch 不影響登入 - acr init Standard 模式呼叫已刪除的 /register → 改引導 OAuth 取 key - .claude/rules 與 system-dev/docs 是同一規範的兩份鏡像,先前只改 rules 導致鏡像仍在教舊做法 → 已同步(此類雙檔同步應納入檢查) 新用戶安裝從此零 secret 前置。 測試 187/188(唯一 fail 為 pre-existing,stash 驗證與本次無關); cypher-executor 與 cli typecheck 全綠。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
16 KiB
交辦文件:完成 arcrun self-hosted harness(給接手的 CC)
建立:2026-06-01(由前一個 CC 調查後撰寫) 對象:接手的外部 CC 目的:把 arcrun 補到「任何 CC 在自己的 CF 帳號上 self-host 後就能順暢開發、且不可能重蹈 mira 的錯」的程度。
先讀:
DECISIONS.md(穩定決策)、.claude/rules/06-mindset.md(mindset)、BACKLOG.md(流動待辦)。 本文件不取代它們,只是把「今天要做的三件事」連同已查證的實況整理好,讓你不用重跑調查。
0. 戰法已轉變(最重要的背景)
richblack 2026-06-01 決定:從 SaaS 改成 self-hosted 開源策略。
這直接改變 harness 的成功定義:
- 舊定義:在 richblack 的 prod 帳號(
cypher.arcrun.dev)上能跑。 - 新定義:任何 CC 在自己的 CF 帳號上
acr init --self-hosted後就能跑通一個含 recipe 的 workflow,而且寫錯時會被程式擋住。
richblack 會用另一個 CF 帳號實測 self-host。所以「self-hosted 一鍵起得來」從「第一期重要但非阻擋項」升為今日第一優先。
arcrun 現在的核心心智(richblack 2026-06-01 校準,比 DECISIONS §1 更硬)
- 核心零件數量少、由 richblack 維護、不接受 CC 自製(可投稿 PR,人 merge = 閘門)。
- 其他人做的一律是 recipe(= http_request + 一組 YAML 設定,不用 deploy)。
- arcrun 是一套給 CC 的 harness:事前提醒 CC 能用什麼 / 不能做什麼,事後用程式擋住讓它無法犯錯。
- 你不用管 mira。 mira 是錯誤做法的源頭(見 §1),它自己會修。你的目標是讓任何 CC 都能用,且絕不會發生 mira 的錯。
1. mira 故障 = 症狀樣本(已定位,不用你修)
mira(/Users/youlinhsieh/Documents/tech_projects/InkStoneCo/polaris/mira/arcrun/*.yaml)的 workflow 寫 component: kbdb_get / claude_api / telegram 等。這些是 mira 當初自己錯做的「假零件」(DECISIONS §1 判準:打固定 endpoint 的東西是假零件,該是 recipe)。
本次整修(BACKLOG 步驟3)已把這些假零件降級成 recipe + 刪掉零件目錄(registry/components 從 33 → 22)。所以 mira 斷了。
這證明的事:mira 的錯,正是當時 harness 沒擋住的漏洞。零件刪了,但 harness 還缺「事前告訴 CC 別這樣做 + 事後擋住 CC 這樣做」的完整機制 → 下一個 CC 還會犯同樣的錯。這就是你要補的(§3 task 2)。
2. 已查證的實況(你不用重查,2026-06-01 實打 prod)
2.1 降級後的 recipe 鏈路是「活的」✅
實打 https://cypher.arcrun.dev/recipes(richblack prod)確認以下 recipe 都在 KV:
| canonical_id | hash | endpoint | auth_service |
|---|---|---|---|
kbdb_get |
rec_4c7dcf9b | https://kbdb.finally.click{{_path}} |
kbdb |
gmail_send |
rec_cd426129 | gmail.googleapis.com/.../send | google_gmail_sa |
google_sheets_append |
rec_9fd1b662 | sheets.googleapis.com{{_path}} | google_sheets_sa |
→ 「對的用法」(component: kbdb_get 走解析鏈 step 6 查 recipe:kbdb_get)本身是通的。 不需要重建 recipe。
注意:這是 richblack 的 prod KV。self-host 的新帳號 KV 是空的,需要 seed 這些 recipe(見 §3 task 1 的 seed 步驟)。
2.2 component-loader 解析鏈(cypher-executor/src/lib/component-loader.ts)
resolveComponent 依序嘗試 8 層(行號近似,以實際檔案為準):
0. 平台 orchestration 零件(trigger_workflow) line ~88
1. 內建零件(純 JS) line ~96
2. 外部 URL(http(s)://...) line ~100
3. cmp_hash → WEBHOOKS KV idx → 邏輯 Worker line ~105
4. rec_hash → RECIPES KV idx → recipe 執行 line ~115
5. 邏輯零件 canonical_id → Service Binding (SVC_*) line ~122
5.5 auth recipe (auth_recipe:{service}) line ~127
6. KV recipe canonical_id → RECIPES KV → fetch 外部 API line ~130 ← kbdb_get 等降級 recipe 命中這層
7. WASM HTTP runner(白名單 WASM_HTTP_RUNNER_IDS) line ~134
8. 找不到 → 報錯 line ~142
WASM_HTTP_RUNNER_IDS 白名單(line ~36)現只剩:http_request / cron / 4 個 auth_* primitive。
→ claude_api、kbdb_upsert_block(BACKLOG 標 deferred、源碼暫留)不在白名單也沒 recipe → 用到它們的 workflow 會落到 step 8 報錯。這是 mira 自己的問題,不在你範圍。
2.3 acr init --self-hosted 現況:純手動問答,差很遠
cli/src/commands/init.ts 的 initSelfHosted()(line 105-131)只是問 6 個問題後寫進 config:
要求 CC 自己事先部署好 Worker、建好 KV、再手填 Account ID / cypher URL / 兩個 KV namespace ID / WASM bucket / CF token。
BACKLOG 步驟7 要的是「貼 CF token → 自動建 KV、部署 Worker、自動 workers.dev、寫回 config」。這是最大缺口,task 1 的主體。
config 讀取端已支援 self-hosted(cli/src/lib/config.ts:52 已能用 cypher_executor_url),所以你只要把「自動部署」這段補上,config/執行端不用動。
2.4 CI/CD 已是通用掃描式(可重用於 self-host 部署)
.github/workflows/deploy.yml 掃所有含 wrangler.toml 的目錄自動部署(見 .claude/rules/05-deploy-convention.md)。
self-host 自動部署可以參考同一套掃描邏輯(find . -name wrangler.toml),對每個目錄跑 wrangler deploy。
3. 今天要做的三件事(按序,全在 harness 主線)
richblack 指示:「全部要做」(含
acr init --self-hosted)。 三件都做完 = 今天可交付:外部 CC 能 self-host 起來、用對的方式開發、犯錯被擋。
🔴 Task 1:完成 acr init --self-hosted 一鍵自動化(第一優先)
✅ 實作狀態(2026-06-02,已大致完成):定稿形態為 installer 模式(richblack 拍板)—— 用戶只做:申請 CF 帳號 → 裝 wrangler → 裝 acr →
acr init --self-hosted(貼 token),其餘自動。 已實作(typecheck 過):cli/src/lib/api-recipe-seeds.ts(10 recipe 種子)、cf-api.ts的CfAccountClient(建 KV/R2/查 subdomain/驗 token)、deploy.ts(常數 + downloadAndDeploy)、initSelfHosted()改寫、acr update、cypher-executor/scripts/seed-api-recipes.ts。 唯一剩餘前置(13.6):repo 沒有含預編譯 wasm 的 GitHub release(.wasm 不 commit,rule 05)→downloadAndDeploy()目前誠實回 implemented:false 不假裝部署(mindset §7)。建 KV/R2/seed/config 已可跑;release 產製管道補上後部署即自動化。定稿設計見 SDDself-hosted-init.md(含 §6 前置依賴)。 以下原始子步驟保留供對照;KBDB recipe 採 Supabase 模式進 seed(richblack 2026-06-02)。
目標:CC 只需提供「CF Account ID + CF API Token」,CLI 自動完成其餘一切。
SDD:定稿 docs/3-specs/arcrun/sdk-and-website/self-hosted-init.md(installer 模式,已與 richblack 對齊)。
子步驟:
- 改
cli/src/commands/init.ts的initSelfHosted():- 收 CF Account ID + CF API Token(要 KV Edit + Workers Scripts Edit + R2 權限)。
- 用 CF API(或 shell out
wrangler)自動建 7 個 KV namespace:WEBHOOKS / CREDENTIALS_KV / RECIPES / USERS_KV / SESSIONS_KV / ANALYTICS_KV / EXEC_CONTEXT(清單見.claude/rules/01-tech-stack.md資料儲存表)+ R2 WASM_BUCKET。 - 自動部署所有 Worker:cypher-executor + registry + 22 個
.component-builds/*。可重用 §2.4 的wrangler.toml掃描。每個 worker 的wrangler.toml已含workers_dev = true(BACKLOG 步驟 P1#2 已做),部署後 workers.dev URL 自動啟用。 - 把 cypher-executor 的
[vars] WORKER_SUBDOMAIN改成 CC 自己的帳號 subdomain(self-host 關鍵,見 P0 #9:cypher-executor 走arcrun-{name}.{subdomain}.workers.dev對內 URL)。 - seed 降級 recipe + auth recipe 進 RECIPES KV:新帳號 KV 是空的。把 §2.1 那些 recipe(kbdb_get/gmail_send/...)+ auth recipe seed 寫進去。auth recipe seed 已有
cypher-executor/scripts/seed-auth-recipes.ts,API recipe 需確認有對應 seed 機制(routes/recipes.ts 是動態 push,可能要寫一份 seed 腳本或用acr recipe push)。 - 寫回 config(現有欄位已足夠)。
- runtime secret 不進 CLI 自動化:
CF_SECRETS_API_TOKEN等由 CC 自己wrangler secret put(rule 05 禁止 secret 進自動化流程)。CLI 應在最後印出提示告訴 CC 要手動 put 哪些 secret 到哪些 worker。
驗收(客觀證據,不是口頭宣布 — mindset §7):
- richblack 用全新 CF 帳號跑
acr init --self-hosted→ 全程無手動建 KV / 部署。 - 跑完後
acr push一個含component: kbdb_get(或 http_request + 自建 recipe)的 workflow → trigger → HTTP 2xx + execution trace 證明跑通。
🔴 Task 2(已重定義 2026-06-01):封鎖自製零件 + recipe 入庫把關
✅ 實作狀態(2026-06-02,第一期部分完成):(1) 封鎖自製零件 = 靠 GitHub PR 人 merge,無需新做 (矛盾已釐清)。(2a) 資料外流提醒 = 既有實作已涵蓋(recipe.ts
obtainExposureConsent+ exposure-warning.ts, 非 TTY 拒絕)。(2b) 打通檢查 = 新增probeRecipeEndpoint(recipe.ts,typecheck 過):push 後實打 endpoint,提醒級不硬擋,含 {{模板}} 誠實說明待 run 才知,401/403 標「多半缺 credential 非 bug」。 公共庫 relay 檢核(--public)= 第一期後。SDDrecipe-push-gatekeeping.md+ tasks.md W2。
⚠️ 方向修正(richblack 2026-06-01):原 Task 2「acr validate 擋假零件名」作廢。 理由:自製/修改零件的路已封鎖(CC 造不出零件)→「擋假零件」這件事不存在;workflow 引用 recipe(
component: kbdb_get)是合法且未來唯一的擴充方式,不該被當假零件擋。 把關點從 workflow validate 移到 recipe 入庫(push)那一刻。 已動的 yaml-parser.ts(LEGAL_PRIMITIVES / findSuspectComponents)已回退。
新目標:
- 封鎖自製零件:靠「零件投稿走 GitHub PR + 人 merge」天然閘門(DECISIONS §8)。零件數量少、 絕大多數是 recipe → 不為零件 PR 蓋自動化把關(量少,人工檢查即可;爆量才回頭想自動化)。
- recipe 入庫把關(CC 唯一能擴充的是 recipe,一律用 push,自有庫/公共庫同一套指令):
- 自有庫(self-hosted)= 提醒級:(a) 資料外流提醒——會讓資料/服務對外可見的動作需人類明示同意; (b) 打通檢查——push 時實打 endpoint 回報 2xx 與否(誠實標原因,不假綠,不硬擋)。
- 公共庫 = 維護者 relay 檢核(實際打通、真收到成功回傳)— 第一期後。
SDD:已寫 design 給 richblack review →
docs/3-specs/component-gatekeeping/recipe-push-gatekeeping.md(+ tasks.md W2 節)。review 通過才動 code。
動到的檔案(待 review):cli/src/commands/recipe.ts(push 加提醒 + 打通檢查)、確認 data-exfil hook 涵蓋 recipe push 路徑。
驗收:
acr recipe push會產對外 webhook 的東西 → 印資料外流警示 + 要人類同意;非 TTY → 拒絕。acr recipe pushendpoint 可達的 recipe → 回報「✓ HTTP 2xx」。acr recipe push缺 credential → 回報「⚠️ 未打通:缺 credential」(誠實),仍允許 push。- workflow 引用 recipe(
component: kbdb_get)不被任何 validate 步驟當假零件擋。
🔴 Task 3:README 重寫成單一路徑 — harness「事前提醒」
目標:self-hosted 開源後,README 是外界 CC 唯一入口。砍掉「玩法一/二/三」三選一,講清楚單一正確路徑。
子步驟(改根 README.md):
- 砍三選一玩法,留一條路:
acr init --self-hosted→ 寫 workflow(primitive 串 + recipe)→acr push→ trigger。 - 明示心智(呼應 mindset §1):「零件就這固定幾個由我們維護、不接受自製;要打外部 API 就寫 recipe;要編排就寫工作流。」
- 連到
.claude/rules/06-mindset.md/ arcrun-mindset Skill,讓 CC 一開始就有正確世界觀。
驗收:README 讀完,一個沒看過 arcrun 的 CC 知道:能用什麼、不能自製零件、打外部 API 要寫 recipe、怎麼 self-host 起來。
4. 今天「不要做」的(避免你走偏)
| 項目 | 為何不做 |
|---|---|
| 修 mira | richblack 明示不用管,mira 自己修 |
步驟2 acr recipe test / relay / credits |
DECISIONS §3c 明確劃為服務側、非第一期 |
| 步驟6 搬家拆 matrix | 純 repo 整理,不影響 CC 能否用 |
砍 injectCredentials 舊路 / BUILTIN_CREDENTIALS_MAP |
獨立清理,不擋交付(DECISIONS §3b / BACKLOG「第一期之後」) |
| 新 primitive / Gherkin 真跑 / 入站認證 | richblack 已標「不要現在做」 |
5. 鐵律提醒(違反會被 hook block)
- 任何 code 變動前先讀對應 SDD + 在回覆開頭宣告(
.claude/rules/00-sdd-protocol.md)。 registry/components/下禁止 TS;cypher-executor TS 禁止 credential/auth/JWT 業務邏輯(.claude/rules/02-forbidden.md,hook 強制)。- 每完成一個 task 立刻更新對應 tasks.md / BACKLOG.md 的
[x],不批次。 - 誠實限制(mindset §7):stub / 未完成就標 unimplemented,不假綠;完成 = 客觀證據(exit code / HTTP status + trace),不是口頭宣布。
6. 交棒:CLAUDE.block.md / README 表達優化(InkStoneCo 總管 2026-06-15)
背景:總管與 leo 釐清 Mira 自製零件衝突時,實測一個現象——連 Opus 讀著 cli/harness/CLAUDE.block.md + rules/,都把 recipe 誤解了四輪(以為 recipe 是私有腳本、以為缺能力可用工作流拼裝補)。leo 的標準是「Haiku 都能輕易理解」。block 是被 acr install-harness 裝進各子 repo(含 mira)的唯一入口,細節雖在 rules/ 但子 repo CC 預設只看 block → block 不夠清楚 = 下一個 CC 還會誤解。這是表達缺口,不是讀者問題。
請 arcrun CC 做(改 cli/harness/CLAUDE.block.md,順帶對齊 §3 README):把現有「不要自製零件 / 打外部 API 寫 recipe」兩句,補成「該做什麼 + 做完去哪 + 已違規的怎麼退場」。三個已核實的盲點要講清楚(論證在下,成品文案你按自己 rules 寫):
-
recipe 是公共資產,不是私人腳本。
acr recipe push= 投稿公共庫(DEFAULT_PUBLIC_LIBRARY_URL)。公私是屬性(UUID 身份模型,cypher-executor/src/routes/recipes.ts§7.5.5:身份=uuid、公私/author 是屬性、多作者版本並存),不是兩條路。「發現缺某 stock recipe → 自己做一個 push 上去」是被鼓勵的,不必問人。 現 block 只寫「寫 recipe」「不用部署」→ 讀者誤以為私有自用、不上傳(正好反了)。 -
缺能力 → 補 API,不准用 recipe/工作流拼裝。這條 rules 其實寫得很對(07-thin-shell §1 upsert 正反例、02-forbidden §5.2、01 §8),但 block 完全沒提 → 子 repo CC 看不到。請把「缺單一 API 能力 = 去補 API endpoint,不拼裝」這條判準帶進 block(口訣:「換個介面要不要重寫?要→是能力該在 API」)。
-
零件的退場路徑空白。現 block 只說「不要自製零件」,沒說「已經有自製零件的(如 mira 的 6 個)該怎麼辦」。請補:已自製零件 → 映射到 stock(
claude_api→自己做 AI 判斷別放工作流,mindset §2;kbdb_*→走已備的acr kbdb薄殼/MCP)或改寫成 recipe;無 stock 對應的單 API 缺口 → 寫 recipe 投稿。
驗收(leo 標準):改完後,一個 Haiku 級 CC 讀 block 就能答對:recipe 要不要投稿(要)、缺 upsert 怎麼辦(補 API 不拼裝)、已有自製零件怎麼退場(映射 stock / 改 recipe)。
連帶(非 arcrun 範圍,總管已記):「真正可複用的多步驟業務流程能否投稿共享」是 wishlist C6(InkStoneCo 頂層),與「拼裝補 API 缺口」是兩回事(C6 紅線已劃清),arcrun 不必現在處理。
注:本交棒由總管寫判準與論證,成品文案 + hook(若要)由 arcrun CC 按自己 rules 產出。總管不直接改 arcrun 實作檔(職責邊界 D9)。