# Skill: Write Recipe（寫 recipe）

## 何時用這個 skill

**`/cypher/search` 回 `not_found` 且 `suggestion` 指「寫 recipe」時，讀這支。**

- 要打某個外部 API（Google Slides / Slack / 任何有 HTTP API 的服務），但 `arcrun_recipe_list()` 沒有
- `acr recipe search <關鍵字>` 落空，回「公庫無符合的 recipe」
- 你想把一條常用的 API 呼叫封裝成可重用、可投稿的配方

> 🔴 **不要因為沒有 recipe 就改寫成 `code` 節點**——那是「腹語術」。
> recipe 不用改平台、不用部署 Worker、不用寫程式，**幾行 YAML 就能自己補上**。

## recipe 是什麼

**「http_request ＋ 參數模板」的具名封裝**（真身存在 cypher-executor 的 RECIPES KV）。
執行時 cypher-executor 直接 fetch 該 endpoint——不 deploy Worker、不寫 WASM。

| 你缺的是… | 走哪條路 |
|---|---|
| 打**外部 API**（服務有 HTTP API） | **recipe（本 skill）** |
| **計算能力**（加解密／壓縮這類純運算） | 零件 PR → `arcrun_get_skill('add_new_wasm_component')` |

## 1. Recipe 的欄位（schema）

```yaml
canonical_id: google_slides_create   # 必填。小寫底線，全庫唯一的可讀名
endpoint: https://slides.googleapis.com/v1/presentations   # 必填。要打的 URL
method: POST                         # 選填，預設 POST（GET/PUT/PATCH/DELETE 皆可）
display_name: Google Slides Create   # 選填，人看的名字
description: 建立一份新簡報。POST presentations，body 帶 title。auth: google service_account。
auth_service: google_slides_sa       # 選填。指向 auth recipe（見第 3 節）
headers: {}                          # 選填。額外固定 header
body: {}                             # 選填。固定 body 欄位（會與節點 payload 合併）
```

- `hash_id`（`rec_xxxxxxxx`）與 `uuid` 由系統自動生成，不用寫。
- **description 認真寫**：AI（包括未來的你）靠它決定要不要用這個 recipe。
  照庫裡的慣例寫「做什麼。怎麼打。auth 用什麼。」三段。

## 2. 真範例（從實際 seed 且實跑過的 recipe 照抄）

### A. token 在 URL path（`telegram_send`）

```yaml
canonical_id: telegram_send
display_name: Telegram Send
description: Telegram sendMessage。token 在 URL path（{{auth.bot_token}}），body 帶 chat_id+text。auth: static_key path 注入。
endpoint: https://api.telegram.org/bot{{auth.bot_token}}/sendMessage
method: POST
auth_service: telegram
```

### B. Bearer／service account（`gmail_send`）

```yaml
canonical_id: gmail_send
display_name: Gmail Send
description: 寄 Gmail。POST messages/send，body 帶 raw（base64url MIME）。auth: google service_account。
endpoint: https://gmail.googleapis.com/gmail/v1/users/me/messages/send
method: POST
auth_service: google_gmail_sa
```

### 模板變數（endpoint 裡可用）

| 變數 | 來源 | 例 |
|---|---|---|
| `{{auth.X}}` | auth recipe 的 `inject.path` 注入 | `bot{{auth.bot_token}}/sendMessage` |
| `{{_path}}` | 節點 payload 的 `_path` 欄位（路徑由工作流決定時用） | `https://sheets.googleapis.com{{_path}}` |

🔴 **金鑰只准名字，不准真身**：endpoint／headers 裡**絕不**寫死 token。
真值走 credential 中心（`acr creds push`），recipe 只留 `{{auth.X}}` 這種名字引用。

## 3. auth 怎麼接（多數 recipe 需要）

`auth_service: telegram` 表示執行時去拿 `auth_recipe:telegram` 做認證注入。
先查有沒有：`acr auth-recipe list` 或 `GET /auth-recipes/<service>`。

**沒有就要一併建**（`POST /auth-recipes`，缺這步 recipe 會注入空值打不通）：

```yaml
service: telegram            # 必填
primitive: static_key        # 必填：static_key | oauth2 | service_account
base_url: https://api.telegram.org   # 必填
required_secrets:            # 必填，每個 secret 的 help_url 也必填（官方文件連結）
  - key: telegram_bot_token
    label: Bot Token（從 @BotFather 取得）
    help_url: https://core.telegram.org/bots/features#botfather
inject:                      # 必填：secret 注入到哪（header / query / body / path）
  path:
    bot_token: "{{secret.telegram_bot_token}}"
```

然後用戶端上傳真值：`acr creds push`（存進 credential 中心，AI 拿不到真身）。

## 4. 裝上（三個介面同一個 API）

```bash
acr recipe push my_recipe.yaml        # CLI：push 時會實打 endpoint 做打通檢查
```

- MCP：`arcrun_recipe_push(...)`
- HTTP：`POST /recipes`（JSON，欄位同上）

裝好後 workflow 直接引用（節點名＝canonical_id，或 config 指定）：

```yaml
config:
  notify:
    component: telegram_send    # 或穩定引用 rec_xxxxxxxx
```

payload（如 `chat_id`、`text`）由上游節點或 context 給——這正是「AI 只填 payload」。

## 5. 驗收（誠實原則）

1. push 時的**打通檢查**只是提醒級——真驗收＝**跑一次 workflow、`verdict=success`（2xx）**
2. 缺 credential 打不到 2xx → 誠實標「未驗收：缺 X」，**不 mock 充綠燈**
3. 打通後可投稿公庫讓別人用：`acr recipe submit-p <canonical_id>`（成為該 recipe 的作者版本）

## 6. 常犯的錯

1. **token 寫死在 endpoint／headers** → 金鑰鐵律違規。只准 `{{auth.X}}` 名字引用
2. **method 用猜的** → 查官方文件。實錄：Sheets append 被猜成 PUT（官方是 POST `:append`），
   seed 到壞 recipe 每個新用戶都打 400
3. **只建 recipe 忘了 auth recipe** → `{{auth.X}}` 注入空值打不通。實錄：telegram auth recipe
   漏進種子，所有新實例的 telegram 發訊全斷
4. **落空就寫 code 節點** → 腹語術。recipe 是正路，且寫好可投稿讓全生態重用
5. **canonical_id 用大寫／空白** → 一律小寫底線（系統會 trim+lowercase，但別靠它救）

## 7. 相關

- 寫意圖工作流（上游，先寫意圖再查缺什麼）：`arcrun_get_skill('write_intent_workflow')`
- 缺的是計算零件不是 API：`arcrun_get_skill('add_new_wasm_component')`
- 查公庫現貨：`acr recipe search <關鍵字>` ／ `arcrun_recipe_search(...)`
- 拉別人寫好的：`acr recipe pull <canonical_id> [--author=<name>]`
