Files
Arcrun/docs/HANDOFF-self-host-harness.md
Leo fb2d0b0c2d feat(self-hosted): acr init --self-hosted installer + recipe push 把關 + commit 部署 wasm
讓任何 CC 用自己的 CF 帳號一鍵 self-host arcrun(戰法轉 self-hosted 開源)。

Task 1 — acr init --self-hosted installer(用戶只給 CF Account ID + token,其餘自動):
- cli/src/lib/cf-api.ts: CfAccountClient(驗 token / 建 KV 冪等 / 建 R2 / 查 workers.dev subdomain)
- cli/src/lib/deploy.ts: 從 GitHub codeload tarball 拉部署物 → 注入用戶 KV id → wrangler deploy
  (tier1 component-builds 先、tier2 cypher-executor/registry 後;部分失敗誠實回報不假綠)
- cli/src/lib/api-recipe-seeds.ts: 10 個現役 API recipe 種子(KBDB 採 Supabase 模式)
- cli/src/commands/init.ts: initSelfHosted() 改寫成 installer 流程
- cli/src/commands/update.ts: acr update(拉新 ref 重部署)
- cypher-executor/scripts/seed-api-recipes.ts: prod 補灌腳本

Task 2 — recipe 入庫把關(封鎖自製零件後,CC 唯一能擴充的是 recipe):
- cli/src/commands/recipe.ts: 新增 probeRecipeEndpoint 打通檢查(提醒級不硬擋,
  含模板誠實說明待 run 才知,401/403 標多半缺 credential 非 bug)
- 資料外流提醒沿用既有 obtainExposureConsent(非 TTY 拒絕)

部署物產製:commit 預編譯 wasm 進 repo(推翻 rule 05「wasm 不 commit」):
- .gitignore: 放行 .component-builds/**/component.wasm(registry 中間產物仍排除)
- 只 commit 19 個正當零件 wasm;claude_api / km_writer / kbdb_upsert_block 排除
  (非薄殼、是把工作流硬塞進零件,違反 DECISIONS §1,待降級)
- rule 05 同步記錄此慣例變更 + 膨脹 trade-off

SDD: sdk-and-website/self-hosted-init.md(installer 定案)、
     component-gatekeeping/recipe-push-gatekeeping.md(recipe 把關)
README 重寫成單一 self-hosted 路徑。CLI typecheck exit 0。

未完(待 richblack):push 此 commit 到 GitHub 後 codeload 才拿得到 wasm;
用第二 CF 帳號端對端驗收 acr init --self-hosted。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:44:41 +08:00

193 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 交辦文件:完成 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. 外部 URLhttp(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 不 commitrule 05)→
> `downloadAndDeploy()` 目前**誠實回 implemented:false 不假裝部署**mindset §7)。建 KV/R2/seed/config
> 已可跑;release 產製管道補上後部署即自動化。定稿設計見 SDD `self-hosted-init.md`(含 §6 前置依賴)。
> **以下原始子步驟保留供對照**KBDB recipe 採 Supabase 模式進 seedrichblack 2026-06-02)。
**目標**CC 只需提供「CF Account ID + CF API Token」,CLI 自動完成其餘一切。
**SDD**:定稿 `.agents/specs/arcrun/sdk-and-website/self-hosted-init.md`installer 模式,已與 richblack 對齊)。
**子步驟**
1.`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 #9cypher-executor 走 `arcrun-{name}.{subdomain}.workers.dev` 對內 URL)。
- **seed 降級 recipe + auth recipe 進 RECIPES KV**:新帳號 KV 是空的。把 §2.1 那些 recipekbdb_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(現有欄位已足夠)。
2. **runtime secret 不進 CLI 自動化**`ENCRYPTION_KEY` 等由 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.tstypecheck 過):push 後實打
> endpoint,提醒級不硬擋,含 {{模板}} 誠實說明待 run 才知,401/403 標「多半缺 credential 非 bug」。
> 公共庫 relay 檢核(--public= 第一期後。SDD `recipe-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.tsLEGAL_PRIMITIVES / findSuspectComponents**已回退**。
**新目標**
1. **封鎖自製零件**:靠「零件投稿走 GitHub PR + 人 merge」天然閘門(DECISIONS §8)。零件數量少、
絕大多數是 recipe → 不為零件 PR 蓋自動化把關(量少,人工檢查即可;爆量才回頭想自動化)。
2. **recipe 入庫把關**(CC 唯一能擴充的是 recipe,一律用 push,自有庫/公共庫同一套指令):
- **自有庫(self-hosted= 提醒級**:(a) 資料外流提醒——會讓資料/服務對外可見的動作需人類明示同意;
(b) 打通檢查——push 時實打 endpoint 回報 2xx 與否(誠實標原因,不假綠,不硬擋)。
- **公共庫 = 維護者 relay 檢核**(實際打通、真收到成功回傳)— 第一期後。
**SDD**:已寫 design 給 richblack review →
`.agents/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 push` endpoint 可達的 recipe → 回報「✓ HTTP 2xx」。
- `acr recipe push` 缺 credential → 回報「⚠️ 未打通:缺 credential」(誠實),仍允許 push。
- workflow 引用 recipe`component: kbdb_get`**不被任何 validate 步驟當假零件擋**。
### 🔴 Task 3README 重寫成單一路徑 — harness「事前提醒」
**目標**self-hosted 開源後,README 是外界 CC 唯一入口。砍掉「玩法一/二/三」三選一,講清楚單一正確路徑。
**子步驟**(改根 `README.md`):
1. 砍三選一玩法,留**一條路**`acr init --self-hosted` → 寫 workflowprimitive 串 + recipe)→ `acr push` → trigger。
2. 明示心智(呼應 mindset §1):「零件就這固定幾個由我們維護、不接受自製;要打外部 API 就寫 recipe;要編排就寫工作流。」
3. 連到 `.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/` 下禁止 TScypher-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),不是口頭宣布。