# 禁止行為清單（零容忍）

**這份清單由 `.claude/hooks/*.sh` 強制執行。違反會 block 工具呼叫（exit 2）**。

---

## 第一類：零件實作層級的禁令

### 1.1 禁止在 `registry/components/` 下建立 TypeScript 檔案
零件**只能**用 TinyGo（`.go`）或 AssemblyScript（`.ts` 但需 `asconfig.json`）實作，並編譯成 `.wasm`。
cypher-executor/registry Worker 或 `.component-builds/` 內的 TS 不算零件邏輯，那是 WASI shim。

**Hook 會擋**：新增 `registry/components/*/{檔案}.ts`（除非目錄內有 `asconfig.json` 明確標記為 AssemblyScript）。

### 1.2 禁止建立新的 `auth_*` 目錄以外的 auth 實作
所有 auth 邏輯只能在：
- `registry/components/auth_static_key/`
- `registry/components/auth_oauth2/`
- `registry/components/auth_service_account/`
- `registry/components/auth_mtls/`

**不可以**出現 `cypher-executor/src/auth-primitive/`、`cypher-executor/src/lib/auth-*.ts`、`auth-worker/`、`credential-worker/` 等目錄。

**Hook 會擋**：`mkdir` 或 `Write` 到上述違規路徑。

### 1.3 禁止用 `wrangler init/generate` 建立 auth/credential/jwt 相關的 TS Worker
Auth primitive 必須透過 `component-worker-template/` 搭配 WASM binary 部署。

**Hook 會擋**：bash 指令含 `wrangler (init|generate) ... auth_`、`... credential_`、`... jwt_` 的 pattern。

---

## 第二類：cypher-executor TS 的禁令

### 2.1 禁止新增任何 credential / auth / jwt 相關的 TS 檔案
**清除已完成**（2026-07-20，credential-primitives-wasm 卷已封存）。下列曾違規的 TS **均已不存在**，
列此僅為「禁止重新引入」的清單：
- ~~`cypher-executor/src/actions/credential-injector.ts`~~ → 已刪（auth 走 WASM primitive）
- ~~`cypher-executor/src/lib/jwt-signer.ts`~~ → 已刪（RS256 在 auth_service_account WASM）
- ~~`component-loader.ts` 的 `BUILTIN_API_RECIPES` / `BUILTIN_CREDENTIALS_MAP`~~ → 已整段刪

**重新建立上述任一者 = 違規**。

**Hook 會擋**：新增任何路徑含以下關鍵字的 `.ts` 檔案：
- `credential-injector`、`credential_injector`
- `jwt-signer`、`jwt_signer`
- `auth-dispatcher` 的 TS 若嘗試在裡面實作 credential 解密 / template 展開 / JWT signing，block

### 2.2 禁止在 cypher-executor 任何 TS 裡實作以下邏輯
這些邏輯全部屬於 WASM 零件職責：

- AES-GCM 解密（`crypto.subtle.decrypt`）— 只准出現在 `wasi-shim.ts` 的 `crypto_decrypt` host function
- RSA-SHA256 簽章（`crypto.subtle.sign` with RSASSA-PKCS1-v1_5）— 只准出現在 `wasi-shim.ts` 的 `crypto_sign_rs256` host function
- Template 展開（`{{secret.X}}` / `{{runtime.X}}` 替換）— 只能在 WASM 零件內
- PEM → PKCS8 解析
- JWT header/payload/signature 組裝
- Token exchange（拿 service account JWT 換 access_token）
- 具體 API call 實作（例如 gmail send / telegram sendMessage / google sheets append）

**Hook 會擋**：
- Write/Edit 到 `cypher-executor/src/` 下的 `.ts` 時，內容含：
  - `crypto\.subtle\.decrypt` 且檔名不是 `wasi-shim.ts`
  - `crypto\.subtle\.sign.*RSASSA` 且檔名不是 `wasi-shim.ts`
  - `interpolateTemplate`、`\{\{secret\.` 的模板邏輯
  - `BUILTIN_API_RECIPES`、`BUILTIN_CREDENTIALS_MAP`（新增用）
  - `gmail.googleapis.com/gmail/v1/users/me/messages/send` 類 hard-code API URL
  - `api.telegram.org/bot.*sendMessage`
  - `sheets.googleapis.com/v4/spreadsheets`
  - `notify-api.line.me/api/notify`

### 2.3 cypher-executor TS 的合法職責（允許）
- HTTP routing（Hono routes）
- workflow 執行排程（`graph-executor.ts`）
- 呼叫 WASM 零件（透過 HTTP fetch 到對應 Worker URL，或 Service Binding fallback）
- 提供 host function（`wasi-shim.ts` 的 `kv_get` / `crypto_decrypt` / `crypto_sign_rs256`）
- KV/R2/Service Binding 存取封裝

---

## 第三類：架構層級的禁令

### 3.1 禁止新增 Service Binding
**Cypher binding 不是 Cloudflare service binding**。它是 YAML/KV 裡的 URL 清單。

零件串接（workflow 層）一律走 HTTP URL，不走 `[[services]]`。

13 個現有的 `SVC_*` 綁定（`cypher-executor/wrangler.toml`，邏輯零件）是歷史遺產（效能優化），**保留但不新增**。

> **2026-06-06 註**（來源：credential-primitives-wasm Phase 7，該卷已封存於 `system-dev/docs/3-specs/archive/`；**本註記述的規則仍現行有效**）：self-hosted 的 cypher 與 auth worker 同在 `{sub}.workers.dev` zone，cypher `fetch()` 打 auth 觸發 CF **same-zone 1042**（壓測階段 11）。**未用 service binding 解**（評估後廢：service binding 靜態、加/改要重 deploy cypher）。改用 **`global_fetch_strictly_public` compatibility flag**（cypher wrangler.toml）讓 same-zone fetch 走公網前門 → 同 zone 也通，**auth 維持 HTTP fetch、不加 binding**。故本禁令不變。

**Hook 會擋**：bash 指令含 `wrangler tail` 以外、涉及 `[[services]]` 新增的 pattern；Edit wrangler.toml 新增 `[[services]]` 區塊時警告確認。

### 3.2 禁止以「從 R2 取 WASM」為設計
平台內建零件已 bundle 進各自 Worker，不從 R2 取。
R2 只在 Phase 5（用戶自製零件）啟用。

**Hook 會警告**：TS 中出現 `env.WASM_BUCKET.get(` 的新增 code（除非在明確標註的 Phase 5 user-submit 路徑中）。

### 3.3 禁止複製貼上 Worker 程式碼到新目錄
要改 `gmail` 零件 → 改 `registry/components/gmail/main.go`，重新編譯、部署。
**不准**新建 `gmail-v2/`、`new-gmail/`、`gmail-worker/` 等目錄。

**Hook 會擋**：`mkdir` 或 `Write` 到 `{component-name}-v2/`、`new-{component-name}/`、`{component-name}-worker/` 類路徑。

### 3.4 禁止在 SDK 內做 server 職責
- **禁止**：SDK 裡做 server 端解密、credential-injector 重實作、workflow executor、auth recipe 解析
- **允許**：SDK 做 HTTP thin wrapper + client 端加密（AES-GCM）

---

## 第四類：流程層級的禁令

### 4.1 禁止沒讀 SDD 就動 code
見 `00-sdd-protocol.md`。

### 4.2 禁止批次更新 tasks.md
每完成一個 task 就立刻 mark `- [x]`。不准「先全部做完再一次更新」。

### 4.3 禁止新建 SDD 而不事先與 richblack 確認
SDD 屬於架構決策，必須人確認。CC 不可以自行在 `docs/3-specs/` 底下建新目錄。
例外：在現有 SDD 目錄內新增 `requirements.md` / `design.md` / `tasks.md` 的單檔補充（需在 CLAUDE.md 已註記的 SDD 範圍內）。

---

## 第五類：薄殼原則的禁令（詳見 07-thin-shell.md）

### 5.1 禁止在薄殼介面（cli/src/、arcrun-mcp/src/）實作業務邏輯
能力只實作一次，放在 API（cypher-executor 端點）。薄殼只做介面轉換 + 暴露。

**Hook 會擋**（規則 7.x，範圍 `cli/src/` 與 `arcrun-mcp/src/` 的 .ts/.js）：
- 7.1 新增 `seedApiRecipes` / `seedAuthRecipes` / `seedRecipes` 編排函式（seed 是 API 行為，§4.1 反例）
- 7.2 介面層拼裝 upsert（同函式內 PATCH + POST + upsert/找則改否則建 字樣）
- 7.3 client 端「全部成功才做下一步」gate（`deployFullyOk` 類，§4.1 反例）

### 5.2 禁止用**零件**補 API 缺的能力（污染零件庫）
缺能力 → 先看 07 §3.5 自力救濟階梯分流：**自家** API 缺 → 補 API endpoint；**第三方** API 缺（gsheets filter / 無 upsert）→ 走 **workflow/code-node 補丁**（合法，是資料產物非介面層 TS，hook 範圍外）；純計算 → code-node；真需新穩定能力才建零件 PR。
禁的是「在介面層拼裝」（5.1，hook 擋）與「亂建零件污染零件庫」，**不是禁任何補丁**（hook 偵測不到語意，靠 code review）。

### 5.3 禁止同一 API 能力在不同介面用不同參數簽名 / 連不同帳號
`validate` 在 CLI 吃 YAML、MCP 卻要 `api_key`+`graph` = 底層分歧。薄殼差異只能來自介面慣例。
所有薄殼讀同一份身份來源（見 07 §4）。

---

---

## 第六類：租戶字串來源（Arcrun#108／#105 同族）

### 6.1 靜態租戶字串不得用於資料面過濾
**知識資料面的 `owner_id`（三元組／entries／records／藏書地圖／工作流 KV）必須與寫入端同源。**
寫入端只有一個真相源＝使用者 `~/.arcrun/config.yaml` 的 `api_key`（＝實例 namespace，
CLI push／小幫手上傳／MCP 都用它）。讀取端拿另一份手抄的環境變數預設值 → 全被過濾掉。

實害：`portalTenant(env) = env.CONSOLE_TENANT || "leo"` 讓 leo 的 **1854 條三元組被過濾成 0 個庫**
（#108）；前一天 `ownerNamespace(env) = env.MCP_OWNER_NAMESPACE || "leo"` 是同一句話（#105）。

**規則**：
1. `cypher-executor/src/lib/tenant.ts` 是租戶字串的**唯一產地**。
   `CONSOLE_TENANT` / `ARCRUN_NAMESPACE` 只能在該檔被讀取。
2. 知識資料面用 `knowledgeOwner(env)`（回 `TenantId`），過濾一律經
   `ownerQuery()` / `ownerField()`——它們只吃 `TenantId`，`tsc` 就擋掉「隨手一個 string」。
3. 帳號層用 `accountTenant(env)`（回 `string`，**刻意不是 TenantId**）：帳號子 namespace
   `{tenant}::portal` 與 cypher 自己寫的設定用它，型別上不可能流進知識資料面。
4. 身分解析路徑上**不准有字面預設值**。解析不到 → 丟 `TenantUnresolvedError`，
   誠實回「讀不到」（不是「你沒有」，#100 同一條）。

**機械強制**（規則存在但沒機制驗證＝它會再犯第三次）：
- 出貨閘：`scripts/build-worker-artifacts.mjs` 編 tier2 成品前先掃，違規 → **編不出成品**。
- 本機自查：`cd cypher-executor && npm run check:tenant`（`npm test` 也會先跑它）。
- 規則本體：`cypher-executor/scripts/tenant-source-rules.mjs`（純函式）；
  閘自己的測試：`cypher-executor/tests/tenant-gate.test.ts`（壞例子會擋＋合法寫法零誤攔）。

> 尚未接上 PreToolUse hook（`.claude/hooks/` 為受保護檔案，需人類加入）。
> 要加的話：檢查器已備妥 `--stdin <相對路徑>` 模式，可在寫入前擋。

## Hook Block 訊息格式

當 hook 擋住一個操作時，訊息格式統一為：

```
❌ BLOCKED by arcrun CLAUDE rules
違反項：<禁令編號，例如 2.2>
原因：<簡短說明>
正確做法：<該改去哪裡、該用什麼方式>
參考：.claude/rules/<對應檔案>
```

這樣 CC 拿到錯誤訊息後有機會自行導正，不是被擋死就愣住。
