/cypher/search 說零件不存在,但那顆零件在同一台實例上跑得動——害人誤判成「缺零件」 #88
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
發現者:arcrun-rag 出貨線(
Leo/arcrun-rag#77)。這個不一致直接害我做出三次錯誤結論,所以先開它。現象(leo21c 實測 2026-08-11)
POST /cypher/search對這些名字全回not_found:if_control、switch、condition、branch、assert_equals、compare、json_path、data_transform、validate、filter——連
http_request也回not_found。但同一台實例上:
http_request跑,跑得好好的if_control的探測工作流,它正常運作:⇒ 搜尋索引與執行期不一致:搜尋說沒有,執行期有。
為什麼這個要優先修
/cypher/search的not_found回應會附一句建議:這句話在這個情況下是錯的指引,而且它很有說服力。我照著它做出的錯誤結論:
ship_check_live裡的code節點辯護說「這台沒有比對零件可用」——那是替 D70 的腹語術找到了一個假藉口⇒ 一個會主動建議你去自幹的錯誤回應,比單純的「查不到」危險得多。
建議的驗收
/cypher/search查得到的集合 ⊇ 執行期真的載得動的零件not_found的訊息不要直接建議投稿,先提示「這台實例的索引可能沒同步,請先實測」真正的病因(不是「registry 少幾筆資料」,是兩個系統各查各的)
/cypher/search(cypher-executor/src/actions/search-nodes.ts)只查 component registry(SUBMISSIONS_KV,經submitComponent/index-only才會有記錄)。但
if_control/http_request/switch/filter/code…這一整批零件,是cypher-executor/src/lib/component-loader.ts在執行期直接解析的(BUILTIN_COMPONENTS/LOGIC_BINDING_MAP/WASM_HTTP_RUNNER_IDS,見檔案 34-54 行既有的 TODO 自承「白名單寫死」)——這條路徑從不查 registry,也從來沒被submit過(它們是 cypher-executor 自帶的,不是投稿存量)。實測 leo21c 這台實例(
https://arcrun-registry.leo21c.workers.dev):registry 的
/components/catalog路由(registry/src/routes/query.ts)在目錄空時本該回200 {success:true,data:{components:[],count:0}}——但這台部署的 registry 版本沒有這條路由,請求落到/:id被當成id="catalog",回 404。search-nodes.ts的fetchCatalog()把 404 解讀成「舊版 registry 沒有/catalog端點」→ 退回逐顆查legacyPerNodeLookup→ 每顆都 404 → 誠實地回「兩庫都查過沒有」。這個「誠實」建立在錯的前提上:這批零件本來就不靠 registry 存在,查不查得到都不該用它來判斷有沒有。(另外查到一個相關但不同的事:
arcrun_search_componentsMCP 工具裡的註解寫「registry 唯一寫入觸發點隨.github/workflows移除而蒸發」——這句話不準;system-dev/wiki/status-archive-2026-08.md07-30 的稽核記錄寫著deploy.yml註冊步驟 grep=0,從來沒自動化過,不是「移除」。這點只是路過發現,不影響本次修法,附上供之後清理註解參考。)改了什麼
不投資 registry 的 backfill(
decisions-summary.mdD29 已定調SUBMISSIONS_KV併入「KV 退休戰」,不宜再加),也不掃registry/components/*目錄當清單來源(那含已標記待刪的死碼km_writer/kbdb_upsert_block,07-30 就是因為掃目錄誤灌進 registry 才出的錯)。改成:把 component-loader.ts 本來就有的「執行期真正拿去 resolve 的白名單」(
BUILTIN_COMPONENTS∪LOGIC_BINDING_MAP∪WASM_HTTP_RUNNER_IDS∪trigger_workflow)匯出成RUNTIME_NATIVE_COMPONENT_IDS,search-nodes.ts在查 registry 之前先比對這份清單,命中就直接found(source:'builtin'),不受 registry 可達性/是否已 backfill 影響。這不是新造一份清單,是把既有唯一真相源(執行期 resolver 自己的白名單)重用出去,避免 search 端另建一份會漂移的複本。真正不存在的名字(不在白名單、也不在 registry)維持原本的not_found/unknown誠實回應,分型建議與相似候選機制沒動。檔案:
cypher-executor/src/lib/component-loader.ts(新增export const RUNTIME_NATIVE_COMPONENT_IDS)cypher-executor/src/actions/search-nodes.ts(查 registry 前先比對這份清單)cypher-executor/tests/search-nodes-runtime-native.test.ts(新增,7 case)實測(目錄怎麼回答 vs 執行期真的動不動,修前/修後各一份)
修前(leo21c 實例,2026-08-11 現場實測,即本票原始症狀):
⇒ 查目錄說沒有,實際零件在跑,正是本票標題描述的落差。
修後(本地 vitest,複現與 leo21c 完全相同的兩種 registry 狀態:連不到/連得到但目錄空):
全 repo 迴歸(
npx vitest run):357 pass(修前 350 pass,多出的 7 個就是新測試);既有 14 個失敗筆數與內容跟修前完全一樣(跟本次改動無關的既有缺陷:
/portal404、executor.test.ts一則錯誤訊息斷言字串不符——都在 stash 前後兩次全跑比對過,非本次引入)。
tsc --noEmit也做了同樣的修前/修後比對:7 個既有型別錯誤(portal-data.ts/portal.ts/types.ts)修前修後一模一樣,我改的兩個檔零新增錯誤。還缺什麼(誠實标 ◐,不是 ✅)
沒有部署到線上——
.claude/hooks/main-and-prod-push-guard.sh擋下了我推分支的動作(本 repo這支 guard 連 push 自己的 feature branch 都要求交回總管確認,不是只擋 main),
所以這份修法目前只在本機分支
fix/cypher-search-runtime-native-88(commit525faaf),還沒有 leo21c 實例上「修後」的即時 curl 對照——上面「修後」那份是本地 vitest 精準複現
leo21c 當下兩種 registry 狀態(連不到/連得到但空)得出的等價證明,不是同一台實例的
直接複驗。照 CP 三態判準,這裡誠實標 ◐ 半通:code 端到端測過、根因抓對、
分支已備妥可審——缺的是「推上分支+部署到 leo21c 這台實例+重跑一次原始那三行 curl」
這最後一步,需要總管過目後推分支,或 leo 手動在 leo21c 側
acr update部署這顆 worker才能補齊。
附帶但不在本票範圍內的發現(供另案參考,這次沒有動)
registry 這台實例的
/components/catalog本身也該修一個小地方:目錄空時該回200 {components:[],count:0},不該回 404——現在的 404 讓fetchCatalog()誤判成「舊版沒這端點」而不是「有這端點、只是空的」,兩種狀態目前混在一起判斷(
no_endpoint分支)。這不影響本次修法(本次修法讓執行期原生零件完全繞過這段判斷),但如果之後
真的要走 registry backfill(把社群投稿零件也接上 search),這個 404-vs-空陣列的混淆
建議先理清楚,不然「registry 剛裝好、還沒收到任何投稿」跟「registry 版本太舊」會
一直被當成同一種狀況處理。
2026-08-12 上午(總管):併進 main 了,但還沒推上 Gitea
逐筆審過+跑過測試,已合併到
matrix/arcrun的 本機 main:8cee9c9merge #88(cypher-executor:357 pass/14 fail,與 main 的 14 個既有失敗完全相同)3eb8b31merge #85(kbdb:196 → 197 pass/0 fail)8e10f1d重編.worker-builds官方成品——這一筆很重要:在它之前,成品記的來源commit 比源碼舊(cypher
797e7f7vs 525faaf、kbdba7e23bavs 3eb8b31),也就是「修好了但執行檔還是舊的」。沒有任何閘會講這件事 ⇒ 已開
Arcrun#93。🔴 卡住的地方:推
main到 Gitea 被本次 session 的權限閘擋下(造不出總管戳記)。這件事本身是總管的權限(leo 08-10:「是否可以推 gitea main 由你來決定」),
所以不是規則上的人閘,是這台機器上的權限設定。
⇒ 在推上去之前,leo21c 拿不到這兩張票的修法——
acr update抓的是git.uncle6.me/api/v1/repos/Leo/Arcrun/archive/main.tar.gz(
cli/src/lib/deploy.ts:95、cli/src/commands/update.ts寫死 ref='main'),不是任何 release。✅ 更正:上一則「還沒推上 Gitea」已經過時——leo 不用做任何事(2026-08-13 複驗)
那則(08-12 01:53)同時貼在本票與
Leo/Arcrun#85,內容一樣。總管今天逐筆驗過:⇒ 當時卡住的權限問題已解,
leo21c那條路拿得到這張票的修法了。🔴 這張票暴露的流程錯(leo 2026-08-13 點破,比技術內容重要)
當時的狀況是:測試過了、審過了、只差推——而推被那個 session 的權限設定擋住。
那正是「叫 leo 做一個動作」的時機。但總管做的是:
s/stage⇒ 他的看板上「等我」那欄是空的⇒ 三個地方,沒有一個是他看得到的。 那件事就在票上躺了一天。
📌 判準(2026-08-13 立):
凡是真的要 leo 做事,當下就把標籤改成
s/stage,而且寫清楚「他要做的那一個動作」。「測試過了、只差推」=該叫他,不是等下一個 session 自己發現。
(署名:總管)