# Tasks: Component Gatekeeping

> 對應 design.md。每完成一個 task 立刻標 [x]，不批次。
> Design 已 richblack 確認（2026-05-29，含 Q1-Q3 決議）。

---

## ⚠️ 收尾狀態（2026-05-30，方向修正後）

投稿改走 **GitHub PR**（廢 registry self-service，見 design 頂部「方向修正」）。本 SDD 收尾於：
- **已完成且 commit**：G1（detectFakeComponent）、G3（wasmImports）、G5/G6（unimplemented_steps）、
  G0 registry 人類閘門（保留不刪）。測試 15 綠。
- **改去向**：G4 Gherkin / 覆蓋檢查 → 未來接 CI PR check（CI 能跑 wasm）。G1/G3 邏輯可複用。
- **不做**：CI PR check（richblack：人工 review 就夠，primitive 極少）、R5 本機 hook
  （PR/merge + G1 + 沙箱已防「未經同意變公共零件」，hook 過度工程）、registry self-service、
  acr parts publish 加閘門、平台 sandbox 重跑。
- **黃金向量**：人工核對（另起 session 從語義寫），不急、不機器自動化。
- **轉出範圍**：真正的裸奔風險在「資料外流」（recipe/webhook 把資料送出去），不分公私庫
  → 另開新 SDD「資料外流警示」（API 層警示 + AI 動手前 hook）。用戶 API 保護（入站認證）
  + recipe/part/function 架構釐清 → 記 BACKLOG 待決策。

---

## W3 `acr parts` 去 hardcode recipe → 動態 store 清單（2026-06-29，總管交辦，issue #13 根治）

> 根因（leo 2026-06-29）：「telegram 與 google 不一致」其實是**查錯表**——讀 `acr parts` + BUILTIN_CREDENTIALS_MAP
> 推論「telegram 走不同機制」。對外部服務該查的是動態 store-backed 的 `acr recipe list` / `acr auth-recipe list`，
> 在對的表裡 telegram 與 google 同一條鏈。`acr parts` 把 5 個 recipe hardcode 進零件清單（parts.ts:197-261）→
> 誤導下一個人查錯表 + pushed recipe 在 `acr parts` 永遠看不到。這是 component(靜態PR-only) 與 recipe(動態store) 的 conflation。
> 屬薄殼修正（07 §1 seed 正例：清單應反映 store 真相，不釘 hardcode）；不違禁令（非新增 seed/upsert 拼裝）。

- [x] W3.1 `acr parts` 移除 5 個 recipe hardcode（gmail_send/google_sheets_append/telegram_send/line_notify_send/notion），只留真零件（WASM）。實證：`acr parts` 只剩 http_request 等真零件。
- [x] W3.2 統一跨類搜尋（leo 2026-06-29 升級）：**新增 `acr search <term>`**（cli/src/commands/search.ts）fan-out 4 來源——component(靜態 BUILTIN_COMPONENTS) + recipe(GET /recipes) + auth-recipe(GET /auth-recipes) + workflow(GET /webhooks/named)，依類別回 counts+命中，各來源獨立 try（離線降級成指路，不連坐）。`acr parts` 末段改指向動態清單 + `acr search`。實證：`acr search telegram` → `component:0 recipe:1 auth-recipe:0 workflow:1`（一眼看出 telegram 是 recipe）。
- [x] W3.3 parts.ts 檔頭註解寫死分流原則：component=靜態(PR-only WASM)、recipe/auth-recipe/workflow=動態(store-backed)；移除處留註解防再 hardcode。

## G1 假零件偵測（R2，Q2=兩者硬擋）
- [x] 1.1 `registry/src/actions/detectFakeComponent.ts`：(a) 外部 URL/domain 偵測（掃 contract 文字 + wasm binary 文字）硬擋；(b) http_request 子集偵測硬擋
- [x] 1.2 排除 `auth_*` primitive 與 `http_request` 自己
- [x] 1.3 接進 sandboxAcceptance 步驟鏈（fake_component_scan 為第一步）
- [x] 1.4 退稿訊息指回正路（「這該是 recipe/工作流」）

## G3 純 WASI 把關（R3）
- [x] 3.1 `wasmImports.ts` 解析 wasm import section，取出所有 import module name（已對真實零件驗證）
- [x] 3.2 白名單：只准 `wasi_snapshot_preview1` + `u6u`；其他 module → 退稿
- [x] 3.3 接進 scanSyscalls（白名單為主，黑名單為次）

## G4 Gherkin 真實作（R1）— venue 修訂：CLI 本地跑，非 registry（design §4 修訂）
> CF Worker 不能 runtime 編譯 wasm + §8 不依賴 CI → Gherkin 在投稿指令本地（Node）跑。
> 在第一段（tinygo build→wasm 之後）測，跟 worker 無關。registry 只存 evidence 不重跑。
- [x] 4.1 `runGherkin.ts`（createWasiShim 跑 wasm，邏輯正確）—— 但 venue 要從 registry 改 CLI
- [ ] 4.2 回退 sandboxAcceptance：registry 不跑 Gherkin（移除 await runGherkin），改回靜態步驟
- [ ] 4.3 Gherkin 邏輯搬到 CLI 投稿指令（Node 環境 instantiate wasm）
- [ ] 4.4 投稿 payload 帶 gherkin_evidence（scenario/given/actual_stdout/passed），registry 存 metadata 可審
- [ ] 4.5 誠實標明第一期 evidence 可造假（mindset + 文件）；平台重跑列未來
- [ ] 4.6 投稿指令（暫名 acr component submit）+ 公私庫分流（-p 公共）— 新 CLI 工程

## G5/G6 誠實標未實作（R3 禁假綠）
- [x] 5.1 SandboxResult 增 `unimplemented_steps: string[]`
- [x] 5.2 cold_start / runtime_compat 列入 unimplemented_steps，submit 回應明示

## G0 人類閘門（R4，核心）
- [x] 0.1 submit 請求增 `human_confirmation`（SubmitOptions in submitComponent.ts）+ route 解析（multipart/JSON 皆支援）
- [x] 0.2 submit 邏輯：非 skip_acceptance 的新投稿，無 human_confirmation/空 reason → gateError（指回正路）
- [x] 0.3 human_confirmation + gherkin_evidence 寫進 KV metadata（軌跡可審）
- [ ] 0.4 CLI `acr parts publish`（既有指令）：互動式問人類（工作流為何做不到 + 確認），非 TTY 拒絕
- [ ] 0.5 MCP / Python lib / JS lib 補 human_confirmation 欄位支援（薄）
- [ ] 0.6 誠實限制寫進 mindset Skill（步驟 7）+ SDK 文件

> 命名修正（2026-05-29）：投稿走**既有** `acr parts publish`（cli/src/commands/parts.ts），
> 非另建 acr component create（符合「修改現有不重建」）。G0-CLI（0.4）與 G4-CLI 合併在此指令做。

## R5 白名單 + 本機 hook
- [ ] 5.3 `registry/MVP_COMPONENTS.txt`（現役 22 個 canonical_id）
- [ ] 5.4 submit 過閘門成功 → 自動 append canonical_id 進白名單（Q3）
- [ ] 5.5 pre-write-guard.sh：寫 `registry/components/{白名單外}/` → exit 2
- [ ] 5.6 pre-bash-guard.sh：mkdir `registry/components/{白名單外}` → exit 2

## W1 ~~CLI workflow validate 擋假零件式 component 名~~（2026-06-01 作廢，方向修正）

> **作廢原因（richblack 2026-06-01）**：「擋假零件」這件事不再存在——因為**自製/修改零件的路
> 已被封鎖**（CC 根本造不出零件），workflow 引用 recipe（如 component: kbdb_get）是**合法且
> 未來唯一的擴充方式**，不該被當「假零件」擋。把關點從「workflow validate」**移到 recipe 入庫
> （push）那一刻**。已動的 yaml-parser.ts `LEGAL_PRIMITIVES`/`findSuspectComponents` 已回退。
> 取而代之 → 見 W2。

## W2 封鎖自製零件 + recipe 入庫把關（2026-06-01 新方向）

> richblack 2026-06-01 定調：
> - 零件由維護者管理，**CC 不能自製/修改零件**（hook + CLI 拒絕）→ 不再有「假零件」。
> - CC 唯一能擴充的是 **recipe**。recipe 一律用「推（push）」，**自有庫與公共庫同一套指令**。
> - 把關依庫別分強度：
>   - **自有庫（self-hosted）**：只能**提醒**（無法在別人機器強制）。兩個提醒：
>     (1) 資料外流提醒——某動作會讓外界看到你的東西（如 workflow 產對外 webhook），同意後是他的責任；
>     (2) 打通檢查——查他要打的 API 是否打得通（2xx）。
>   - **公共庫**：由維護者機制檢核「實際打通、真收到成功回傳」（PR/CI relay，DECISIONS §3c，第一期後）。
> 屬 change，需先寫 design 給 richblack review 才動 code。本節先記框架。

- [x] W2.1 封鎖自製零件 — **釐清完成（richblack 2026-06-02）**：靠「零件投稿走 GitHub PR + 人 merge」
  天然閘門（DECISIONS §8）。BACKLOG 步驟5「不做 hook」真意 = 零件少、不為零件 PR 蓋自動化把關
  （量少人工檢查；爆量才回頭想），**不是**不阻止自製。無矛盾，不需新做 hook。
- [x] W2.2 `acr recipe push` 資料外流提醒 — **既有實作已涵蓋**：recipe.ts:70-79 `obtainExposureConsent`
  （exposure-warning.ts：互動打資源名確認、非 TTY 拒絕、首次問記住）。data-exfil-warning SDD 已做，確認涵蓋 recipe push 路徑。
- [x] W2.3 `acr recipe push` 打通檢查 — **新增** `probeRecipeEndpoint`（recipe.ts）：push 成功後實打 endpoint，
  回報 2xx/⚠。提醒級不硬擋；endpoint 含 {{模板}} → 誠實說明待 run 才知；401/403 → 標「多半缺 credential，非 recipe bug」（不假綠，mindset §7）
- [ ] W2.4 公共庫 push（--public）= 維護者 relay 檢核（DECISIONS §3c）— 第一期後，本期只做自有庫提醒級

## 驗收（design §8）
- [ ] V1 投寫死 endpoint 假零件 → G1 退稿（終端輸出）
- [ ] V2 投 `.ts` 進 registry/components → hook exit 2
- [ ] V3 本機造白名單外零件目錄 → hook exit 2
- [ ] V4 無 human_confirmation 的 submit → 403
- [ ] V5 帶 human_confirmation + 過 Gherkin 真零件 → 通過 + reason 留 metadata + 白名單 append
- [ ] V6 故意改壞 then_contains → Gherkin 退稿
- [ ] V7 回應含 unimplemented_steps（cold_start/runtime_compat 不假綠）
