我的藏書地圖是空的——但我有 1854 條,而且搜尋查得到 #108

Closed
opened 2026-08-12 15:06:51 +00:00 by Leo · 3 comments
Owner

症狀

leo 2026-08-12 實撞:kbdb_get_map(藏書地圖)回 0 個庫
但他的 arcrun_whoami 顯示 role: adminlibraries: ["*"]、「全部知識庫」,
而且 kbdb_search 查得到東西。只有地圖那格是空的。

地圖是 AI 進庫前的導航——空的等於每次都要瞎猜該查哪裡。

實測(同一分鐘,leo21c)

/kbdb/map 不帶 owner_id      → 10 庫 / 1854 條三元組
/kbdb/map?owner_id=bfezv28v  →  9 庫 / 1851 條      ← leo 的知識在這
/kbdb/map?owner_id=leo       →  0 庫 / 0            ← portal 拿來過濾的就是這個值

根因

cypher-executor/src/routes/portal-data.ts:712

const res = await kbdbFetch(c.env, `/map?owner_id=${encodeURIComponent(portalTenant(c.env))}`);

cypher-executor/src/routes/portal.ts:62

export function portalTenant(env: Bindings): string {
  return env.CONSOLE_TENANT || "leo";     // ← 靜態環境變數,預設值寫死 leo
}

leo 的知識在 owner_id = bfezv28v(2026-08-11 深夜回灌時改的名,見 Leo/mira#8 08-12 留言),
CONSOLE_TENANT 沒跟著改 ⇒ 過濾掉全部。

🔴 這與 Arcrun#105 剛修掉的是同一個形狀,只是低一層

#105 修的是 ownerNamespace(env) = env.MCP_OWNER_NAMESPACE || "leo"
——身分來自環境變數,而不是來自登入者

本票是同一句話換一個檔案:portalTenant(env) = env.CONSOLE_TENANT || "leo"

#105 讓 MCP 接住了登入者的身分,但它下游的 cypher 資料面仍然用靜態租戶字串過濾。
只要實例的 owner_id 與 CONSOLE_TENANT 對不上(例如做過資料搬遷/改名),使用者就會看到空地圖。

要達成什麼

已登入的人(與他授權的 AI)看到的地圖,要與他實際擁有的知識一致。

不指定做法。但有一題請先回答:租戶字串該從哪裡來?
#105 的答案是「從登入者身分來」;本票的 CONSOLE_TENANT 還是環境變數。
兩者要不要收斂成同一個來源,是這張票的核心判斷。

怎麼驗才算數

  1. leo 用 AR-Mira 呼叫 kbdb_get_map看得到他的庫與三元組數(kb 1851/合計 1854)
  2. 實際打開 Portal 的「總圖」頁面看畫面——curl 不算
  3. 換一個只被授權部分庫的帳號,只看得到那幾個庫(不能為了修這題就把過濾拿掉)
  4. 回歸:kbdb_searchkbdb_query 現在是好的,不能弄壞

紅線

  • 🔴 不准把 owner_id 過濾整個拿掉——那會讓任何登入者看到所有租戶的庫
  • 🔴 不准把 leo 的資料改名回 leo(08-11 回灌已定案 bfezv28v,改回去是製造新故障)
  • 🔴 不准部署到 leo21c(要 leo 親手解閘);測試用 youlin
  • 🔴 不准推 main

📍 repo:matrix/arcruncypher-executor/src/routes/portal.ts:62portal-data.ts:712
📍 相關:Arcrun#105(MCP 身分,已修)|Leo/mira#8(owner_id 改名的來龍去脈)

## 症狀 leo 2026-08-12 實撞:`kbdb_get_map`(藏書地圖)回 **0 個庫**, 但他的 `arcrun_whoami` 顯示 `role: admin`、`libraries: ["*"]`、「全部知識庫」, 而且 `kbdb_search` 查得到東西。**只有地圖那格是空的。** 地圖是 AI 進庫前的導航——空的等於每次都要瞎猜該查哪裡。 ## 實測(同一分鐘,leo21c) ``` /kbdb/map 不帶 owner_id → 10 庫 / 1854 條三元組 /kbdb/map?owner_id=bfezv28v → 9 庫 / 1851 條 ← leo 的知識在這 /kbdb/map?owner_id=leo → 0 庫 / 0 ← portal 拿來過濾的就是這個值 ``` ## 根因 `cypher-executor/src/routes/portal-data.ts:712`: ```ts const res = await kbdbFetch(c.env, `/map?owner_id=${encodeURIComponent(portalTenant(c.env))}`); ``` 而 `cypher-executor/src/routes/portal.ts:62`: ```ts export function portalTenant(env: Bindings): string { return env.CONSOLE_TENANT || "leo"; // ← 靜態環境變數,預設值寫死 leo } ``` leo 的知識在 `owner_id = bfezv28v`(2026-08-11 深夜回灌時改的名,見 `Leo/mira#8` 08-12 留言), 而 `CONSOLE_TENANT` 沒跟著改 ⇒ 過濾掉全部。 ## 🔴 這與 `Arcrun#105` 剛修掉的是同一個形狀,只是低一層 `#105` 修的是 `ownerNamespace(env) = env.MCP_OWNER_NAMESPACE || "leo"` ——**身分來自環境變數,而不是來自登入者**。 本票是同一句話換一個檔案:`portalTenant(env) = env.CONSOLE_TENANT || "leo"`。 ⇒ **`#105` 讓 MCP 接住了登入者的身分,但它下游的 cypher 資料面仍然用靜態租戶字串過濾。** 只要實例的 owner_id 與 `CONSOLE_TENANT` 對不上(例如做過資料搬遷/改名),使用者就會看到空地圖。 ## 要達成什麼 **已登入的人(與他授權的 AI)看到的地圖,要與他實際擁有的知識一致。** 不指定做法。但有一題請先回答:**租戶字串該從哪裡來?** `#105` 的答案是「從登入者身分來」;本票的 `CONSOLE_TENANT` 還是環境變數。 兩者要不要收斂成同一個來源,是這張票的核心判斷。 ## 怎麼驗才算數 1. leo 用 AR-Mira 呼叫 `kbdb_get_map`,**看得到他的庫與三元組數(`kb` 1851/合計 1854)** 2. **實際打開 Portal 的「總圖」頁面看畫面**——curl 不算 3. 換一個只被授權部分庫的帳號,**只看得到那幾個庫**(不能為了修這題就把過濾拿掉) 4. 回歸:`kbdb_search`/`kbdb_query` 現在是好的,不能弄壞 ## 紅線 - 🔴 **不准把 owner_id 過濾整個拿掉**——那會讓任何登入者看到所有租戶的庫 - 🔴 不准把 leo 的資料改名回 `leo`(08-11 回灌已定案 `bfezv28v`,改回去是製造新故障) - 🔴 不准部署到 `leo21c`(要 leo 親手解閘);測試用 youlin - 🔴 不准推 main 📍 repo:`matrix/arcrun`(`cypher-executor/src/routes/portal.ts:62`、`portal-data.ts:712`) 📍 相關:`Arcrun#105`(MCP 身分,已修)|`Leo/mira#8`(owner_id 改名的來龍去脈)
Author
Owner

📐 收工標準補一條:修完要留一道會擋的閘(leo 2026-08-12 立原則後回頭補)

leo 2026-08-12:「做一個平台要減少 hotfix。

當晚實測:32 個 commit,其中 5 個在修「上一個修法造成的問題」
把那些 hotfix 逐個追根,全部由同兩種缺陷生出來:

  1. 同一個能力有兩份實作 ⇒ 修一次只到一半
  2. 規則存在,但沒有機制驗證有沒有照做 ⇒ 靠人記得,而人不記得

🔴 本票屬於第 ② 類。 對照組:.worker-builds 落後源碼這件事 Arcrun#93 守著
——當晚它擋對三次,每次都攔下一個「會送出舊東西」的出貨。
而本票這件事沒有守門員,所以它發生了,而且沒有任何一步會紅燈。

⇒ 因此本票的收工標準多一條(與原有的驗收並列,不取代)

修掉當下這個 bug 不算完成;要留下一個「下次再犯會被擋住」的機制。

那個機制長什麼樣由你判斷(hook/CI check/測試/管線的一站都可以),但要滿足:

  • 會擋,不是只提醒——當晚另一個教訓:「hook 只提醒不擋 ⇒ 我三次跳過都沒被攔住」
  • 誤攔比漏攔更容易殺死一道閘——被擋得莫名其妙,人就會學會繞過它,那它等於不存在。
    ⇒ 判準要看「這個動作有沒有在做那件事」,不是「字串裡有沒有出現那個詞」。
    (當晚總管修三道閘、製造了 5 次誤攔,全是這個病。)
  • 要能被測試——當晚有一道閘連「讀它自己的原始碼」都擋,導致它無法被驗證。
    迴歸測試寫成獨立檔案再跑,別讓閘擋住自己的測試。

為什麼加這條

不加的話,這張票修完之後,同一個形狀會在下一個沒人守的地方再出現一次——
而我們會再開一張票、再修一次。那就是 hotfix 迴圈,而平台的工作是讓它停下來。

—— [總管]
EOF

## 📐 收工標準補一條:**修完要留一道會擋的閘**(leo 2026-08-12 立原則後回頭補) leo 2026-08-12:「**做一個平台要減少 hotfix。**」 當晚實測:32 個 commit,**其中 5 個在修「上一個修法造成的問題」**。 把那些 hotfix 逐個追根,全部由同兩種缺陷生出來: 1. **同一個能力有兩份實作** ⇒ 修一次只到一半 2. **規則存在,但沒有機制驗證有沒有照做** ⇒ 靠人記得,而人不記得 🔴 **本票屬於第 ② 類。** 對照組:`.worker-builds` 落後源碼這件事**有** `Arcrun#93` 守著 ——當晚它擋對三次,每次都攔下一個「會送出舊東西」的出貨。 而本票這件事**沒有守門員**,所以它發生了,而且沒有任何一步會紅燈。 ### ⇒ 因此本票的收工標準多一條(與原有的驗收並列,不取代) **修掉當下這個 bug 不算完成;要留下一個「下次再犯會被擋住」的機制。** 那個機制長什麼樣由你判斷(hook/CI check/測試/管線的一站都可以),但要滿足: - **會擋,不是只提醒**——當晚另一個教訓:「hook 只提醒不擋 ⇒ 我三次跳過都沒被攔住」 - **誤攔比漏攔更容易殺死一道閘**——被擋得莫名其妙,人就會學會繞過它,那它等於不存在。 ⇒ 判準要看「**這個動作有沒有在做那件事**」,不是「**字串裡有沒有出現那個詞**」。 (當晚總管修三道閘、製造了 5 次誤攔,全是這個病。) - **要能被測試**——當晚有一道閘連「讀它自己的原始碼」都擋,導致它無法被驗證。 迴歸測試寫成獨立檔案再跑,別讓閘擋住自己的測試。 ### 為什麼加這條 不加的話,這張票修完之後,**同一個形狀會在下一個沒人守的地方再出現一次**—— 而我們會再開一張票、再修一次。**那就是 hotfix 迴圈,而平台的工作是讓它停下來。** —— [總管] EOF
Author
Owner

已修,PR #112(分支 fix/library-map-108)——尚未部署,issue 保持 open 等 leo21c 實證

租戶字串該從哪裡來:從「寫入這批知識的那一方」來,也就是實例 namespace(~/.arcrun/config.yamlapi_key),不是一份手抄的環境變數預設值。

不是掛到每個帳號上——portal 帳號共用同一台實例的知識庫(design D-2),帳號之間的差別是 libraries 權限不是 owner_id;複製一份到帳號上只是多一個會過期的副本。與 #105 收斂的是同一條原則(單一權威來源+解析不到誠實失敗+可機械驗證),落點不同是因為「誰擁有這個事實」不同。

修法三段:

  1. 唯一產地 cypher-executor/src/lib/tenant.tsknowledgeOwner(env) 回 branded TenantIdARCRUN_NAMESPACECONSOLE_TENANT → 丟錯,|| 'leo');帳號層 accountTenant(env) 回普通 string,型別上不可能流進資料面。
  2. acr update 先驗再寫GET /kbdb/map?owner_id=<api_key> 查得到庫才注入 ARCRUN_NAMESPACE,查不到就一個字都不動(避免把一鍵安裝實例指向空的那一格)。
  3. 空地圖分四態(沿 #100):no_library_grantfiltered_outscope_mismatchconfirmed_emptyscope_mismatch 以前不存在,所以設定錯誤被畫成「你沒有資料」。

同族一起修(下面那道閘一次抓到):console-dashboard.ts 4 處、console-auth.ts 1 處同一句話——console 首頁的規模數字與藏書地圖對 leo 也一直是空的。

留下的閘:「靜態租戶字串不得用於資料面過濾」,擋在出貨路徑(scripts/build-worker-artifacts.mjs 編成品前先掃,違規→建置中止),判準盯行為不盯字詞,閘本身有 24 條測試(含 11 條零誤攔案例)且擋不到自己。

驗證:cypher vitest 441 綠/14 紅(14 紅與 base e05518a 逐條相同);cli 60/60 綠;tsc 零新增錯誤;閘壞例子實跑 exit 1、build 實跑「建置中止」。

未驗:端到端(kbdb_get_map 回 1851、Portal 總圖畫面、部分授權帳號)需部署到 leo21c,那道閘要 leo 親手解。部署注意事項寫在 PR ③——重點:光部署不夠,ARCRUN_NAMESPACE 要真的進到 cypher 的 [vars](走 wrangler 直推要自己帶 --var),且 CONSOLE_TENANT 不要動(改了 leo 登不進 Portal)。

已修,PR #112(分支 `fix/library-map-108`)——**尚未部署,issue 保持 open 等 leo21c 實證**。 **租戶字串該從哪裡來**:從「寫入這批知識的那一方」來,也就是實例 namespace(`~/.arcrun/config.yaml` 的 `api_key`),不是一份手抄的環境變數預設值。 **不是**掛到每個帳號上——portal 帳號共用同一台實例的知識庫(design D-2),帳號之間的差別是 `libraries` 權限不是 `owner_id`;複製一份到帳號上只是多一個會過期的副本。與 #105 收斂的是同一條原則(單一權威來源+解析不到誠實失敗+可機械驗證),落點不同是因為「誰擁有這個事實」不同。 修法三段: 1. 唯一產地 `cypher-executor/src/lib/tenant.ts`:`knowledgeOwner(env)` 回 branded `TenantId`(`ARCRUN_NAMESPACE` → `CONSOLE_TENANT` → 丟錯,**無 `|| 'leo'`**);帳號層 `accountTenant(env)` 回普通 string,型別上不可能流進資料面。 2. `acr update` **先驗再寫**:`GET /kbdb/map?owner_id=<api_key>` 查得到庫才注入 `ARCRUN_NAMESPACE`,查不到就一個字都不動(避免把一鍵安裝實例指向空的那一格)。 3. 空地圖分四態(沿 #100):`no_library_grant`/`filtered_out`/`scope_mismatch`/`confirmed_empty`。`scope_mismatch` 以前不存在,所以設定錯誤被畫成「你沒有資料」。 **同族一起修**(下面那道閘一次抓到):`console-dashboard.ts` 4 處、`console-auth.ts` 1 處同一句話——console 首頁的規模數字與藏書地圖對 leo 也一直是空的。 **留下的閘**:「靜態租戶字串不得用於資料面過濾」,擋在出貨路徑(`scripts/build-worker-artifacts.mjs` 編成品前先掃,違規→建置中止),判準盯行為不盯字詞,閘本身有 24 條測試(含 11 條零誤攔案例)且擋不到自己。 驗證:cypher vitest 441 綠/14 紅(14 紅與 base e05518a 逐條相同);cli 60/60 綠;tsc 零新增錯誤;閘壞例子實跑 exit 1、build 實跑「建置中止」。 ◐ **未驗**:端到端(`kbdb_get_map` 回 1851、Portal 總圖畫面、部分授權帳號)需部署到 leo21c,那道閘要 leo 親手解。部署注意事項寫在 PR ③——重點:**光部署不夠,`ARCRUN_NAMESPACE` 要真的進到 cypher 的 `[vars]`**(走 wrangler 直推要自己帶 `--var`),且 `CONSOLE_TENANT` 不要動(改了 leo 登不進 Portal)。
Author
Owner

通了——leo 實機驗收(2026-08-13)

強制更新後印出那行,AR-Mira 立刻查得到:

→ 核對雲端要用哪個知識命名空間... ✓
  ARCRUN_NAMESPACE = bfezv28v(這個命名空間底下查得到你的知識庫)
  ARCRUN_BUNDLE_VERSION = 1.4.43(實際部署 commit 9ff933b)
  23/23 worker ✓
kbdb_get_map          → 9 個庫,kb = 1851 條三元組
kbdb_search("Arcrun") → 29 筆,全是 leo 自己的 journals(2026_04_18 / 05_13 / 06_11 / 07_07…)
arcrun_whoami         → auth: portal-login|logged_in_as: leo21c|role: admin|libraries: ["*"]

🔴 但它先卡了一輪,而卡住的原因不是本票

cli/dist/lib/deploy.jsnamespaceHasKnowledge 出現 0 次。

本票的 CLI 那半(寫 ARCRUN_NAMESPACE)在原始碼裡是對的,但 leo 的 acr npm link 到
cli/dist/,而 dist/ 不在版控——他跑的是他上次編的那份,裡面根本沒有這段程式碼。

那行沒印出來不是「保險判定沒知識」,是「寫它的程式碼不存在」。
兩者在畫面上長得一模一樣(都是沉默),我因此往 code 又查了一輪。

重編之後:dist 含 namespaceHasKnowledge → 1 次namespaceHasKnowledge(...) = true

這正是 #109 票上寫的那句話成真:

「如果 dist/ 是舊的,修法不會生效,而我會判定『這個修法沒用』
那次是先重編才跑,所以沒出事。但『先想到要重編』靠的是運氣,不是機制。」

這次運氣沒站在我這邊。 它從「差點」變成「真的發生過」——已該補進 #109。

順帶量到的(不是本票,另有票)

9 個庫裡 8 個 triplet_count = 0(只有 kb 有 1851)⇒ 就是 #87(藏書地圖是空的)。
本票只管「地圖看不看得到庫」,庫裡的關係抽取歸 #87。

—— [總管]

## ✅ 通了——leo 實機驗收(2026-08-13) 強制更新後印出那行,AR-Mira 立刻查得到: ``` → 核對雲端要用哪個知識命名空間... ✓ ARCRUN_NAMESPACE = bfezv28v(這個命名空間底下查得到你的知識庫) ARCRUN_BUNDLE_VERSION = 1.4.43(實際部署 commit 9ff933b) 23/23 worker ✓ ``` ``` kbdb_get_map → 9 個庫,kb = 1851 條三元組 kbdb_search("Arcrun") → 29 筆,全是 leo 自己的 journals(2026_04_18 / 05_13 / 06_11 / 07_07…) arcrun_whoami → auth: portal-login|logged_in_as: leo21c|role: admin|libraries: ["*"] ``` ## 🔴 但它先卡了一輪,而卡住的原因不是本票 **`cli/dist/lib/deploy.js` 裡 `namespaceHasKnowledge` 出現 0 次。** 本票的 CLI 那半(寫 `ARCRUN_NAMESPACE`)在原始碼裡是對的,但 leo 的 `acr` npm link 到 `cli/dist/`,而 **`dist/` 不在版控**——他跑的是他上次編的那份,裡面根本沒有這段程式碼。 ⇒ **那行沒印出來不是「保險判定沒知識」,是「寫它的程式碼不存在」。** 兩者在畫面上長得一模一樣(都是沉默),我因此往 code 又查了一輪。 重編之後:`dist 含 namespaceHasKnowledge → 1 次`、`namespaceHasKnowledge(...) = true`。 這正是 **#109** 票上寫的那句話成真: > 「如果 `dist/` 是舊的,修法不會生效,**而我會判定『這個修法沒用』**。 > 那次是先重編才跑,所以沒出事。但『先想到要重編』靠的是運氣,不是機制。」 **這次運氣沒站在我這邊。** 它從「差點」變成「真的發生過」——已該補進 #109。 ## 順帶量到的(不是本票,另有票) 9 個庫裡 **8 個 triplet_count = 0**(只有 `kb` 有 1851)⇒ 就是 **#87**(藏書地圖是空的)。 本票只管「地圖看不看得到庫」,庫裡的關係抽取歸 #87。 —— [總管]
Leo closed this issue 2026-08-12 17:58:09 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Leo/Arcrun#108