Arcrun#10 裁定:一次性解析走通用逃生口,不再鑄 domain 零件。 - workflow.yaml:parse_card 由 component:km_wiki_card_parse -> component:code, config.code 內聯 card-to-envelope 的 planCard 邏輯(去 import/export、raw NUL 分隔符改 u0000 escape、改用 code 沙箱注入的 sha256);下游 refs 改 parse_card.data.*; 加 limits(timeout_ms/max_output_bytes)。已端到端驗證(YAML 解析->JS eval)與原 planCard 輸出逐欄全等(含 content_hash)。 - 刪 registry/examples/km-wiki-ingest/component-contract.yaml(km_wiki_card_parse 契約)。 - lib/card-to-envelope.mjs:header 改述為「code 節點內聯 JS 的權威來源 + 參考實作」, 邏輯不變(續為 inline JS 之單一真相源)。 - lib/dry-run.mjs / description.md:框架改述為 code-節點形態;部署清單更新為「部署通用 code 零件」。dry-run-evidence.json(3 entries/15 triplets/16 nodes)不變——解析輸出等價。 不部署、不寫 live。留分支可部署狀態。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJiLCRUU2o3aSpPEzVCt2o
7.1 KiB
km-wiki-ingest — 機械式 wiki 卡片 → KBDB ingest(Arcrun#8 / 頂層 SDD T2–T4)
Phase A 產物:機械 ingest 邏輯 + 乾跑證據 + workflow 設計。不部署、不寫 live KBDB。
解決什麼問題
把各 repo 的 system-dev/wiki/cards/**/*.md(人工精耕卡)機械地(無 LLM)灌進 leo21c KBDB:
- 卡片 → base entry(
metadata.embed=true,供語意搜尋)。 ## 實體→ graph node;## 關聯的 typed-edge(A >> 關係 >> B)與[[wikilink]]→ graph triplet。
取代舊 kbdb-ingest-plugin/scripts/ingest-cli.mjs 的 raw → Haiku → 三元組 路:新路純解析卡片內既有結構,決定性、零 token、零幻覺。
形式選擇與理由(給總管)
形式 = Arcrun workflow(YAML 編排)+ 通用 code 零件(sandbox inline JS,Arcrun#10)承載卡片→envelope 解析 + 現成零件(cron / http_request / foreach_control / kbdb_upsert_block)。 不再鑄 domain 零件 km_wiki_card_parse(Arcrun#10 裁定:一次性解析走通用逃生口)。
理由:
- 編排本來就是 arcrun 的主場:cron 限速 drain、Gitea webhook 只吃 delta、foreach 小批、冪等 upsert——這些跟現成零件 1:1 對得上,且 leo 要「Arcrun workflow 慢慢做」、arcrun 哲學禁一次性腳本。
- arcrun 唯一缺的是「卡片 → envelope」的解析。那是一段決定性純轉換(無 LLM、無網路、無檔案)——正好是
code零件 sandbox 的理想形狀(stdin_stdout_json+no_network_syscall+no_filesystem_syscall)。用通用code節點內聯這段 JS(而非鑄 domain 零件、也非在 YAML 裡塞string_ops正則):workflow 可讀、解析可單元測試、且 registry 不因一次性邏輯增生 domain 零件。 - 小批是結構性的,不是靠祈禱:一卡一 tick,每卡在 graph worker 的 fan-out ≈
7+4N+Msubrequest(notes 卡 N≈4/M≈5 → est 28~33,穩壓 CF 50 頂下);code節點內聯解析會預先把超大卡以source_urianchor 分段,任何單一 graph 呼叫都不破頂。
Phase A 交付:純解析+打包核心(lib/card-to-envelope.mjs,現在就能跑,= code 節點內聯 JS 的權威來源)+乾跑驗證器(lib/dry-run.mjs,印出「將寫入什麼」)+本 workflow.yaml(parse_card = code 節點)。解析零件=通用 code(Arcrun#10 分支,已就緒待部署);本 example 不再自帶 domain 零件契約。部署被閘控,故 live 接線是「設計而非執行」。
診斷小結:fan-out 精確來源 + 小批為何解得掉
讀 kbdb-graph-plugin 現役寫入路徑(triplet-ingest.ts / triplet-crud.ts / templates.ts / kbdb-client.ts)逐行拆帳:
POST /triplets/ingest(graph worker 單次 invocation)對 base 的 subrequest:
ensurePluginTemplates(3) # 頂層一次
+ listRecordsByTemplate(1) # 抓同 source 現存 active(冪等分組)
+ Σ_triplet [ createTriplet → ensurePluginTemplates(3) + createRecord(1) ] # ★ 每條邊重跑 ensure!
+ persistNodes [ ensurePluginTemplates(3) + Σ_node createRecord(1) ]
+ Σ_deprecated updateRecord(1)
= 7 + 4*N_triplets + M_nodes + D_deprecated
- 精確炸點還原:07_01 單一 envelope 吞
N=11, M=10, D=0→7+44+10 = 61 > 50→ 破頂半殘。放大器=createTriplet內每條邊都重呼ensurePluginTemplates(3 個 GET),佔了 33/61。 - 小批為何解得掉:把「整檔一 envelope」改成「一卡一 envelope、必要時再 anchor 分段」,把
N壓到讓7+4N+M ≤ 40。notes 三卡實測 est 上限 = 33,全綠。超大卡(自測 20 邊/22 節點=114)→ 自動分 4 段,每段 ≤ 38。 - 附帶建議(非本 Phase 必改):graph 端把
createTriplet/persistNodes內重複的ensurePluginTemplates提到 ingest 入口只跑一次,可把每 envelope 省下3*(N+1)個 subrequest(單卡 est 33→約 18),批量還能更大。此為 graph-plugin 的可選優化,記此存查。
冪等設計
| 對象 | 冪等鍵 | 行為 |
|---|---|---|
| entry | page_name(穩定:wikicard:<repo>/<canonical>)+ metadata.content_hash |
找到同 page_name:hash 相同 → skip;不同 → PATCH content(觸發重嵌)。沒有 → POST 新建。 |
| triplet envelope | source.uri + source.content_hash |
graph 現役 per-source 冪等:同 hash 整包 no-op(triplet-ingest.ts:65)。 |
| 分段 | 各段 source.uri = <基uri>#segNN |
各段獨立 uri → 各自獨立冪等,繞開 per-source content_hash 整包 skip(否則同 uri 第 2 段起會被判定「已落地」而整包跳過)。節點只放進「首次引用它的段」,跨段不重送(避免 graph 重建 entity)。 |
觸發(兩階段,對齊 SDD R3)
- Phase 0(一次性 backfill):
cron */2每 tick drain 一張卡(限速慢推),反覆跑到全庫清空。冪等 → 可續傳、重跑零寫入。 - 穩態(日常增量):Gitea push webhook → arcrun workflow,只吃
commits[].{added,modified}中的system-dev/wiki/cards/**/*.md。⚠️ Gitea → Cloudflare(arcrun),非 GitHub Actions,不觸 GitHub flag 紅線(D4/D20)。量小、不撞頂、不限速。
乾跑證據(Phase A,不寫 live)
node lib/dry-run.mjs --repo-path <notes clone> --repo Leo/notes --self-test
對 Leo/notes 的 3 張卡實測:3 entries(embed=true)+ 3 envelopes、15 triplets、16 nodes;
單次 graph 呼叫 subrequest 上限 = 33(< 50),無任一 envelope 破頂。
self-test 合成超大卡(不分段 est=114 會炸)→ 自動分 4 段、每段 ≤ 38,全綠。
待 live 部署 + 寫入(總管過 leo 閘用)
- 部署通用
code零件(Arcrun#10 分支feat/issue-10-code-component,已就緒):cd registry/components/code && npm install && npx wrangler deploy(→code.arcrun.dev)+register-component.sh code。本 workflow 的 parse_card 以component: code引用它,解析 JS 已內聯在 workflow.yaml(=lib/card-to-envelope.mjs邏輯)。不再部署 domain 零件km_wiki_card_parse。 - 部署 workflow:
km_wiki_ingest_drain(cron drain);wrangler直推 leo21c(禁acr update——codeload 綁 GitHub 假綠,Arcrun#4)。 - 注入環境變數(不放 repo):
repo=Leo/notes ref=main gitea_token kbdb_url=https://arcrun-kbdb.leo21c.workers.dev kbdb_api_key graph_url graph_api_key=leo。CLOUDFLARE_ACCOUNT_ID=leo21c(別讓官方 58309b 污染)。 - entry 寫入路徑確認:若
kbdb_upsert_block尚不透傳metadata_json(需embed:true/content_hash),entry 改用http_request直打 basePOST/PATCH /entries帶body_json.metadata_json。 - 預期寫入量(Leo/notes 現況 3 卡):3 entries + 15 triplets + 16 node records(去重後更少);分 3 次 graph 呼叫(每次 ≤ 33 subrequest)+ 3 次 entry upsert。全庫鋪開時照 cron 一卡一 tick 慢推。
- 驗收:ingest 後
GET /embed/backfill/status應見 pending 上升→drain 後歸零、embedded 增加;三模式(關鍵字/語意/圖)curl 驗。