From 8ab6f8a66b5f6d8553fe3bdbf058aa7611c52976 Mon Sep 17 00:00:00 2001 From: richblack Date: Thu, 14 May 2026 14:54:26 +0800 Subject: [PATCH] fix(cypher): interpolateString single-ref array/object pass-through (P0 #11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mira_feed_watcher 第一輪 cron tick 跑 264ms 完成但 0 raw 處理 — 挖到 root cause: interpolateString 看到模板就 string.replace,非 string 值(如 kbdb_get 回的 blocks 陣列)一律 JSON.stringify。所以 `items: "{{list_raws.blocks}}"` 把 陣列轉成字串給 filter 零件,filter 收到字串 != array → items 被忽略 → FOREACH 跑 0 次 → watcher 看似成功實則空跑。 修:interpolateString 加 single-ref pass-through —— 若整個值是純單一 `{{x}}` 引用,回 raw value(保留 array / object 型別)。多 ref / 混合文字仍 stringify。 對應 SDD: arcrun.md 三-A P0 #11。 下一輪 cron tick 應該真正處理 raws,加 wiki-processed tag。 --- .agents/specs/arcrun/arcrun.md | 12 ++++++++++++ cypher-executor/src/graph-executor.ts | 12 +++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.agents/specs/arcrun/arcrun.md b/.agents/specs/arcrun/arcrun.md index cd3964f..56a91a2 100644 --- a/.agents/specs/arcrun/arcrun.md +++ b/.agents/specs/arcrun/arcrun.md @@ -233,6 +233,18 @@ acr push 就會自動建立 cron-idx 並開始定時觸發。 --- +#### 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 | diff --git a/cypher-executor/src/graph-executor.ts b/cypher-executor/src/graph-executor.ts index 7ffc66e..0f6c51c 100644 --- a/cypher-executor/src/graph-executor.ts +++ b/cypher-executor/src/graph-executor.ts @@ -526,8 +526,18 @@ function propagateCtx( * 支援 array index:{{paragraphs.0.entity}} → ctx.paragraphs[0].entity * 非 string 值(object/array)遞迴展開內部 string;undefined / null / number / bool 保留原值 * 2026-05-13 加遞迴:原本只跑 top-level,set 零件 values 嵌套 / kbdb_create_block content 內含 {{x.y}} 用不了。 + * 2026-05-14 加 single-ref pass-through:若整個 string 是 `{{x}}` 且 x 是 array / object, + * 回 raw value 不 stringify(讓 filter `items: "{{list.blocks}}"` 能拿到真陣列)。 + * 多 ref 或混合文字仍 stringify 為字串。 */ -function interpolateString(s: string, ctx: Record): string { +function interpolateString(s: string, ctx: Record): unknown { + // 整個值是單一 {{x}} 引用 → 回 raw value(保留 array / object 型別) + const single = s.match(/^\s*\{\{([\w.]+)\}\}\s*$/); + if (single) { + const val = getNestedValue(ctx, single[1]); + return val === undefined ? s : val; + } + // 多 ref / 混合文字 → 一律拼成 string return s.replace(/\{\{([\w.]+)\}\}/g, (_, key: string) => { const val = getNestedValue(ctx, key); if (val === undefined) return `{{${key}}}`;