Files
Arcrun/.agents/specs/component-gatekeeping/design.md
T
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

219 lines
17 KiB
Markdown
Raw 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.
# 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 投稿管道」者**作廢**,凡屬「把關邏輯」者**搬到 CI(PR 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 增兩個欄位:
```ts
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 pattern`https?://`、常見 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` + `u6u`host 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 編譯 WASM**`new 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 contract`input_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 TS**sandboxAcceptance / submitComponent / routes / types+ **CLI**acr 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。
兩道閘門邏輯自洽。