Files
Arcrun/system-dev/docs/5-records/incidents/2026-05-13-chain-ctx-propagation.md
T
uncle6me-web 5d00e71275 chore: D22 落地——docs/SDD/wiki/CLAUDE.md 進 repo(Gitea private 預設全 push)
頂層 D22 決策(leo 2026-07-03 拍板):推什麼由開發環境歸屬決定,
Gitea private=除機敏值/build 產物/.github 外全 push。
解 T1.5 卡點:雲端工人 clone 拿得到 credential-store-migration.md,可就地改寫 SDD。
機敏掃描兩輪通過(新增 189 檔約 2.1MB,node_modules/dist/wasm 照舊排除)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 07:13:33 +08:00

4.2 KiB
Raw Blame History

2026-05-13 cypher-executor multi-node chain context propagation 漏失

總耗時:約 20 分鐘 debug(找 P0 #9 過程中順帶找到) 根因4 個 edge type 沒 spread baseCtx 給下游節點,原始 context 從第 2 節點開始消失 修法ON_SUCCESS / ON_FAIL / IF / ON_CLICK 套用 PIPE / FOREACH 同模式 {...baseCtx, ...result} 影響:任何 chain workflow 從第 2 節點開始 interpolate context key 都失敗


症狀

mira acr run wiki_synthesis7 節點 workflow)回 Unauthorized。trace 顯示:

  • load_schema(節點 1)✓ 200input 含 api_key=ak_xxx
  • load_skill(節點 2)✗ 401 Unauthorizedinput 含 api_key="{{api_key}}"模板原文未替換

推測 → 驗證

對照組2 節點 chain 看 input keys

節點 input keys
n1 api_key, b1, b2, block_id ✓ 全 context 在
n2 blocks, count, success, api_key, block_idb1, b2 不見api_key 是原文 "{{api_key}}"

n2 的 ctx 只有 n1 output spread,原始 contextb1, b2, ...)全消失。

根因

graph-executor.ts 在 outEdges 處理時,PIPE 跟 FOREACH 已有 baseCtx merge

// PIPE (line 381-384)
const pipeContext = {
  ...(context as Record<string, unknown>),
  ...baseResult,
};

// FOREACH (line 438-444)
const baseCtx = ...;
const itemContext = {
  ...baseCtx,
  ...(result as Record<string, unknown>),
  [iteratorKey]: item,
};

但 ON_SUCCESS / ON_FAIL / IF / ON_CLICK 直接傳 result

case 'ON_SUCCESS':
  result = await this.executeNode(nextNode, graph, result, ...);  // ← bug

result 是上游節點的 output沒有原始 context。下游 interpolate 找不到原始 key 就原文留下。

歷史脈絡

2026-05-07 commit e8fca33"FOREACH preserves outer context")已意識到問題並修了 FOREACH。但沒同步處理另外 4 個 edge type。今天才被 mira 7 節點 workflow 踩到。

→ 教訓:架構級修法要全 edge type 一致掃過,不只修當下踩到的。

修法

cypher-executor/src/graph-executor.ts 4 個 edge case 補:

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

// 改後
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, ...);

套用在 line 407 (ON_SUCCESS) / 415 (ON_FAIL) / 423 (IF) / 472 (ON_CLICK)。

驗證

acr run wiki_synthesis 7 節點 workflow 端對端跑通:

節點 修前 input 修後 input
n2 (load_skill) api_key={{api_key}} api_key=ak_xxx
n7 (emit_result) 上游 spread only baseCtx + 各上游 spread ✓

整條 16.2 秒(含 claude_api 真實 Claude 呼叫),結果 {success: true, data: {...}}

Known Limitation

interpolateData() 只展開 node.data top-level string values不遞迴 nested object

# emit_result 的 values 內 {{...}} 不會展開
emit_result:
  component: set
  values:
    text: "{{classify.data.text}}"   # ← 原文傳,不展開

非阻擋 P1SDD 待開 interpolate-nested-config。當前 workaround:直接看上游節點的 trace output。

未來怎麼避免

  1. 新 edge type 加進來時必須走 baseCtx merge 模式——可以抽出 helper mergeCtxForDownstream(context, result) 強制所有 caller 用,避免漏
  2. interpolation 邊界要有測試:寫一個 2 節點 chain 用 {{api_key}} 引用原始 context 的 e2e testCI 跑過
  3. trace output 要顯示 input —— 沒看 trace 內 input 欄位很難看出 interpolation 失敗(目前 acr CLI 不顯示 input,只顯示 result)。CLI 應加 --verbose 顯示每節點 input

Reference

  • 對應 SDDmatrix/arcrun/.agents/specs/arcrun/arcrun.md P0 #10
  • 相關 commite8fca33 2026-05-07FOREACH 同類修法)
  • 受影響 SDDpolaris/mira/.agents/specs/mira-app/tasks.md 7B.3c
  • 同日另一個 incident2026-05-13-cypher-outbound-522.md