Files
uncle6me-web 5919c6b90f fix(kbdb): 藏書地圖讀端每次都觸發重算寫 D1(Arcrun#87 止血)
liveTripletCountsByLibrary 沒有過濾 status,recomputeLibraryMap 只算
COALESCE(status,'active')='active'——兩邊判準不一致,只要庫裡有一筆
superseded triplet 就永遠判定 stale,導致每次讀地圖(GET /map、GET /map/:library、
kbdb_get_map MCP 工具)都觸發重算並新建一筆 library_map record,無止盡寫 D1,
且加劇既有的非原子 supersede 競態(kb 44 筆全 superseded、notes 兩筆同時 active
即 arcrun-rag#50 的共同根因之一)。

修法:liveTripletCountsByLibrary 的 SQL 改成 pivot 出 status 再套用與
recomputeLibraryMap 逐字一致的過濾,兩邊判準對齊。

新增迴歸測試釘住此 bug(反向驗證:跑在修前的 SQL 上會失敗,非空氣測試);
獨立用 leo21c MCP 連線連讀兩次 kbdb_get_map() 重現修前症狀
(general 庫 updated_at 從 1786457080 前進到 1786457114,中間無寫入)。

kbdb/tests/library-map.test.ts 19/19 全綠。既有殘骸(100 筆 library_map record)
未清——目前沒有可用的 DELETE 通道,待部署後另行處理。

kbdb-sql-ok:liveTripletCountsByLibrary 的 .prepare 呼叫是牆內本體
(kbdb/src/actions/),本次 checkout 開在巢狀 worktree
matrix/arcrun/.worktree-fix-87/(避免打斷另一 session 佔用中的
matrix/arcrun 主 checkout),kbdb-api-wall-guard.sh 的字面路徑比對
*matrix/arcrun/kbdb/src/* 吃不到中間多出的 worktree 目錄層,非真的繞牆。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:16:30 +08:00

78 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# library-map — Tasks
> 進度真相源。依 design §7 歸屬;動工前總管審本 SDD(#39 明令)。
| # | 任務 | 類 | 依賴 | 狀態 | 備註 |
|---|---|---|---|---|---|
| M1 | `library_map` Templateslots 定義(含 triplet 按庫過濾現況核實;不足則 Triplet template 加 optional library slot | B | — | ✅ 07-19PR#72 merge | D6 零建表。核實:triplet 無 library slot → 已走預案(design §1 核實結果) |
| M2 | kbdb base `POST /map/recompute?library=``GET /map``GET /map/:library`(聚合 SQL 住基本盤;交易式 supersede | B | M1 | ✅ 07-19PR#72 mergeleo21c `33d88016`demo `699018af` 已部署+backfillleo21c kb 111/notes 108、demo general 23 | PR+測試(真 SQLite 驗聚合);merge 後 gated 部署(leo 閘)+逐庫 backfill recompute |
| M3 | 讓地圖跟得上資料、全租戶自動 backfill(原訂做法:ingest 尾端接鏈逐庫呼 recompute | B | M2 | 🔁 **07-19 標的 ✅ 是誤報,08-08 更正並改法重做** | 見下方 08-08 段 |
| M4 | MCPinstructions 注入全館地圖+`get_map` 工具 | B | M2 | ✅ 07-19PR#73 mergeleo21c `1e73da90` 部署,MCP instructions 實載地圖) | 與 #68 同族薄殼;`kbdb_get_map`connect 時注入(isolate TTL 快取,失敗靜默略過不擋連線);merge 後 gated redeploy arcrun-mcpleo 閘) |
| M5 | GUI 首頁:全館地圖 renderconsoleportal | B | M2 | ✅ 07-19PR#74 mergeleo21c cypher `9bc3a1f3` 部署) | 取代空白搜尋框 |
| M6 | D30 連動:map 層 embedsemantic 庫路由第一跳 | B | M2 | ⬜ | #58/#59/#60 家族的第一片治本 |
| M7 | dogfoodleo 庫(leo21c)首個實例 backfill+驗收(requirements 驗收段全項) | A | M3-M5 | 🔄 demo 側實質驗過;leo21c 正式驗收單(requirements 全項)待做 | 過了才進 demo/客戶 |
### M3 更正(2026-08-08matrix/arcrun CC,總管交辦)
**07-19 標 ✅ 是誤報**`arcrun-rag 670f38a` 只接了 rag-ingest-cards 那一條管線,**repo 內
`grep -rn "map/recompute" --include=*.ts` 排除 node_modules.github-public)查無任何呼叫點**
三週來沒手動 backfill 過的租戶(絕大多數、含 youlin 與所有新租戶)`GET /map` 恆回空,
`kbdb_get_map` 的 MCP 說明文字還宣稱「地圖由 ingest 尾端自動重算(M3)」——那是假話(詳見
`system-dev/wiki/mistakes.md` 08-08 段「藏書地圖 `kbdb_get_map` 對多數租戶永遠是空的」)。
**leo 裁示**:總管原提兩案(①接上 M3 原訂設計/②降級成只算 count 的即時聚合,不維護快取),
leo 否決②——「**藏書地圖就是 arcrun 的最重要功能,讓 AI 一眼看到所有庫的摘要**」,
即時聚合算得出 count、算不出 narrative,降級等於砍功能。
**改法(非①非②,第三案)**:不再依賴任何外部呼叫者(ingest workflow)記得呼
`POST /map/recompute`——那條線跨 repo/跨租戶,已證實三週沒人接上,天生脆弱。改成
`GET /map``GET /map/:library` 讀端自己核對即時三元組數,落差就地呼叫既有的
`recomputeLibraryMap``kbdb/src/actions/library-map.ts` `ensureFreshLibraryMaps`)補算。
聚合 SQL 沒有第二套、narrative/relation_profile/bridges 這些摘要欄位原封不動——不是砍成
只算數字,只是觸發時機從「等外部呼叫」改成「讀的當下順手核對」。同時解掉:
① 全租戶自動 backfill(不需要任何人做任何事)② 跟得上資料(下一筆 ingest 進來,
下一次讀就反映)③ 不再依賴跨 repo 的 ingest 接鏈。
`narrative` 欄位有一個已知誠實限制:這個機制只**保留**既有 narrative(不會被自動重算洗掉),
但不會**生成**新的 narrative——沒人手動填過、也沒有 ingest 端寫過的庫,narrative 仍是空的
`content` 顯示「(narrative 待 ingest 補寫)」)。這是 design §1 本來就承認的缺口
narrative 抽自 wiki 首段,屬內容語意萃取,非聚合 SQL 能生出來),非本次新增。
**驗證**`kbdb/tests/library-map.test.ts` 新增 6 案(全綠,18/18)涵蓋:從未手動 recompute
即自動補齊/跟得上新資料(不手動重算,數字自動更新)/narrative 不被靜默洗掉/
`GET /map/:library` 誠實分辨查無此庫(404) vs 已知空庫(200)owner 隔離/無 triplet template
不報錯。`mcp/tests/unit/tools/kbdb-map.test.ts` 新增 1 案釘住舊謊言不再出現(18/18 全綠)。
tsc 兩包乾淨。實測:`yuga3bse` 租戶(從未 backfill 過、真實 triplet 資料橫跨 5 個庫)改前
`kbdb_get_map``{libraries:[],count:0}`——改動待部署後需重新實測驗證非空。
### M3 止血(2026-08-11Arcrun#87,總管交辦「動工前的量測」comment 第四節)
**08-08 那次改法本身留了一個判準缺口,這次補上**`ensureFreshLibraryMaps` 比對
「即時三元組數」(`liveTripletCountsByLibrary`)與「快取的地圖數」(`recomputeLibraryMap`
算出來寫進去的),但兩邊的 status 過濾不一致——`recomputeLibraryMap` 只算
`COALESCE(status,'active')='active'``liveTripletCountsByLibrary` 完全不濾 status。
只要一個庫裡混了任何一筆 superseded/deprecated triplet,兩邊數字就永遠對不上,
`ensureFreshLibraryMaps` 就永遠判定 stale ⇒ **每次讀地圖都觸發重算,每次都新建一筆
library_map recordsuperseded 舊的),無止盡寫 D1**——且加劇 `recomputeLibraryMap`
本身非原子 supersede 的既有競態(更高重算頻率 = 更高並發重算機率),是 `kb`
全部 44 筆被標 superseded、`notes` 庫兩筆同時 active`arcrun-rag#50`)這兩個症狀的
共同根因之一。
**修法**`liveTripletCountsByLibrary``kbdb/src/actions/library-map.ts`)的 SQL 改成
先 pivot 出每筆 triplet record 的 status,再套用與 `recomputeLibraryMap` 逐字一致的
`COALESCE(status,'active')='active'` 過濾,兩邊判準對齊後,資料未變動時兩個計數必然相等,
stale 判定回歸「真的有資料變動才 stale」。
**驗證**:新增迴歸案「Arcrun#87 迴歸:superseded triplet 存在時,連讀兩次地圖不會再次
觸發重算」(`kbdb/tests/library-map.test.ts`,19/19 全綠);反向驗證過——把同一顆測試跑在
修前的舊 SQL 上會失敗(`library_map` record 數 2 vs 期望 1),證明測試真的釘住這個 bug、
不是空氣測試。另外用 leo21c MCP 連線(`bfezv28v`)連讀兩次 `kbdb_get_map()`(無中間寫入)
獨立重現修前症狀:`general``updated_at``1786457080` 前進到 `1786457114`
**尚待**:改動只在分支 `fix/library-map-recompute-loop-87-v3`(未 push、未部署 leo21c);
既有 100 筆 library_map 殘骸(`kb` 44 筆 superseded`general` 41`notes` 2)未清——
清除需要一個目前不存在的 DELETE 通道(cypher-executor 的 `/kbdb/records/:id` proxy 只有
GET/POST/PATCH,無 DELETEkbdb base 自己雖有 `DELETE /records/:recordId` 但走 leo21c
需要 `KBDB_INTERNAL_TOKEN`,非 CC 可持有的機密)——待總管部署本修法+視情況補一支
DELETE proxy 後再清。