# arcrun — 進度與待辦

> 設計細節見 `arcrun/README.md`（產品說明）和 `arcrun/BETA_TEST.md`（封測指南）。
> 這份文件只記錄：目前狀態、還差什麼、封測能不能啟動。

---

## 一、封測目標場景

封測者是工程師朋友，有自己的網頁，需要後端自動化。目標是他能在 AI 協助下，一次或很少次完成以下完整流程：

1. `acr init` 取得 api_key
2. `acr parts scaffold` 查零件格式，AI 幫寫 workflow YAML
3. 若內建零件不足，`acr recipe push` 增加打外部 API 的 recipe
4. `acr creds push` 上傳 OAuth token（gmail / google_sheets 等）
5. `acr push` 部署 workflow，取得 Webhook URL
6. 網頁 POST /webhooks/named/{name}/trigger，結果存 Google Sheets

---

## 二、場景各步驟驗證狀態

### Step 1：acr init → api_key
- [x] `acr init` Standard 模式完成，api_key 存入 `~/.arcrun/config.yaml`
- [x] 已驗證：`mode: standard, api_key: ak_...` 正確

### Step 2：acr parts scaffold → AI 看到零件格式
- [x] `acr parts` 列出 21 個零件，完全內建，不依賴 registry.arcrun.dev
- [x] `acr parts scaffold google_sheets` 輸出 spreadsheet_id / range / operation / values 格式與 credentials.yaml 範本
- [x] 已驗證：輸出可直接貼入 YAML

### Step 3：acr recipe push → 打外部 API
- [x] `acr recipe push` 上傳成功，回傳 rec_hash
- [x] workflow 使用 `component: rec_xxxxxxxx`，acr push 後 trigger 能正確呼叫外部 API
- [x] 已驗證（2026-04-18）：httpbin_post recipe → trigger → httpbin.org/post 回傳正確 ✅

### Step 4：acr creds push → 自動注入 token
- [x] `POST /credentials` API 完成，以 `{api_key}:cred:{name}` 存入 KV
- [x] Webhook trigger 時 injectCredentials 從 KV 取得 token 自動注入
- [x] `/register` 現在回傳 `encryption_key`，`acr init` 自動存入 config
- [x] `acr creds push` 從 config 讀 encryption_key，不再需要手動設定環境變數
- [x] 已驗證（2026-04-18）：beta@arcrun.dev 帳號完整流程：init → creds push → trigger → credential 注入成功 ✅

### Step 5：acr push → Webhook URL
- [x] `acr push workflow.yaml` 部署成功，顯示 Webhook URL 和完整 curl 範例
- [x] config 中的 `component` / 參數在 push 時套入 graph 節點
- [x] 已驗證（2026-04-18）：sheet-test workflow push 成功 ✅

### Step 6：網頁 POST → 執行 → 結果到 Google Sheets
- [x] `POST /webhooks/named/{name}/trigger -H 'X-Arcrun-API-Key: ...'` 觸發執行正常
- [x] google_sheets 零件有實作（append row 到 Sheets API）
- [x] 已驗證（2026-04-18）：trigger sheet-test → 報「缺少 credential」（符合預期，credential 未上傳）✅
- [ ] **未驗證**：真實 google_oauth token + acr creds push → trigger → Google Sheets 實際寫入
  - 需要真實 OAuth token 才能完整驗證

---

## 三、封測啟動阻擋項

P0 全部清除才啟動封測。

| # | 項目 | 狀態 | 說明 |
|---|------|------|------|
| 1 | acr parts scaffold 正確輸出 | ✅ 完成 | 21 個零件內建清單 |
| 2 | acr recipe push 端對端 | ✅ 完成 | httpbin_post 驗證通過 |
| 3 | acr creds push 代碼 | ✅ 完成 | 值交 CF Workers Secrets 託管 |
| 4 | credential 注入端對端 | ✅ 完成 | 無 token 時錯誤訊息正確 |
| 5 | acr push + webhook trigger | ✅ 完成 | 端對端驗證通過 |
| 6 | acr creds push 實測 | ✅ 完成 | acr init 自動存入 config（CLI 1.0.9）|
| 7 | Google Sheets 真實寫入 | ⚠️ 部分驗證 | credential 注入已驗證；實際 Sheets 寫入需真實 OAuth token |
| 8 | 第三方服務認證 recipe | ✅ 完成 | 20 個服務（Notion/Slack/GitHub/OpenAI 等），CLI 1.1.0 |
| **9** | **cypher-executor outbound HTTP fetch 全失效** | ✅ **已解決 2026-05-13**（CF 同 zone 自循環死鎖，改走 workers.dev）| 詳見下方專段 |
| **10** | **multi-node chain context propagation 漏失** | ✅ **已解決 2026-05-13**（ON_SUCCESS/ON_FAIL/IF/ON_CLICK 沒 spread baseCtx）| 詳見下方專段 |

**目前狀況**：P0 全部解決。
- #9 修復方式：component worker URL 從 `*.arcrun.dev`（同 cypher zone）改走 `arcrun-{name}.{WORKER_SUBDOMAIN}.workers.dev`（避開同 zone 自循環）
- #10 修復方式：4 個 edge type 補 `{...baseCtx, ...result}`，跟 PIPE/FOREACH 一致

兩個 P0 解完 mira 7 節點 workflow 端對端通（含真 Claude 16 秒呼叫）。

---

### ✅ P0 #9（2026-05-13 已解決）：cypher-executor outbound fetch 全失效

**完整事件報告（含誤判路徑）**：[docs/incidents/2026-05-13-cypher-outbound-522.md](../../../docs/incidents/2026-05-13-cypher-outbound-522.md)

**修復方式**：cypher-executor fetch component worker 從 `*.arcrun.dev`（同 zone）改走 `arcrun-{name}.{WORKER_SUBDOMAIN}.workers.dev`。對外 `cypher.arcrun.dev` 不變，用戶 0 感知。

**改動檔案**（2026-05-13）：
- `cypher-executor/src/lib/component-loader.ts`：`wasmWorkerUrl(canonicalId, subdomain)` 簽名加 subdomain 參數 + URL pattern 改 workers.dev
- `cypher-executor/src/actions/auth-dispatcher.ts`：同步新簽名
- `cypher-executor/src/types.ts`：`Bindings` 加 `WORKER_SUBDOMAIN: string`
- `cypher-executor/wrangler.toml`：`[vars]` 加 `WORKER_SUBDOMAIN = "uncle6-me"`
- 5 個 component worker 在 dashboard 啟用 workers.dev URL（kbdb-get / kbdb-ingest / kbdb-create-block / kbdb-patch-block / claude-api，**未來新 component 也都要開**）

**驗證**：cypher-executor → kbdb-get / claude-api 從 522 → 200。mira `acr run wiki_synthesis` 5 節點 workflow 跑通前 3 節點（kbdb_get chain）。

**Self-hosted fork 注意**：必須改 `wrangler.toml [vars] WORKER_SUBDOMAIN` 為自己的 CF 帳號 subdomain，並把所有 component worker 在 dashboard 啟用 workers.dev URL。

---

### ✅ P0 #10（2026-05-13 已解決）：multi-node chain context propagation 漏失

**現象**：cypher binding workflow 從第 2 個節點開始，原始 input context（top-level `api_key` / `mira_token` 等）丟失，下游節點 `{{api_key}}` 模板原文未替換傳給零件 → 401 Unauthorized 或類似錯。

**測試重現**：

```yaml
flow:
  - "input >> ON_SUCCESS >> n1"
  - "n1 >> ON_SUCCESS >> n2"
config:
  n1: { component: kbdb_get, api_key: "{{api_key}}", block_id: "{{b1}}" }
  n2: { component: kbdb_get, api_key: "{{api_key}}", block_id: "{{b2}}" }
context: { api_key: "ak_xxx", b1: "...", b2: "..." }
```

n1 收到 ctx 含 `api_key / b1 / b2` ✓ → 跑通。
n2 收到的 ctx 只有 `n1.output spread`（blocks/count/success/block_id），**`api_key / b1 / b2` 不見**，`{{api_key}}` 原文傳到零件回 401。

**根因**：`graph-executor.ts` 在 PIPE / FOREACH 邊類型已修「baseCtx ∪ result」，但 **ON_SUCCESS / ON_FAIL / IF / ON_CLICK 四個 edge type 沒套同模式**，直接把 `result` 當下游 ctx 傳，丟掉原始 context。

**修法**（`cypher-executor/src/graph-executor.ts` line 407 / 415 / 423 / 472）：

```typescript
// 改前
result = await this.executeNode(nextNode, graph, result, ...);

// 改後（同 PIPE/FOREACH 模式）
const baseCtx = (typeof context === 'object' && context !== null) ? context as Record<string, unknown> : {};
const baseResult = (typeof result === 'object' && result !== null) ? result as Record<string, unknown> : {};
const mergedCtx = { ...baseCtx, ...baseResult };
result = await this.executeNode(nextNode, graph, mergedCtx, ...);
```

**驗證**：mira `acr run wiki_synthesis` 7 節點 workflow 端對端跑通（16 秒，含真 Claude 呼叫）。每個節點都拿到正確 `api_key` 不再 401。

**歷史脈絡**：類似問題 2026-05-07 commit e8fca33 在 FOREACH edge 已修一次（"FOREACH preserves outer context"），但當時沒同步處理另外 4 個 edge type。本次補完。

---

### ✅ P0 #10 補完三個衍生問題（2026-05-13 晚 ~ 2026-05-14）

P0 #10 修完後 mira 嘗試做 wiki 多段結構，又踩出三個 cypher binding 設計缺陷。**都是同一天解掉**。

#### A. interpolateData() 不遞迴 nested object

**現象**：`set` / `kbdb_create_block` 的 `values: { text: "{{classify.data.text}}" }`、`tags_json: ["facet:{{paragraph.facet}}"]` 等 nested config 內的 `{{x}}` 不被替換，原文傳給零件。

**根因**：`interpolateData()` 只 iterate top-level，對非 string 值（object / array）直接 pass-through 不下沉。

**修法**：拆 `interpolateString` + `interpolateValue`（遞迴 object / array），`interpolateData` 改 call `interpolateValue`。

**測試**：`set values: { text: "hello {{name}}", arr: ["item {{name}}"] }` 帶 `name=world` → 全展開。

#### B. ctx 沒存上游 output 的 node id namespace

**現象**：`{{classify.data.text}}` 找不到上游 classify 的 output；只能用 `{{data.text}}`（直接 spread 取），但會被下個節點覆蓋，多節點 chain 用不了。

**根因**：`propagateCtx` 只把上游 result spread 進 ctx，沒額外存 `[node.id]: result`。

**修法**：`propagateCtx` 改回傳 `{ ...baseCtx, ...baseResult, [upstreamNodeId]: upstreamResult }`。讓下游能用 `{{node_id.data.text}}` 從 namespace 取，永不被覆蓋。

**測試**：5 節點 chain 用 `{{load_schema.blocks.0.content}}` / `{{classify.data.text}}` 全展開。

#### C. FOREACH 找 iterable 只看 result，不看 ctx + 不看 nested

**現象**：mira wiki_synthesis 雙重 FOREACH（外層 `對每個 paragraph`、內層 `對每個 triplet`），外層 OK，內層跑 0 次。

**根因 (C1)**：`getIterableFromContext(result, key)` 只看當前節點 output。`result` 是 `create_paragraph` output（`{data, success}`），不含 paragraphs。但 `paragraphs` 早就在 ctx 從 classify spread 來。

**根因 (C2)**：當外層 FOREACH 把 `paragraph` item 注入 ctx，內層 FOREACH 要找 `paragraph.triplets`。`getIterableFromContext` 只看 top-level，看不到 `paragraph` 物件裡的 `triplets`。

**修法**：
- (C1) FOREACH `result` 找不到 iterable → fallback 找 `context`
- (C2) `getIterableFromContext` 加一輪「掃 ctx 內每個 object 找 nested key」

**測試**：mira wiki_synthesis 3 層樹（wiki-page → paragraphs → triplets）端對端跑通，KBDB 內驗證 `物理 AI` wiki 有 2 段 paragraph + 4 個 triplet，parent_id 正確接到對應 paragraph。

#### Edge type 一致化

抽 `propagateCtx(context, result, upstreamNodeId)` helper，5 個 edge type（PIPE / ON_SUCCESS / ON_FAIL / IF / ON_CLICK / FOREACH）全部用同一 function 組下游 ctx。**未來新 edge type 必須用這 helper**，避免再漏。

#### CLI validator 同步

`cli/src/lib/yaml-parser.ts` validateRelations 加 regex 支援 `對每個 X` / `FOREACH X` 迭代器命名（之前 validator 字串完全比對擋住，但 graph-builder 執行端早已支援）。

---

### 三-A、P1 待改進（不擋封測，但 mira 已踩到）

#### ✅ P1 #3：cypher-executor `scheduled()` handler（2026-05-14 完成）

**原痛點**：cron 零件只做 expression validation；cypher-executor 沒 `scheduled()` handler。寫了 cron 首節點的 workflow 不會真的跑。

**之前的 workaround**（已撤）：mira 寫了個 `/mira/wiki-from-raw` route 從前端 fire-and-forget 觸發 wiki_synthesis。但這違反「一律 arcrun-native」原則，也讓 arcrun 永遠補不齊缺失。**已刪 route，回 arcrun-native 路線**。

**落地**：
1. `wrangler.toml`：`[triggers] crons = ["* * * * *"]`（每分鐘 tick）
2. `src/lib/cron-match.ts`：5 欄位 cron expression matcher（支援 `*` / `N` / `*/N` / `1-5` / `5,10` 組合）
3. `src/scheduled.ts`：scheduled handler 掃 KV `cron-idx:` prefix，比對 controller.scheduledTime，匹配就 `executeWebhookGraph` 背景跑
4. `routes/webhooks-named.ts`：acr push 偵測首節點是 cron 零件 → 抽 `cron_expr` 存進 record + 額外寫 `cron-idx:{api_key}:{name}` 輕量 index entry。DELETE 一併清理
5. `src/index.ts`：export default 改 `{ fetch, scheduled }`
6. cypher-executor 自己加 `workers_dev = true` 給未來 self-trigger 用（fork 用 path-based 子 trigger 也走 workers.dev 避同 zone）

**workflow YAML 慣例**：
```yaml
flow:
  - "my_cron >> ON_SUCCESS >> downstream_node"
config:
  my_cron:
    component: cron
    cron_expr: "*/5 * * * *"   # 每 5 分鐘
```
acr push 就會自動建立 cron-idx 並開始定時觸發。

**測試**：`tests/arcrun-test/cron_heartbeat.yaml` — 每分鐘 fire 一次 + set 節點 log。
`wrangler tail arcrun-cypher-executor` 應看 `[scheduled] trigger cron_heartbeat ...`。

**對應 use case**：mira `mira_feed_watcher`（7B.3h，下一輪做）/ RSS 每日抓 / voice-stt 每小時掃 / 等所有 cron-driven source。

---

#### P1 #1：workflow 缺 IF/branch 能力（2026-05-14 mira 7B.3f 提出）

**現象**：mira 想做「找有則 PATCH 沒則 CREATE」（index-entry upsert），arcrun 目前只有 `ON_SUCCESS` + `對每個 X`（FOREACH）+ 已存在但壞掉的 `if_control`（見已知限制 #1），沒有 `>> ON_TRUE >>` / `>> ON_FALSE >>` 條件路由。

**短期 workaround**（已採用，2026-05-14）：建 `kbdb_upsert_block` 零件，把分支邏輯封進零件內部（GET by page_name → 找到 PATCH 沒找到 POST）。caller 看到的是單純的 upsert 介面。

**長期解**：升 `if_control` false branch 路由 / 加 `>> ON_TRUE >>` edge type，讓 workflow 層可表達分支。對未來所有「找則改否則建」/「條件分流」場景都會撞到，不只 mira。

**位置**：cypher-executor/src/graph-executor.ts edge type 處理（5 個 edge type 抽出 `propagateCtx` 後新增 IF 應該不難）+ cli/src/lib/yaml-parser.ts validator。

---

#### P0 #11（2026-05-14 已解決）：interpolateString stringify array 撐爆下游

**現象**：mira_feed_watcher 用 `items: "{{list_raws.blocks}}"` 把 kbdb_get 拿到的 blocks 陣列傳給 filter 零件。watcher 跑 264ms 完成、0 raw 處理。

**根因**：`interpolateString` 看到模板就用 `String.replace`，非 string 值（陣列）一律 `JSON.stringify`。filter 零件收到字串 `"[{...},{...}]"` 不是 array，items 被忽略 → 0 matches → FOREACH 跑 0 次。

**修法**：`interpolateString` 加 single-ref pass-through 規則：若整個值是純單一 `{{x}}` 引用，回 raw value（保留 array / object 型別）。多 ref / 混合文字仍 stringify 拼接字串。

**測試**：mira_feed_watcher 推到 prod 後下一個 cron tick 觀察 wiki-processed tag 是否在 raws 上出現。

---

### 三-B、新零件加入紀錄

| 日期 | 零件 | 動機 | 對應 SDD |
|---|---|---|---|
| 2026-05-14 | `kbdb_upsert_block` | mira 7B.3f index-entry per-entity upsert，繞過 workflow 缺 IF/branch 能力（P1 #1）。內部 GET by page_name → 找到 PATCH 沒找到 POST。page_name 當 idempotency key。 | polaris/mira/.agents/specs/mira-app/design.md §3.5.12.4.1 |

**新零件 checklist（避免 P1 #1 重蹈 kbdb_upsert_block 漏白名單覆轍）**：

每加一個 API/data 零件（不是 logic primitive），都要：
- [ ] `registry/components/{name}/main.go` + `component.contract.yaml` + `go.mod`
- [ ] `tinygo build -target=wasi` 通
- [ ] `.component-builds/{name}/` 完整 4 檔（`wrangler.toml` 含 `workers_dev = true` + `pnpm-lock.yaml` + `tsconfig.json` + `src/index.ts`）
- [ ] **`cypher-executor/src/lib/component-loader.ts` 的 `WASM_HTTP_RUNNER_IDS` 加 canonical_id**（漏這條 cypher-executor 永遠拋「找不到零件」，端對端會死靜悄悄）
- [ ] `acr validate workflow.yaml` 通
- [ ] 直接 curl `https://{kebab}.arcrun.dev` + `https://arcrun-{kebab}.{WORKER_SUBDOMAIN}.workers.dev` 都 200

---

### 三-C、P1 #2：workers_dev = true 全 component 自動化（2026-05-14 已收）

**原痛點**：每新部署一個 component worker，要去 CF Dashboard 手動 Enable workers.dev URL，否則 cypher-executor fetch 該 worker 會 404。

**解**：32 個 `.component-builds/*/wrangler.toml` 全部加 `workers_dev = true`。CI 每次 deploy 自動啟用對應 workers.dev URL，零手動。

**未來新 component**：模板 (`component-worker-template/`) 應該預設帶 `workers_dev = true`，新人 fork 不會踩。已列入「新零件 checklist」第 3 條。

**為何不走 `*.acr-comp.uncle6.me` 自訂 zone**：CF Universal SSL 只發一層子域，sub-sub `*.acr-comp.uncle6.me` 不蓋；要 ACM ($10/月) 才能簽。違反 arcrun「fork 後 self-host 用 free tier 跑得起來」核心目標。workers_dev=true 走 CF 默認的 workers.dev cert，free tier OK，更乾淨。

---

### 原 P0 #9 調查紀錄（保留作歷史參考）

**現象**：cypher-executor 的 `makeHttpRunner` (`cypher-executor/src/lib/component-loader.ts:142`) 對任何 outbound URL fetch 都回 CF **522 (origin timeout, ~1000ms)**。

**測試矩陣**（mira repo `polaris/mira/arcrun/wiki_synthesis.yaml` 端對端壓測時發現）：

| 路徑 | 結果 | 證明 |
|---|---|---|
| 本機 curl → kbdb-get.arcrun.dev | 200 (22ms) | KBDB worker 本身健康 |
| cypher-executor → kbdb-get.arcrun.dev (HTTP) | **522** (1002ms) | outbound HTTP fetch 壞 |
| cypher-executor → claude-api.arcrun.dev (HTTP) | **522** | 同 zone 也壞 |
| cypher-executor → httpbin.org (外部) | **522** | 不只是 same-account loop |
| cypher-executor → string_ops (Service Binding) | 200 ✅ | SVC_* 路徑正常 |
| acr run hello (built-in via SB) | ✅ | hello.yaml 仍跑得通 |

**衍生小 bug**：`set` 零件 input schema 變了（要 `assignments` 陣列或 `values` 物件，不是 `value`）。tests/arcrun-test/hello.yaml 跑 string_ops 沒踩到。

**影響範圍（封測啟動阻擋）**：

- 任何用戶 workflow 含 outbound HTTP 都壞：
  - 用戶 `acr recipe push` 後 trigger → 打外部 API 全 522（推翻 P0 #2 4/18 紀錄）
  - 用戶要存資料進 arcrun 內建 KBDB → 522（mira 7B.3c 卡這裡）
  - 任何 auth primitive 走獨立 Worker URL 路徑 → 522
  - Google Sheets 寫入 → 522（推翻 P0 #7「待真實 OAuth 驗證」評估，根本還沒到 OAuth 步驟就壞）
- 只有「全內建邏輯零件 + 純 service binding」workflow 還能跑

**根因（2026-05-13 確認）：5/8-5/9 9 次 manual `wrangler deploy` 把含 WIP bug 的 cypher-executor 推上 prod**

調查路徑（按時序）：

1. 一開始懷疑 free tier CPU cap（10ms / invocation）→ 對照測試「5 節點 SB chain」跑了 2.2 秒卻通過，**推翻**
2. 換懷疑 CF zone 規則 / Bot Fight Mode → dashboard bindings 乾淨無攔截，**推翻**
3. 用戶補繳費恢復 Workers Paid → 重測仍全 522，**徹底排除付費假設**
4. 看 dashboard Version History：5/8 4 次 + 5/9 5 次 = **9 次 manual `wrangler deploy by uncle6.me`**
5. 對照 GitHub Actions：4/24 後完全沒 deploy（最後是 commit e222116 `fix(wasi-shim)`）
6. 對照 git：本機 main **領先 origin/main 3 commits 未 push**，含：
   - `497f92a feat(arcrun): recipe system + resumable workflow + component registry canon`
   - `e8fca33 feat(cypher): 3-node wiki workflow end-to-end (FOREACH + nested interp + unified parsing)`
   - `519423c feat(arcrun): mira wiki page with tag filter + accumulated WIP`（自描含 `cypher-executor: auth-dispatcher / wasi-shim adjustments (WIP)`）

**結論**：那 9 次 manual deploy 把上面 3 個 unpushed commit 的 cypher-executor 改動推上 prod，其中至少一個改動破壞了 outbound fetch（最可能是 519423c WIP 內的 wasi-shim / auth-dispatcher 改動）。GitHub Actions 因為沒 push 沒跑，CI 沒 catch，4/18-4/24 那段 SDD「驗證通過」的紀錄是 truth，現在 prod 是壞的版本。

**為何 SB 路徑沒事 / HTTP 路徑全死**：SB 走 cypher-executor 內部 service binding API（`env.SVC_X.fetch()`），不經過 outbound HTTP code path。HTTP 路徑走 `makeHttpRunner` (component-loader.ts:142) 的 `fetch(url, ...)`，這條路被 WIP code 弄壞。具體壞在哪要 diff 那 3 個 commit 的 cypher-executor 改動才知道。

**驗證 wrangler tail 證據**：trigger 任何 outbound HTTP 的 graph，cypher-executor 自己 `wallTime: 497ms, cpuTime: 2ms, outcome: ok`、無 logs、無 exceptions。代表 cypher-executor 把「fetch 失敗的 522 response」當作 component 正常輸出包回 client，自己沒撞任何錯。

**解法（三選一）**：

- **A. Rollback prod 到 4/24 的 e222116** — CF dashboard → arcrun-cypher-executor → Deployments → 找 4/24 那筆 → Rollback。5 分鐘恢復 outbound fetch，丟失 wiki workflow / recipe / resumable 等 cypher 端 WIP 改動（但前端、registry components、KBDB blocks 都不丟，因為它們是別的 worker / 別的儲存）。**richblack 操作。**
- **B. Diff 3 個 unpushed commit 找出壞掉的改動修掉** — 不丟功能，但要動 src code 走 SDD 協議，30min - 數小時。
- **C. 架構切換**（mira 老闆 2026-05-13 提的）：sub-workflow 自殺交棒模式，cypher-executor 不再做集中 graph executor。從根本繞開「cypher-executor 一個 invocation 跑長 graph」這條脆弱路徑。一勞永逸但是大改。

**衍生小 bug 仍要修**（跟付費無關）：`set` 零件 input schema 變了（要 `assignments` 陣列或 `values` 物件，不是 `value`）。要嘛 update set 零件 contract 容錯，要嘛文件化新 schema。

**為什麼這直接擋封測**：

封測場景 Step 6「網頁 POST → 結果存 Google Sheets」走 google_sheets 零件 (HTTP outbound to googleapis.com)。如果 cypher-executor outbound 全壞，**封測者跑任何含外部 API 的 workflow 都會 522**，不是「Google Sheets 實際寫入未驗證」級別的小事。

**也直接擋 mira**：[polaris/mira/.agents/specs/mira-app/tasks.md] 7B.3c-f（wiki 合成 workflow）卡這裡。

---

## 四、封測前 P3（啟動當天）

- [ ] 引導封測者到 arcrun.dev 登入取得 api_key
- [ ] 確認聯絡管道

---

## 五、已知限制（封測期間不修）

1. `if_control` false branch 不路由（條件 false 時後續節點不執行）→ 升級計畫見 P1 #1，2026-05-14 mira 用 `kbdb_upsert_block` workaround
2. 多節點 context 不自動解包（上游輸出 flat merge，下游需從 `data.result` 取值）
3. 用戶自製邏輯零件（Phase 5）封測後才實作

---

## 六、實作進度

| Phase | 內容 | 狀態 |
|-------|------|------|
| 0 | Workers 部署、CI/CD、DNS | ✅ |
| 1 | CLI 基礎（init / validate / run / parts） | ✅ |
| 2 | /register、/cypher/execute、21 個零件 | ✅ |
| 3 | Service Binding 架構、{{variable}} 插值、ON_FAIL 修正 | ✅ |
| 4 | 動態 Recipe KV（CRUD）、acr recipe 指令 | ✅ |
| 5 | 用戶自製邏輯零件（WASM push） | ⏸ 封測後 |
| 6 | Credential 多租戶（{api_key}:cred:{name}）、acr creds push | ✅ |
| 7 | acr parts 內建清單、acr parts scaffold | ✅ |
| 8 | /webhooks/named、acr push 改版、config 套入 graph | ✅ |

### CLI 版本

| 版本 | 變更 |
|------|------|
| 1.1.0 | auth recipe 系統：20 個服務預建（Notion/Slack/GitHub/OpenAI/Google SA 等）；acr auth-recipe 指令 |
| 1.0.9 | /register 回傳 encryption_key；acr init 自動儲存；creds push 不需手動設環境變數 |
| 1.0.8 | acr push → webhooks/named；config 套入 graph；acr parts 內建清單 |
| 1.0.7 | acr creds push → POST /credentials |
| 1.0.6 | acr recipe push / list / delete |
| 1.0.5 | hello.yaml 改 string_ops，--version 修正 |
| 1.0.4 | config/context 分離 |
| 1.0.3 | 初始發布 |
