Files
Arcrun/.agents/specs/component-gatekeeping/design.md
Leo 913ed79faa docs(gatekeeping): 方向修正 — 零件投稿走 GitHub PR,廢 registry self-service
richblack 2026-05-30 決定:零件投稿管道 = GitHub PR(稀有低頻事件、人 review
merge、CI 跑驗收),不是 registry submit API self-service。

理由:primitive 極少、未來絕大部分是 recipe → 新增零件稀有,不需 self-service
自動化管道。PR 天然滿足每道閘門(merge=人類閘門、CI 跑 G1/G3/Gherkin/覆蓋檢查),
且 CI 能 runtime 跑 wasm,繞開 CF Workers 不能 runtime 編譯 wasm 的 venue 牆。

黃金向量(Claude.ai 建議):價值保留、實作降級為人工核對 + B 覆蓋檢查(純靜態),
不做機器自動重跑(為不存在的規模做的過度工程 + 撞 venue 牆)。

作廢:registry submit 當主投稿管道、四路 self-service、平台 sandbox 重跑、
acr parts publish 加人類閘門。保留搬 CI:G1/G3/G4/覆蓋檢查邏輯。
已 commit 的 registry G0/G1/G3 保留不刪(G1/G3 邏輯被 CI 複用)。

§8 釐清(待確認改 DECISIONS):§8 不依賴 CI 指執行鏈路(高頻);零件投稿稀有,
走 PR/CI 不違反。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 13:07:46 +08:00

17 KiB
Raw Permalink Blame History

Design: Component Gatekeeping(零件投稿真把關)

2026-05-29。實作 requirements.md 的 R1-R6。


⚠️ 方向修正(richblack 2026-05-30):投稿走 GitHub PR,廢 registry self-service

零件投稿管道 = GitHub PR,不是 registry submit API。 理由與影響見下;以下 §0-§9 的 registry submit 設計, 凡屬「self-service 投稿管道」者作廢,凡屬「把關邏輯」者搬到 CIPR check)跑

為何改: primitive 極少、未來絕大部分是 recipe → 新增零件是稀有低頻事件,不需 self-service 自動化管道。 PR 天然滿足每道閘門:

設計的閘門 PR 怎麼天然滿足
G0 人類閘門 PR 必須有人 mergerichblack approve);AI 偽造不了 GitHub approve
舉證「為何不是工作流」 PR descriptionreview 時看
G1 假零件 / G3 純WASI / G4 Gherkin / 覆蓋檢查 / 黃金向量 CIPR check)跑 —— CI 有 tinygo + 能 runtime 跑 wasm繞開 CF Workers 不能 runtime 編譯 wasm 的 venue 牆

§8 衝突釐清: DECISIONS §8「不依賴 GitHub Actions」指執行鏈路init/push/run/recipe,常態高頻, 用戶機器+CF)。零件投稿是稀有低頻、該由 PR 治理,用 PR/CI 不違反 §8——反而更對(CI 能跑 wasm registry Worker 不能)。需在 DECISIONS 補這個區別(待 richblack 確認改穩定文件)。

哪些作廢 / 哪些保留:

  • 作廢:registry submit API 當主投稿管道、四路(CLI/MCP/py/jsself-service 投稿、平台端 sandbox 重跑、acr parts publish 加人類閘門(投稿不走 CLI 了)。
  • 保留並搬 CI:G1 假零件偵測邏輯(detectFakeComponent.ts)、G3 純WASIwasmImports.ts)、G4 Gherkin 真跑(CI 能跑 wasm)、B 覆蓋檢查、黃金向量人工核對。
  • 已 commit 的 registry G0/G1/G3 程式碼保留不刪(無害,且 G1/G3 邏輯被 CI 複用),但 registry submit 不再是主管道。
  • R5 本機 hook(擋 CC 直接造零件目錄)仍要 —— 它擋的是「繞過 PR 直接改 repo」,與 PR 管道互補。

以下 §0-§9 為原 design,閱讀時套用上述修正。


0. 架構總覽

0.0 範圍:把關跨公共庫 + self-hosted 私人庫(richblack 2026-05-29

把關不是公共庫專屬。每個 self-hosted 部署有自己的零件庫(自己的 registry Worker)。 加入公共庫或任何 self-hosted 私人庫,都跑同一套把關鏈(G0-G6 + 本機 hook)。

  • 實作上天然成立:把關邏輯在 registry Worker code 裡,self-hosted 跑同一份 registry → 把關跟著走,不需公私庫分兩套。
  • G0 人類閘門在 self-hosted 下,「人類」= 部署擁有者本人(防的是「他的 AI 自作主張把東西做成零件」,不是防他本人;他自己確認 + 舉證即可過)。
  • 理由:self-hosted 一樣有「自用服務沒驗證就變零件」的風險,且私人庫零件之後可能貢獻回公共。

0.1 把關鏈

投稿零件的唯一入口是 registry Worker 的 submit。把關鏈(依序,任一失敗即退稿):

submit 請求(帶 wasm + contract + 人類確認憑證 + 舉證)
  │
  ├─ G0 人類閘門(R4)          ← 最先擋:沒人類確認 + 舉證 → 403
  ├─ G1 假零件偵測(R2        ← contract/原碼有外部 URL 或 http 子集 → 退稿指回正路
  ├─ G2 size_check(已有)
  ├─ G3 syscall_scan + 純WASIR3)← 擴充:只准 WASI preview1 + u6u host func 白名單
  ├─ G4 gherkin_testsR1     ← 真跑 WASMgiven→stdin→比對 then_contains
  ├─ G5 cold_startmock,標未實作)
  └─ G6 runtime_compatmock,標未實作)
  → 全過 → 派 hash → 寫 KV

另一道獨立防線:本機 hook(R5),擋 CC 繞過 API 直接在 repo 造零件目錄。


1. G0 人類閘門(R4)— registry submit endpoint

1.1 請求格式增欄

submit 請求 body 增兩個欄位:

interface SubmitRequest {
  wasm_base64: string;
  contract: ComponentContract;
  human_confirmation?: {
    confirmed_by_human: true;          // 必須為 literal true
    reason_why_not_workflow: string;   // 非空,AI 舉證
    confirmed_at: string;              // ISO timestamp
  };
  skip_acceptance?: boolean;           // 既有:backfill 用(仍保留)
}

1.2 閘門邏輯

若 skip_acceptancebackfill 既有零件)→ 跳過 G0(這些是已驗、已部署的存量,不是新投稿)
否則(新投稿):
  若無 human_confirmation 或 reason_why_not_workflow 空 → 403
    "建零件需人類確認。請用 `acr parts publish`(會互動式問你),
     並說明為何工作流做不到。預設假設工作流能做——先試工作流 / recipe。"
  記錄 reason_why_not_workflow 進 KV metadata(軌跡可審)

1.3 四路收斂(CLI / MCP / Python / JS

  • 它們建零件都呼叫 registry submit endpoint → G0 在 endpoint,自動四路通管。
  • CLI acr parts publish:強制互動式 prompt 問人類「(1) 工作流為何做不到?(2) 確認要建零件?」,把答案組成 human_confirmation 送出。非互動環境(AI 直跑)acr 偵測 stdin 非 TTY → 拒絕並提示「需人類互動」。
  • MCP / Python / JS lib:傳 human_confirmation 才能成功;它們的 SDK 文件註明此欄位需人類提供。
  • 誠實限制(寫進 mindset + 文件):AI 技術上能偽造 confirmed_by_human:true。靠 reason 留記錄 + mindset 明示「絕不代替人類確認建零件」+ 軌跡可審,讓偽造成明確越界,不聲稱不可能繞過。

2. G1 假零件偵測(R2

新增 detectFakeComponent(contract, wasmBytes): string | null

  • (a) 外部 URL/domain:掃 contract 的 description / input_schema / output_schema 文字,及 wasm binary 文字,比對 URL patternhttps?://、常見 domain)。命中 → 退稿。
  • (b) http_request 子集:若 contract 宣告只做「打固定 endpoint」(heuristicdescription 含「打/呼叫 ... API/endpoint」且 input 有 url-like 欄位且無實質邏輯運算),標記疑似。
  • 退稿訊息:「偵測到疑似假零件(寫死 endpoint / http 子集)。這該是 API recipehttp_request + 固定設定)或工作流,不是零件。見 DECISIONS §1。」
  • 排除:auth_* primitivecredential 後端,DECISIONS §3b 不適用假零件判準)、http_request 自己。

3. G3 純 WASI 把關(R3

擴充現有 scanSyscalls

  • 現況:掃 FORBIDDEN_SYSCALLS 黑名單。
  • 擴充:改為「import 白名單」——解析 wasm import section,確認所有 import module 只屬 wasi_snapshot_preview1 + u6uhost functions)。出現其他 module → 退稿(runtime 鎖定風險)。
  • 實作:簡易 wasm import section 解析(不需完整 wasm parser,掃 import 段的 module name 字串)。

4. G4 Gherkin 真實作(R1)— 修訂(2026-05-29richblack review

4.0 為何不能在 registry Worker 跑(原設計作廢)

原設計假設 registry Worker instantiate 投稿 wasm 跑 Gherkin。此假設錯誤

  • Cloudflare Workers 禁止 request-time 編譯 WASMnew WebAssembly.Module(bytes) / WebAssembly.compile() 只能 startup 用 bundle 的 moduleworkers-types 把 Module 標 abstract 正反映此限制)。registry 收到的是 runtime 投稿 bytes → 跑不了。
  • DECISIONS §8:第一期不依賴 GitHub Actions → 也不能靠 CI 跑 Gherkin。
  • 剩下唯一一致 venue = 投稿者本地機器(有 tinygo + 能跑 wasm,與現有 build 流程同環境)。

4.1 正確設計:Gherkin 在投稿指令本地跑

零件投稿走一個獨立 CLI 指令(既有指令;「本地或公共都是投稿」):

  1. 本地 tinygo build(或讀已 build 的 .wasm)。
  2. 本地跑 Gherkin:對每個 gherkin_tests[],用 Node 的 WebAssembly + 同一份 wasi-shim instantiate wasmgiven→stdin→run→比對 then_contains。Node 環境能 runtime 編譯 wasm(不像 CF Workers)。
  3. 任一 scenario 失敗 → 投稿指令本地就擋下,不送出。
  4. 通過 → 把測試結果隨投稿上傳(見 4.2)。

runGherkin.ts(已寫,用 createWasiShim)邏輯正確,只是執行 venue 從 registry Worker 改成 CLINode。registry 端不再跑 Gherkin。

4.2 「平台看得到測試結果」(呼應 §3c:執行者不能驗證自己)

投稿 payload 帶 gherkin_evidence:每個 scenario 的 {scenario, given, actual_stdout, passed}

  • registry 存進 KV metadata(軌跡可審)。
  • 平台看得到原始 stdout,不是只看投稿者宣稱的「passed」。
  • 誠實限制(同人類閘門):本地跑 + 自報結果,AI 技術上能偽造 actual_stdout。靠軌跡可審 + mindset 明示 + 未來 §3c 的 test/relay(投稿走 relay 讓平台當下親跑,第一期後)補強。第一期是「本地跑 + 上傳證據 + 可審」,不聲稱不可繞過。

4.3 公私庫分流(投稿指令旗標)

  • 預設投稿 → 私人庫self-hosted 自己的 registry)。
  • -p / --public → 推公共庫
  • 兩者都跑同一套把關(§0.0:跨公私庫同一套)。差別只在目標 registry。

4.4 registry 端對應

  • registry submit 仍跑 G1(假零件)、G3(純WASI)——這兩個是靜態掃描,不需執行 wasm,CF Worker 可跑
  • G4 Gherkin 的執行移到 CLIregistry 收 gherkin_evidence 存證、可選做輕量一致性檢查(evidence 的 scenario 數與 contract.gherkin_tests 對得上、每個 passed=true),但不重跑(跑不了)。
  • G5/G6cold_start/runtime_compat)維持 unimplemented_steps 標記。

4.5 信任模型與發佈風險(richblack 2026-05-29 定,重要——勿誤讀為零風險)

Gherkin 全綠 ≠ 零件安全可發佈。 把關能驗到什麼、不能驗到什麼,要說清楚:

把關各層各防什麼

防得了 防不了
G4 Gherkin 「零件對宣稱的 given 不 work」 「Gherkin 寫得避重就輕、只測會過的 case」——投稿者可寫不完整的測試,沒測到的 input 仍可能出錯/惡意
G1 假零件 / G3 純WASI 連外 / 非 WASI / 寫死 endpoint 沙箱內的邏輯錯誤
純 WASI 沙箱(真正的發佈底氣) 框死零件能做的事:只能 stdin→stdout,無網路 syscall、無檔案系統、無 host 能力(除白名單 host func)→ 就算邏輯有問題或惡意,傷害被沙箱框死 沙箱允許範圍內的錯誤輸出
市場機制(§1 長尾:別人用了發現不對 → 回報 → 修 投稿當下測不到的

結論:發佈非零風險,是風險可控。 主要安全保證是純 WASI 沙箱框死能力(不是 Gherkin)。Gherkin 驗行為契約、沙箱框死破壞力、市場補長尾,三者合起來讓「發佈陌生人的零件」風險可控到能接受。

信任層級(誰跑 Gherkin 決定可信度,§3c:執行者不能驗證自己)

  • 第一期:投稿者本地跑 + evidence 上傳可審。AI 技術上能偽造 actual_stdout(它控制本地環境)。誠實標明:第一期 evidence 可造假,靠純 WASI 沙箱 + 軌跡可審 + mindset 撐著。
  • 未來:平台自己重編源碼 + 重跑 Gherkin(投稿者無法造假「跑的結果」)。需要能 runtime 跑 wasm 的 venue——非 CF Worker(不能 runtime 編譯)、非 CI(§8 不依賴)——可能是公共庫專屬的 sandbox 服務。列未來,可能擋公共庫「完全可信發佈」。
  • 再往後:§3c 的 test/relay(投稿走 relay 讓平台當下親跑)。

5. G5/G6 mock 標未實作(R3 誠實)

  • cold_start / runtime_compat 保留 mock,但 SandboxResult 增 unimplemented_steps: string[],回傳時明列 ["cold_start","runtime_compat"],submit 回應與文件明示「這兩步未實作、未真正驗證」。不回 success:true 假裝全綠——回 success 但附 unimplemented 清單。

5.5 黃金向量:人工核對 + B 覆蓋檢查(richblack 2026-05-30 定)

Claude.ai 建議用「黃金向量 + 把關自己重跑」自動驗收,防放水的 Gherkin。價值保留,實作降級,理由:

  • primitive 極少、未來絕大部分是 recipe(人類閘門 + 工作流優先把零件擋在源頭)。現役 17 白名單 + cron/platform_crypto,未來極少新增。
  • 「把關自動重跑向量」是為「零件大量增加」做的規模化基建——但零件不會大量增加 → 為不存在的規模做自動化 = 過度工程(DECISIONS 附錄「會不會累積成債」判準)。
  • 且「把關自己重跑 wasm」撞 venue 牆(CF 不能 runtime 編譯 wasm,同 §4.0)→ 需平台端 sandbox(第一期沒有)。

降級後做法:

  • A 黃金向量 conformance → 人工核對:黃金向量當「人類閘門時用 CC 核對 primitive 的對照表」。新增 primitive(極稀有)時,人類閘門已要你親自確認,那一刻用 CC 本地跑向量核對(本地有 tinygo + 能跑 wasm,繞開 venue 牆)、人工凍結。不做機器自動重跑。
  • B 覆蓋檢查 hook → 現在做(純靜態、不可造假、成本低、價值與零件數無關):靜態 parse contractinput_schema 每個 required 欄位至少出現在一個 Gherkin given、output_schema 每個欄位至少被一個 then_contains 斷言。缺 → exit 2 指出漏哪個欄位。擋「只測 happy path、不碰宣告過的行為面」。
  • 初始向量來源(信任根:寫向量≠寫實作):另起 session 從 contract 語義寫、不看實作原碼(primitive 語義客觀如 add(2,2)=4)。人工核對用,不急、新增 primitive 時逐個補,不必 21 個一次到位。
  • 殘留(誠實):向量/覆蓋檢查擋不住「列出的 case 全對、沒列到的 case 錯」。交給「用」——出 bug 補進向量,永不 regress。是會長大的網,非設一次完美。

6. R5 白名單 + 本機 hook

  • registry/MVP_COMPONENTS.txt:一行一個白名單 canonical_id(現役 22 個)。
  • pre-write-guard.sh 增規則:寫入 registry/components/{name}/...{name} 不在 MVP_COMPONENTS.txt → exit 2,訊息「新增零件需走 submit API 人類閘門,不可直接造 repo 目錄」。
  • pre-bash-guard.sh 增規則:mkdir .../registry/components/{白名單外} → exit 2。
  • .ts 偵測現有 hook 已做(rule 1.1)。
  • B 覆蓋檢查5.5):可放 registry submit 的靜態驗收(不需跑 wasmCF 可跑)或 pre-write hook,擋宣告過的欄位沒被 Gherkin 測到。

7. 範圍邊界

  • 動 registry TSsandboxAcceptance / submitComponent / routes / types+ CLIacr parts publish,既有指令)+ hook
  • 不動 cypher-executor 執行路徑、不動既有零件 wasm。
  • backfill 路徑(skip_acceptance)保持可用,不被新閘門擋(存量零件不需人類閘門)。
  • CLI/MCP/Python/JS 四路:本期至少做 CLI acr parts publish + registry endpoint 強制;MCP/Python/JS 補 human_confirmation 欄位支援(薄)。

8. 驗收標準

  • 投一個寫死 endpoint 的假零件 → G1 退稿(終端輸出)。
  • 投一個 .ts 進 registry/components → hook exit 2。
  • 投一個白名單外的新零件目錄(本機造)→ hook exit 2。
  • 無 human_confirmation 的 submit → 403。
  • 帶 human_confirmation + 過 Gherkin 的真零件 → 通過、寫 KV、reason 留 metadata。
  • Gherkin given/then 對真零件跑綠;故意改壞 then_contains → 退稿。
  • cold_start/runtime_compat 在回應裡列入 unimplemented_steps(不假綠)。

9. 決議(richblack 2026-05-29 design review 定)

  • Q1 → 消解Gherkin 測的零件永遠是封閉邏輯(框架),不連外。任何要加外部 URL 的東西按定義就是 recipe,不是零件——這種「連外零件」根本不該存在(會被 G1 假零件偵測擋下、降成 recipe)。所以 G4 Gherkin 只跑不需 host function 的封閉邏輯零件,不需要 mock host func、不需要 skip 機制。零件用 u6u.http_request 連外 = G1 直接退稿。
  • Q2 → 兩者都硬擋(a) contract/原碼有具體外部 URL/domain → 硬退稿;(b) 宣告能力是 http_request 子集 → 也硬退稿。理由:與 Q1 一致——零件不該連外,這兩個 pattern 都是「該是 recipe 的東西偽裝成零件」,硬擋無誤殺顧慮(真的要連外就去做 recipe)。
  • Q3 → submit 過閘門後自動 append:人類閘門通過 + 驗收綠的零件,submit 成功時自動把 canonical_id append 進 MVP_COMPONENTS.txt。白名單反映「已正當投稿的零件」,不需手動維護。本機 hook 讀此檔擋「白名單外的直接造目錄」。

Q1 連帶結論(強化 G1

既然「零件不連外、連外即 recipe」是硬規則,G1 假零件偵測 = G4 Gherkin 的前置守門: G1 擋掉所有連外/http 子集的投稿 → 能進到 G4 的必然是封閉邏輯零件 → Gherkin 必然不需 host func。 兩道閘門邏輯自洽。