uncle6me-web
|
cac874601f
|
chore(builds): 重編 tier2 成品——把 #108 放進執行檔(出貨路徑 A 的第 0 步)
原始碼修好不等於使用者拿得到:`.worker-builds/` 是安裝器與 `acr update`
實際部署的那份執行檔。本次一併重編,repo_dirty=false、repo_head=b223a698。
arcrun-cypher-executor sha256=e0026a23792f source=b223a698 ← #108
arcrun-kbdb sha256=9c6d41895d78 source=f87d0e92(未變)
arcrun-http-request sha256=9a9dcb71879a source=1e85dfb4(未變)
arcrun-code sha256=285a7406ec69 source=621cb8d9(未變)
arcrun-mcp sha256=3ebc0d441bc0 source=10d150ac(未變)
其餘四顆的 js 位元組有變是 esbuild 版本/相依重裝造成的重編,source commit 未動。
本次起這條路徑多一道閘:編成品前先跑租戶來源檢查,違規直接「建置中止」
(見同 PR 的 scripts/build-worker-artifacts.mjs)——實跑輸出貼在 PR。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-13 00:16:55 +08:00 |
|
uncle6me-web
|
b223a69884
|
fix(portal): 藏書地圖看得到自己的知識——租戶字串改從「寫入端」來,不再拿環境變數預設值(Arcrun#108)
leo 2026-08-12 實撞:藏書地圖回 0 個庫,同一分鐘 KBDB 裡有 1854 條三元組,
`arcrun_whoami` 顯示 admin/全部知識庫、`kbdb_search` 也查得到——只有地圖那格是空的。
病根(不是資料掉了,是讀寫兩端各拿一個來源):
寫入端 owner_id = `~/.arcrun/config.yaml` 的 `api_key`(CLI push/小幫手上傳/MCP,
leo = `bfezv28v`)
讀取端過濾 = `portalTenant(env) = env.CONSOLE_TENANT || "leo"`
——repo toml 帶的**官方 prod 值**,而 `acr` 從來不注入 CONSOLE_TENANT
⇒ 那個 `"leo"` 不是理論邊角,是每台 self-hosted 實例的實際行為,1854 條全被濾掉。
與 #105(`env.MCP_OWNER_NAMESPACE || "leo"`)同一句話,換一個檔案。
租戶字串該從哪裡來(本票的核心判斷):
**從「寫入這批知識的那一方」來,不是從一份手抄的環境變數預設值來。**
不是「掛到每個帳號上」——portal 帳號共用同一台實例的知識庫(design D-2),
帳號之間的差別是 libraries 權限不是 owner_id;複製一份到帳號上只是多一個會過期的副本。
#105 真正的教訓是:過濾用的租戶字串要有單一權威來源、解析不到要誠實失敗、且要能機械驗證。
修法:
1. 唯一產地 `cypher-executor/src/lib/tenant.ts`
- `knowledgeOwner(env)` → branded `TenantId`:`ARCRUN_NAMESPACE` → `CONSOLE_TENANT` →
丟 `TenantUnresolvedError`。**沒有字面預設值**——`|| 'leo'` 正是把「這台機器沒設定」
偽裝成「你沒有資料」的元凶。
- `accountTenant(env)` → 普通 `string`(帳號子 namespace `{tenant}::portal` 與 cypher
自己寫的設定用它)。**回 string 是刻意的**:型別上就不可能流進知識資料面。
- 資料面過濾一律經 `ownerQuery()` / `ownerField()`,只吃 `TenantId`。
2. 值的正解由 CLI 從真相源導出:`acr update` 把 config 的 `api_key` 注入成 `ARCRUN_NAMESPACE`,
但**先驗再寫**(`GET /kbdb/map?owner_id=<api_key>` 查得到庫才寫;查不到/問不到就一個字
都不動)。無條件覆蓋會把「知識本來就在 CONSOLE_TENANT 底下」的一鍵安裝實例指向空的那一格
——那是 #97/#106 那類「更新一次把人家的東西弄不見」,比原本的 bug 更糟。
未注入時回退 CONSOLE_TENANT ⇒ 對官方 prod 與未更新的實例,這次改動是惰性的。
3. 空地圖分四態(沿 #100「讀不到就說讀不到」):no_library_grant/filtered_out/
scope_mismatch/confirmed_empty。scope_mismatch 以前不存在,所以設定錯誤被畫成
「你沒有資料」。回應仍不含租戶字串(design §3.3 紅線)。
4. 同族一起修(同一道閘一次抓到):console-dashboard 4 處、console-auth 1 處
——console 首頁的規模數字與藏書地圖對 leo 也一直是空的。
留下的閘(規則存在但沒機制驗證=會再犯第三次):
· 型別閘:TenantId 只能由 tenant.ts 產出 → 拿隨手一個 string 去過濾,tsc 當場不給過。
· 出貨閘:scripts/build-worker-artifacts.mjs 編 tier2 成品前先掃,違規 → 編不出成品。
· 閘自己可測:規則是純函式(tenant-source-rules.mjs),tests/tenant-gate.test.ts
逐條驗「5 種壞例子會擋」+「11 種合法寫法零誤攔」;掃描範圍只有 src/,擋不到自己。
規範寫入 .claude/rules/02-forbidden.md 第六類、system-dev/wiki/mistakes.md #26。
沒動:庫權限過濾(一字未改,回歸測試釘住)、帳號資料落點、任何金鑰、租戶字串仍不下發前端。
驗證:
cypher vitest 441 綠 / 14 紅,14 紅與 base commit e05518a 逐字相同(既有)
tsc 5 個既有錯誤,零新增
cli node:test 60/60 綠(含本次新增 12 條);tsc 零錯誤
閘 壞例子實跑 exit 1;build 實跑「建置中止」;乾淨時實跑通過
端到端 ◐ 未驗:需部署到 leo21c,那道閘要 leo 親手解(見 PR ③)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-13 00:15:58 +08:00 |
|
uncle6me-web
|
fad5da0e17
|
rules/07 §3.6:自舉例外——能力只實作一次,但不一定要是 HTTP API
代補 PR #111 的 agent 想加但改不動的段落(該檔受保護)。
立這條的原因不是理論:#97 的修法一開始寫在 cli/src/lib/resource-resolver.ts
(能力住在介面層,違反 §0)⇒ 安裝器拿不到它 ⇒ 同一個 bug 只修了一半,
走 acr 的人有保護、走 install.arcrun.dev 的人沒有——而所有真實用戶走後者。
leo 2026-08-12:「根本就不應該在 CLI,我要的是一個大家都可以用到的規則。」
記三件,都是為了不讓下一個人「修正」回去:
① 為什麼不放 cypher API(自舉/輸入是使用者自己的帳號狀態/它是純函式)
② §0 的正確讀法是「只准有一份、不准住在單一介面裡」,放 API 只是常見手段
③ npm 打包例外:cli/ 下的逐位元組副本由 sync --check 機械擋漂移,不是第二份實作
📍 repo:matrix/arcrun(shared/resource-rule/、cli/、.claude/rules/07)
+ products/arcrun-rag(安裝器待接上,已派工)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-12 23:50:53 +08:00 |
|
Leo
|
13155a1d7b
|
Merge PR #111: 把「該用哪些資源」搬出 CLI——一份實作,兩條路吃同一條規則
leo 2026-08-12:「根本就不應該在 CLI,我要的是一個大家都可以用到的規則。」
總管複驗(不聽自述,自己重跑):
兩份 rule.mjs 逐位元組相同;--check 綠
故意加一行製造漂移 → --check 當場擋下;還原後恢復綠 ⇒ 閘是真的,不是假綠
cli 測試 58 pass / 0 fail(main 上是 49 ⇒ 多 9 條全過)
邊界乾淨:沒動 cypher/kbdb/mcp;resource-resolver.ts 減 461 行變薄殼
兩條新測試比我寫的收工標準更嚴:
two-paths-agree 斷言「一邊停手、一邊照做 = 最危險的分歧」與「停手的理由也要一樣」
single-implementation 帶 assert.ok(files.length > 0) 防「檢查 0 個卻通過」
——後者正是總管當晚剛撞過的假綠形狀
殘項(agent 誠實標的,不擋併):
· youlin 上走安裝器那條路的實測沒跑成——安裝器在 arcrun-rag,不在它的工作副本裡,
且沙箱無 CF 憑證。它依紅線「不確定時寧可中止」沒有猜著跑。
· 安裝器那一行 import 尚未接上(規格在 shared/resource-rule/README.md §4)
· rules/07 想補「自舉例外」正例,該檔受保護,內容留在 README §2 待總管代補
|
2026-08-12 15:44:50 +00:00 |
|
uncle6me-web
|
bb548b6fdf
|
refactor(shared): 「該用哪些資源」搬出 CLI——一份實作,acr 與安裝器吃同一條規則
leo 2026-08-12:「根本就不應該在 CLI,我要的是一個大家都可以用到的規則。」
「這個實例該用哪些資源」換到安裝器就要重寫一次 ⇒ 依 rules/07-thin-shell.md 的判準
它是**能力**,而它原本住在 cli/src/lib/resource-resolver.ts ⇒ 那本身就是違規。
後果已經真的發生:acr 那條有 Arcrun#97 的修法、安裝器那條沒有,於是安裝器照名字
找、找不到就建一顆空的綁上去 ⇒「我按了更新,工作流和登入全不見了」。
規則搬到 shared/resource-rule/(零依賴 ESM,Node 與 Workers runtime 都直接跑):
· rule.mjs 規則本體+把 CF 回應讀成事實的 normalizeLive*
· cf-resource-api.mjs ResourceApi 的 CF REST 實作——**眼睛也共用**:
兩條路各自解讀 CF 回應,只要一邊看不到既有綁定就會去新建,
#97 不需要規則寫錯就能重演
· installer-entry.mjs 安裝器唯一該碰的入口 resolveInstanceResources()
不是做成 cypher 端點的理由(自舉):這條規則要在「決定怎麼裝」的當下就用得到,
而那時 cypher 可能還不存在(安裝器的工作正是把它生出來);且輸入是使用者自己帳號的
綁定狀態,不該送去平台換答案。它是純函式,用不著變成服務。
只有一份,機械看守:
· 安裝器直接 import repo archive 裡的原稿,**不需要副本**
· acr 因為 npm pack 打不進套件目錄外的檔案,帶一份逐位元組鏡射
(scripts/sync-resource-rule.mjs 產生;build/test 先跑 --check,差一位元組就紅)
——同 cli/harness/ 產生物+世代閘的既有慣例
· cli/tests/single-implementation.test.ts 掃全 repo:7 支規則函式的實作只有一處
CLI 淨 -496 行(邏輯是搬走,不是複製)。cf-api.ts 的 CfAccountClient 保留公開介面,
ResourceApi 那七個方法全部委派共用 client。
驗證:cli 58/58 綠(含新增的兩條路一致性 fixture + 三種情境),tsc --noEmit 乾淨。
|
2026-08-12 23:37:01 +08:00 |
|
uncle6me-web
|
e05518a2b4
|
chore(builds): 重編成品——把 #107 的版本標籤修法放進執行檔
出貨線第 1 站的 #93 新鮮度閘擋下:cypher-executor 成品記 10d150a,
而 HEAD 已是 2129356(#107 改了 routes/health.ts 與 types.ts)。
這是該閘今晚第三次擋對——沒有它,1.4.42 會送出一個「版本標籤修法只在源碼裡」的成品。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-12 22:22:57 +08:00 |
|
Leo
|
21293568d5
|
Merge PR #107: 更新完還看得到版本號——CLI 重部署不再把版本標籤洗掉(Arcrun#106)
總管複驗(不聽自述,自己重跑並與 base 逐條比對):
cli main 是 0 pass / 3 fail(測試根本跑不起來)→ PR 49 pass / 0 fail
⇒ agent 那句「#97 的守衛測試一直沒在跑」是真的,又一個假綠
cypher 14 failed / 402 passed(base 14 / 400);失敗清單 diff 無輸出=零回歸
設計比我建議的對:版本標籤每趟重烙、其他 vars 才沿用。
我原本建議「一律沿用」會讓版本號永遠停在安裝當時。
殘項(agent 誠實標的,不擋併):沒在真實例上跑過、沒有真 Portal 截圖
(它的環境指向 leo21c 紅線、無 youlin 憑證、瀏覽器未授權)。總管接手補這段。
|
2026-08-12 14:05:57 +00:00 |
|
uncle6me-web
|
53b05c6d3d
|
fix(cli): 更新完還看得到版本號——CLI 重部署不再把版本標籤(和你的設定)洗掉
leo 08-12 實撞:更新完 leo21c,Portal 設定頁的「版本」變成
「無法讀取目前版本(知識庫服務可能正在啟動)」。版本號是 leo 唯一的驗收介面,
看不到就等於他無法自己確認任何一次更新有沒有生效。
根因(Arcrun#106):`bundle_version` 來自部署時注入的 plain_text var
`ARCRUN_BUNDLE_VERSION`,而**只有安裝器會注入**。wrangler deploy 是整份覆蓋,
toml 沒寫的 var 直接消失 ⇒ CLI 更新那條路每跑一次就把標籤洗掉一次。
#97 修好了「櫃子」(KV/D1/Vectorize 沿用既有),沒修「櫃子上的標籤」。
修法(兩種 var 走相反的規則,這是本次的判斷):
· 設定類 var = 使用者實例的事實 → **沿用**(讀綁定時同一份回應就帶回來,不多打 API)
——把 #97「已部署的 worker 上綁著什麼就是事實」原封不動套用到 plain_text var。
· 版本標籤 = 這份成品的屬性 → **每趟重烙,絕不沿用舊值**。
沿用舊值會得到一個永遠停在安裝當天的假標籤——比沒有標籤更糟,
因為它會讓人以為驗收過了。
版號取部署當下發行頻道公告的 release(Portal/daemon 就是拿它當「最新版」比),
另外把**真正部署的 commit** 一起烙上去(/health 多吐 `bundle_commit`)→ 漂掉查得出來。
查不到 release 就誠實退成 `YYYY-MM-DD+<commit7>`,不掰一個 semver 假裝已是最新。
順帶(都是同一條路上的東西):
· ref 先解析成 commit sha 再用 sha 下載 archive——不可變,順手解掉 branch tarball 被快取的老病
· Portal 版本行接受帶 build metadata 的 semver(`1.4.41+d61` 這種先前一律被當成「較舊版本」)
· cli 測試在 node 22 上本來一支都跑不起來(.js→.ts 解析 + parameter property),補上 resolve hook
——#97 那份「使用者的東西還在不在」的迴歸守衛也在其中,跑不起來的守衛等於沒有守衛
· types.ts 的 ARCRUN_BUNDLE_VERSION 重複宣告(TS2300)併回一處
驗證見 PR:cli 49/49 綠、cypher health 4/4 綠、Portal 版本行原始碼實跑五種情境、
對真實已部署 worker 的唯讀 dry-run。**未做**:真實實例上的 acr update 端到端
(本機唯一有憑證的帳號是 leo21c=紅線禁碰,youlin 無憑證)。
Refs: Leo/Arcrun#106, #97, #95
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-12 21:58:43 +08:00 |
|
uncle6me-web
|
f87d0e92f4
|
fix(migrations): 0005/0006 從來沒進過版控——被 *.sql 規則吃掉,每個用戶都收到「部署物缺」
leo 更新 leo21c 時撞到(他問「部分失敗?」):
✗ D1 migration: 部署物缺 kbdb/migrations/0005_credential_template.sql
✗ D1 migration: 部署物缺 kbdb/migrations/0006_drop_credentials_table.sql
(四顆 worker cypher/registry/kbdb/mcp 全部 ✓,失敗的只有這兩個檔)
根因不是誰忘了推:.gitignore:53 的 `*.sql` 是為了擋 D1 匯出備份(整庫全量=機敏),
但它連 migration 一起吃掉。0001-0004 還在,只因為它們在該規則之前就 commit 了
(gitignore 不影響已追蹤檔案)⇒ 0005/0006 從產生那天起就不在任何 clone 裡。
⇒ 這不是 leo 一台的事:更新指令從 Gitea 抓 main,那兩個檔不在那裡
⇒ **任何人裝/更新都會收到同一組失敗**,包含全新安裝。
修法照 rules/05-deploy-convention.md「WASM 來源」段已有的慣例
(`.component-builds/**/component.wasm` 就是用否定規則放行的):
!kbdb/migrations/*.sql
範圍實測(沒開太大):
kbdb/migrations/0005、0006 → 放行
backup-2026.sql / kbdb/backup-x.sql / dump.sql / cypher-executor/export.sql → 仍被擋
進版控前確認過無機敏值:grep 命中的 token/secret/api_key 全是欄位名
(api_key、secret_ref)與註解;無 >=20 位英數的疑似真值。
殘項:leo21c 實查 templates 9 個、credential 不在其中 ⇒ 0005 從未套用,
那台仍停在 D38 之前(credentials 走 0002 的獨立表)。要補套需另跑一次更新。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-12 21:10:12 +08:00 |
|
uncle6me-web
|
ba152bc83a
|
chore(builds): 重編 tier2 成品——把 #105 放進執行檔(出貨路徑 A 的第 0 步)
leo 選 A(把出貨線推到能出一版含 #105 的 bundle)。查證後發現最前面還有一層:
cypher 源碼最後動 10d150a(#105) / 成品最後動 a24f291(更早)
mcp 源碼最後動 10d150a(#105) / 成品最後動 8e10f1d(更早)
⇒ #105 改了 cypher 與 mcp 兩邊源碼,但沒有重編成品。
這正是 Arcrun#93 那道「源碼比執行檔新就停下來」的閘要擋的狀態。
用官方唯一編譯點 scripts/build-worker-artifacts.mjs(Arcrun#80 的機制,已存在)
重編五顆,全部 5/5:
arcrun-cypher-executor 571KB source=10d150ac
arcrun-kbdb 146KB source=10d150ac
arcrun-mcp 1152KB source=10d150ac
arcrun-http-request 78KB source=1e85dfb4(未變)
arcrun-code 150KB source=621cb8d9(未變)
交叉驗證(不只信它自記的 commit):/portal/data/ 這條 #105 才有的路徑
在 arcrun-mcp 成品裡出現 8 次。
下一步(等 leo 解閘):把修好的引擎部署到 geek6688 當出貨機
(ARCRUN_SHIP_BASE 可覆寫,預設是 leo21c——arcrun-rag#79 要搬離的正是這個),
再從那台跑出貨線,第 17 站 purge 的 wait 節點才有帶修法的引擎可跑。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-12 20:03:30 +08:00 |
|
Leo
|
89b80ff90e
|
Merge PR #105: MCP 用登入者的身分查詢,不再去找服務內部金鑰
總管複驗(不聽自述,自己重跑,並與 base a24f291 逐條比對):
mcp tsc 乾淨;vitest 113/113 綠
cypher 14 紅與 base 逐字元相同(diff 無輸出);passed 386→400,新增 14 條全過
kbdb 5 紅與 base 相同
安全邊界 11 條全綠(跨租戶 404/越庫 404/owner_id 由 server 定死)
殘項(已記,不擋併):
· kbdb 的 owner_id 欄位改動沒有新測試守著(213 總數未變)
· 工作流面仍靠 MCP_OWNER_NAMESPACE || leo 那個巧合,本 PR 刻意沒動(拔了 arcrun_* 會全面失效)
· 端到端 ❌ 未驗:要部署到 leo21c,那道閘要 leo 親手解
|
2026-08-12 11:42:03 +00:00 |
|
uncle6me-web
|
10d150ac2b
|
fix(mcp): MCP 用登入者的身分查詢,不再去找一把服務內部金鑰
leo 2026-08-12:「人類進 Portal 輸入帳密表示你是主人,可以查到你權限所有東西;
AI 透過輸入帳密的 MCP 查詢表示是授權的 AI,可以查到主人允許查的任何東西。」
「掛上 MCP 並輸入帳密,那個動作本身就是授權」⇒ 下游不得再要求第二次認證。
病根(不是金鑰沒同步,是身分沒接住):
oauth/routes.ts 驗完 Portal 帳密只留下 `loginOk = res.ok` 一個布林值,身分當場丟棄,
namespace 改從 `MCP_OWNER_NAMESPACE || "leo"` 拿。於是查詢時手上沒有身分可帶,
只好用 KBDB_INTERNAL_TOKEN 直打 KBDB——那條路繞過 portal 所有庫過濾,
而且不管誰登入都看到同一格、看到全部。CLI 也從不注入 MCP_OWNER_NAMESPACE,
所以那個 "leo" 預設值是每台實例的實際行為,不是理論上的邊角。
修法(走既有那條路,不發明新的):
1. 接住身分:/authorize 解析 /portal/login 回應,把 portal session token +
display_name/role/libraries 存進 authorization code → access token。
/portal/login 補回 session_expires_in,access_token TTL 夾成
min(自己的 TTL, portal session TTL)——不讓「MCP 還連著、底下 session 早死」。
cypher 回 200 但沒給 session_token(舊版)→ 不發碼,不簽一張沒有身分的 token。
2. 攜帶身分:kbdb_* 全部改走 cypher `/portal/data/*`,Authorization 帶登入者的
session。庫過濾/租戶注入/停用即時生效全在 server 側,與人類走 portal 網頁同一道閘。
kbdb_graph_neighbors 因此不再需要 kbdb_base(server 自己知道查哪個庫)。
藏書地圖(含連線時注入 instructions 的那份)同樣只回有權限的庫,快取改 per-session
分格——地圖本身就是情報,不能讓先連上的人把視野留給下一個。
3. fail-closed:舊 token 沒有身分 → 誠實要求重新連線,不偷偷退回服務金鑰那條老路。
服務級憑據(static token / partner key)維持既有 KBDB 直連,arcrun_* 零回歸。
新增 cypher portal 資料面端點(能力長在 API,MCP 只暴露;rule 07):
GET /portal/data/map、/portal/data/map/:library
GET /portal/data/templates、POST /portal/data/templates
GET /portal/data/records/by-template/:t、GET /portal/data/records/:id
POST /portal/data/records
全部:呼叫端自帶 owner_id 一律不生效;越權與不存在同回 404;寫入 owner_id 由 server 定死。
KBDB base:`GET /records/:id` 與 by-template 補回 owner_id 欄位——原本不回,
呼叫端無從判斷「這筆是不是我的」,按 id 直讀等於沒有租戶邊界。
沒動:KBDB fail-closed 閘、任何金鑰、租戶字串仍不下發給呼叫端。
驗證:
mcp tsc 綠;vitest 113/113 綠(改前 48 綠 29 紅)
cypher vitest 400 綠 / 14 紅,14 紅與 base commit a24f291 逐條相同(既有)
kbdb vitest 208 綠 / 5 紅,5 紅同為既有(migrations/*.sql 被 gitignore)
端到端 ◐ 未驗:需部署到 leo21c,那道閘要 leo 親手解(見 PR)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-12 19:33:12 +08:00 |
|
uncle6me-web
|
a24f2912eb
|
chore(builds): 重編執行檔——把 wait 的修法真的放進引擎成品(Arcrun#93 的閘抓到的)
#101 併進 main 後,.worker-builds/arcrun-cypher-executor 還記著 a5e4caf,
比源碼 HEAD 少了 f1370e2(wait 搬回引擎那筆)。
⇒ 這正是 #93 那道閘存在的理由:**沒有它,這次出貨會送出一個
「引擎裡沒有 wait 修法」的成品**——leo 更新完照樣燒 CPU,而出貨線全綠。
閘寫出來的隔天就抓到一次,抓到的還是我。
arcrun-cypher-executor source a5e4caf → f1370e22 sha256=8411ed59b7ad
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-12 15:30:26 +08:00 |
|
Leo
|
1791ffa497
|
Merge pull request '#101 等待搬回引擎——WASI 沙箱裡沒有「不花 CPU 地等」這種東西' (#103) from fix/wait-not-in-wasm-101 into main
|
2026-08-12 07:26:00 +00:00 |
|
Leo
|
296fb01247
|
Merge pull request 'portal 安裝完成清單拿掉「去 Google 申請 AI 金鑰」那一項(arcrun-rag#81)' (#102) from fix/portal-onboarding-drop-gemini-key-81 into main
|
2026-08-12 07:24:47 +00:00 |
|
uncle6me-web
|
f1370e2275
|
fix(engine): 等待搬回引擎——WASI 沙箱裡沒有「不花 CPU 地等」這種東西(Arcrun#101)
leo 在 youlin stage 實測(只有 input >> wait 兩個節點):
ms=3000 → 38.9s 後 503(1102) / ms=20000 → 34.0s / ms=30000 → 34.9s / 寫死 3000 → 34.8s
四個值同一種死法、與 ms 無關 ⇒ 病不是「等待很貴」,是「等待從來沒成功過」。
修法:wait 移進 BUILTIN_COMPONENTS,由引擎 await 一個 timer。
只花 wall-clock、不記 CPU ⇒ 等 30 秒與等 3 秒同價(皆 ≈0)。
I/O 契約沿用 component.contract.yaml,既有 workflow 的 wait 節點定義不必改。
🔴 誠實標明:原本註解斷言「Workers 時鐘在同步執行期間凍結,所以自旋永不結束」。
寫測試去證,反而被打臉——workerd 裡自旋 2553 圈後 Date.now() 就前進了。
那條假斷言已刪除(不是改鬆),完整機制降級為推測。修法不依賴它:
純 WASI 沙箱本來就沒有睡覺這個手段,會等的只有宿主。
實測:
npx vitest run tests/wait-builtin.test.ts → 12 passed (12)
npx vitest run(全套) → 386 passed / 14 failed
(14 = 動工前的既有紅燈數,未新增)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-12 15:15:08 +08:00 |
|
uncle6me-web
|
d58a6e152d
|
fix(portal): 安裝完成清單拿掉「去 Google 申請 AI 金鑰」那一項(arcrun-rag#81)
leo 2026-08-12:「已經改用 workers AI,刪掉。」
那一項長在**安裝完成清單裡而且是勾選項** ⇒ 使用者會以為不做這步就沒裝完,
而它要人離開流程、去第三方網站申請帳號、把金鑰貼進表單
——**這是整條安裝路徑上最重的一個動作,而它現在是白做的**。
連帶:步數不再寫死「還差 2 步」,改成由剩下的項目算出來
(少一項卻還寫 2,是另一種說謊)。
|
2026-08-12 14:44:38 +08:00 |
|
uncle6me-web
|
793a94ecb5
|
chore(worker-builds): 重編——#100 的修法要進執行檔才會送到用戶那台
cypher-executor source 525faaf → a5e4caf(sha 49d59597 → d1765930)。
今天第三次在同一件事上被提醒:改完源碼沒重編,出貨與安裝送出去的還是舊的。
|
2026-08-12 13:36:39 +08:00 |
|
uncle6me-web
|
cbeddf7535
|
merge: 讀不到就說讀不到——總圖不再把「讀不到」畫成「你沒有」(Arcrun#100)
總管審過並自己跑了驗證:
cypher-executor 374 pass(原 357,+17 條),14 個失敗與 main 基準線完全相同
反向驗證:拿掉帶認證那一行 ⇒ 14 → 17 failed;還原 ⇒ 回到 14
它挖到兩件我沒交辦、而且比原題更嚴重的:
· /triplets/stats 的 total 是**分頁長度不是總數**(預設 limit=100)
⇒ 只修 401 的話畫面會從「0」變成「100」——一樣是假的
· 數 entries 原本不帶 owner 過濾 ⇒ **會混到別的租戶**(實測 459,137 vs leo 的 458,732)
|
2026-08-12 13:36:17 +08:00 |
|
uncle6me-web
|
d7c6bd0680
|
merge: 更新不再照名字找資源——已部署 worker 綁著什麼就是什麼(Arcrun#97)
總管審過並自己跑了驗證(那條線被權限閘擋住,沒能執行):
新測試 20 條全過(含四種「說不準就停手」情境)|CLI 全套 38 pass / 0 fail
反向驗證:把「照名字 ensure」原語加回去 ⇒ 紅線測試當場變紅(0 pass / 1 fail)
重編 dist:舊的 ensureKvNamespace/ensureD1Database 在產物裡 0 個
它做的比交辦的多:加了兩條紅線測試(cf-api 不得再提供「找不到同名就順手建一顆」的原語、
只有 resource-resolver 能決定要不要建),以及一條我沒想到的——
**部署出去的 toml 不得殘留官方帳號的資源 id**(自架寫進官方庫=跨租戶外洩)。
誠實標記:驗證是本機模擬(假 CF API),**沒有在真機上跑過**。
|
2026-08-12 13:36:17 +08:00 |
|
uncle6me-web
|
a5e4caf5cb
|
fix(portal): 讀不到就說讀不到——總圖不再把「讀不到」畫成「你沒有」(Arcrun#100)
leo 2026-08-12 打開總圖看到:「0 個實體 · 0 條關聯/知識庫還沒有任何關聯——
上傳文件後 AI 會自動織網」。**而他庫裡有 1854 條三元組。**
那句話會叫他去做一件不需要做的事。
三個獨立的洞疊起來才變成那句謊:
① console-dashboard.ts 打 kbdb-graph-plugin 沒帶認證(同段落打 kbdb 的兩支都有帶)
② 那顆 worker 不在更新的部署清單裡 ⇒ token 一換它就落單
③ **畫面把 null 畫成 0**——後端已經誠實回 null 了,是前端把它變成謊話
為什麼一直沒被發現:graph plugin 原本身上沒有 token ⇒ 門開著 ⇒ 沒帶也進得去。
2026-08-12 輪替後它有了 token,門關上,401 才浮出來。
📍 repo:matrix/arcrun(cypher-executor/src/routes/、console-ui/public/)
📍 票:Leo/Arcrun#100
|
2026-08-12 13:33:53 +08:00 |
|
uncle6me-web
|
45a546a686
|
fix(cli): 更新不再照名字找資源——已部署 worker 綁著什麼就是什麼(Arcrun#97)
病根:更新會「確保」它需要的資源存在,而它是**照名字找**的。
使用者的資源是安裝器建的(`arcrun-rag-<x>-kv-webhooks`),更新找的是 `WEBHOOKS`
⇒ 找不到 ⇒ 新建一顆空的並綁上去。
2026-08-12 實撞(leo21c):一次例行更新後
KV 9 顆 → 18 顆、D1 1 顆 → 2 顆,worker 全綁到新建的空的
⇒ 工作流一支都看不到、portal 登出、總圖空的、80 把 recipe 解不出來
leo 原話:「leo21c 是掛掉的」。資料沒掉,但從他的角度就是東西全不見了。
修法方向:**已經部署上去的 worker 綁著什麼,那就是事實**——
名字是使用者那側的事,不是更新指令可以決定的。
📍 repo:matrix/arcrun(cli/)|📍 票:Leo/Arcrun#97
|
2026-08-12 13:30:17 +08:00 |
|
uncle6me-web
|
e69d6bbc03
|
chore(worker-builds): 重編——kbdb 的跳脫修法要進執行檔才算數(Arcrun#94)
今天已經在這件事上被咬過一次(8e10f1d):源碼改了、執行檔沒重編,
出貨會全綠地送出舊的。這次併完就重編,不留同一個坑。
kbdb source 525faaf → c497ec4(sha256 7bc66656 → ffb8d434)。
其餘四顆源碼沒動、位元組也沒動。
|
2026-08-12 12:22:10 +08:00 |
|
uncle6me-web
|
b302c03ea8
|
merge: 搜尋框打的 % 和 _ 是字,不是萬用字元(Arcrun#94)
總管逐筆審過兩筆,並自己跑了測試(那條線被權限閘擋住沒能執行):
前後對照 查 100% 改前撈回「共有 100 個待辦項目」→ 改後只回真的含 100% 的
查 owner_id 改前連 ownerXid 也中 → 改後只回字面命中
單打 % 或 _ 改前整個庫都回來 → 改後只回字面命中
反向驗證 拿掉跳脫 → 8 failed(紅的正是病徵本身)
拿掉 ESCAPE → 5 failed(含既有 search-long-query 兩條)
既有測試 18 檔 197 pass → 19 檔 213 pass
它連帶處理了一個我沒想到的:跳脫會讓 pattern 變長,所以長度上限改用「跳脫後」的
長度算——否則打 48 個 % 會產生 98 bytes 的 pattern,退回 08-03 修掉的那個 HTTP 500。
誠實標記:只在本機真 SQLite 上驗過,沒對任何線上實例跑過。
同款漏洞另有兩處未動(library-map.ts:169、library-backfill.ts:75-76,吃維運端前綴參數)。
|
2026-08-12 12:21:48 +08:00 |
|
uncle6me-web
|
c497ec418e
|
test(kbdb): 用真 SQLite 釘住 #94 的前後對照與跳脫邊界
只驗 SQL 形狀不算數——「% 被當成萬用字元」這件事,只有真的跑一次 LIKE 才看得見。
用 node:sqlite(與 library-map/embed-backfill 同款治具)跑真 SQL,每組驗收同時跑
「舊寫法」與「現行寫法」,讓前後對照直接長在測試裡。
守五件事:
① 前後對照 —— 100% / owner_id / 單打符號,改前撈回不相干的、改後只回字面命中
② 跳脫邊界 —— `\` 不跳脫會漏掉真正的 `C:\`,也會讓 `_` 的跳脫失效、萬用字元漏回來
③ ESCAPE 齊全 —— 沒有任何 `content LIKE ?` 是裸的(漏一個就等於那條路沒修)
④ 不退化 —— 不含 % _ \ 的查詢 pattern 逐字不變,最熱路徑仍是單一 LIKE
⑤ byte 預算 —— 滿是 % 的長查詢仍在 D1 的 50 bytes 上限內(承 2026-08-03 的 500 修復)
反向驗證(把修法拿掉,兩半分別驗,兩半都是承重的):
拿掉跳脫 → 8 failed / 204 passed,紅的正是病徵本身
拿掉 ESCAPE → 5 failed / 207 passed(含既有 search-long-query 2 條)
復原後 → 19 檔 213 pass(基線 18 檔 197 pass)
|
2026-08-12 11:59:36 +08:00 |
|
uncle6me-web
|
6985bf4850
|
fix(kbdb): 搜尋框打的 % 和 _ 是字,不是萬用字元(Arcrun#94)
使用者在搜尋框打 `%` 或 `_`,搜出來一堆跟他打的字無關的東西。
病根:pattern 一直是 `'%' + 使用者輸入 + '%'` 直接內插,而 SQLite 的 LIKE 有
兩個萬用字元(`%` 任意長度、`_` 任意一字)且**沒有預設跳脫字元**——不寫
ESCAPE 就沒有任何辦法表示「字面上的 %」。所以他打的符號被當成 pattern 語法:
`100%` → `%100%%` → 「100」後面接什麼都算 ⇒ 撈回一堆不相干的
`owner_id` → `%owner_id%` → `_` 匹配任一字元 ⇒ ownerXid 也中
單打 `%`/`_` → `%%%`/`%_%` → 整個庫都回來
舊病,不是 08-10 斷詞(#84)引進的:pattern 從來就是這樣拼的。之前關鍵字搜尋
幾乎恆為 0 命中,這個洞被那個洞蓋住;斷詞讓搜尋真的會回東西之後才浮出來。
斷詞那段一個字都沒動。
修法:pattern 產生點全部收斂到兩支 helper——
· escapeLikeLiteral():跳脫 `%` `_` `\` 三個字元
· CONTENT_LIKE 常數:每個 `content LIKE ?` 一律帶 `ESCAPE '\'`
為什麼跳脫字元本身(`\`)也要處理:宣告 ESCAPE 之後 `\` 就變成 pattern 裡有
意義的字元,而且它會把後面那個字吃掉、且不報錯——打 `C:\` 會變成去找 `C:%`
(真正的 `C:\` 反而漏掉);只跳脫 %/_ 而漏掉 `\`,打 `C:\_temp` 時 `\\` 先被
讀成字面 `\`、後面的 `_` 變回萬用字元 ⇒ 撈到 `C:\Xtemp`。三個是一組的。
`[` `]` `?` `*` 不需要跳脫(那是 GLOB/別的方言,LIKE 不吃),多跳只會白白吃掉
pattern 的 byte 預算。
連帶:跳脫會變長(`%`→`\%`),所以 50 bytes 上限改用「跳脫後」的長度算
(likeBytes),否則打 48 個 `%` 會產生 98 bytes 的 pattern ⇒ 退回 2026-08-03
修掉的那個 HTTP 500。不含這三個字元的查詢 likeBytes ≡ utf8Len ⇒ 既有查詢逐字不變。
search-long-query.test.ts 兩條「逐字比對謂詞字串」的斷言跟著新字串更新——改的是
比對用的常數、不是放寬檢查(仍逐字相等比對),pattern 本身一個字都沒變。
沒有引進第三種搜尋機制,沒有動資料層(三表不變),沒有動斷詞。
|
2026-08-12 11:59:36 +08:00 |
|
uncle6me-web
|
8e10f1d83e
|
chore(worker-builds): 重編官方成品——kbdb/cypher-executor 的修法之前只在源碼裡
為什麼要有這一筆:`.worker-builds/` 是**出貨與安裝真正拿去部署的執行檔**,
而它記的 source_commit 在剛併完 #85/#88 之後對不上源碼:
arcrun-cypher-executor 成品記的 797e7f7 源碼已經是 525faaf ⚠️
arcrun-kbdb 成品記的 a7e23ba 源碼已經是 3eb8b31 ⚠️
⇒ 這時候去出貨或 `init`,送出去的是**舊的執行檔**,測試全綠也沒用
(wiki/mistakes.md「修的東西在執行檔裡,改完源碼+測試綠 ≠ 送到用戶手上」)。
🔴 build-bundles.mjs 的「落後閘」抓不到這個:它比的是「.worker-builds 這個目錄有沒有
落後 main 的 commit」,不是「成品記的 source_commit 有沒有落後那顆 worker 的源碼」。
這次是人工比對才發現的。修這道閘另開票。
重編後:cypher-executor source=525faaf、kbdb source=3eb8b31,兩顆的新程式碼都在產物裡
(kbdb 找得到 embed_backfill_usage/backfillEntryLibraryTags,cypher 找得到 builtin 那條路)。
誠實標記:另外三顆(code/http_request/mcp)源碼沒動,位元組卻變了——是 esbuild
版本漂移換了它產生的 helper(`__esm` 多了一段 try/catch)。這正是 #77 記的
「build 不可跨機器重現」那個結構性斷點的實例。
|
2026-08-12 09:50:50 +08:00 |
|
uncle6me-web
|
3eb8b31f2b
|
merge: 補算向量的節奏、額度、世代核對+標庫共用同一顆閘(Arcrun#85/D68/D69)
總管逐筆審過 1d6dde4/674e1b4/37e13fc:全部落在 kbdb/,沒有新表(額度用量寄生在
既有 entries 表單一列,D38)、沒有動實例。標庫與時間分層共用一套 SelectionCriteria
與同一顆每日 D1 額度計數器——兩者若各記各的,其中一個會把另一個的閘繞過去。
實測:kbdb 全套 196 pass / 0 fail(含反向驗證:拿掉 cap 那個 case 會變紅)。
紅線仍在:這只是併進 main,還沒部署到任何實例。
|
2026-08-12 09:40:30 +08:00 |
|
uncle6me-web
|
8cee9c9f76
|
merge: /cypher/search 不再誤報執行期原生零件 not_found(Arcrun#88)
總管逐筆審過 525faaf:改動只在 cypher-executor 的 search-nodes.ts(查 registry 前先比對
執行期白名單)與 component-loader.ts(把既有三份白名單的聯集匯出),沒有新清單、
沒有掃目錄灌死碼。實測基準線:本分支 357 pass / 14 fail,main 350 pass / 14 fail
——同樣的 14 個既有失敗(portal-data 等 5 檔),新增的 7 個全綠。
|
2026-08-12 09:40:18 +08:00 |
|
uncle6me-web
|
7e3ca4c1a1
|
Revert "WIP(kbdb): 語意搜尋零命中的排查——⚠️ 被總管中途叫停,未完成驗證"
This reverts commit af3edff856.
|
2026-08-11 23:18:38 +08:00 |
|
uncle6me-web
|
af3edff856
|
WIP(kbdb): 語意搜尋零命中的排查——⚠️ 被總管中途叫停,未完成驗證
leo 2026-08-11 判斷「如果是 Vectorize 沒完成就不用查了」,總管據此停線。
真因已經寫在 repo 自己的註解裡(kbdb/wrangler.toml:43-51,Arcrun#11):
metadata index 只收「建立後 upsert」的向量,既有向量須 reindex,
否則帶 owner_id filter 一律 0 命中——與實測每一格吻合
(805 筆在、關鍵字搜得到、語意 0、拿自己查自己也 0 ⇒ 不是分數門檻)。
⚠️ 這批改動是排查途中的產物,**沒有走完驗證**,不要當成可用的修法。
保留只是不讓它憑空消失(總管中斷造成,不是它做壞)。
接手的人請先讀 Arcrun#85 上的結論再決定要不要用。
真正的補救是 reindex,而 reindex 要燒 AI 額度
⇒ 卡在 Arcrun#85 的每日額度閘上線之後才能做。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 23:05:20 +08:00 |
|
uncle6me-web
|
c5d696556e
|
保管:#89/#90/#91 卡在人類閘前的產物,從 session 暫存目錄搶進版控
三樣都做完並實測過,但落地的最後一步是終端機裡等人親手打字的互動閘。
它們原本只存在於某個 session 的 scratchpad——那種目錄一關就沒了。
· recipes/gitea_put_file.yaml 出貨線 7 站等它
· recipes/cf_worker_deploy_simple.yaml ⚠️ 只適用無 bindings 的簡單情形(見 #90)
· hash-component/ sha256/sha1/md5,已與系統原生指令逐位元核對
(.wasm 是 1.3MB 編譯產物,不進版控,README 附重編指令)
README 寫了落地指令與各自的注意事項。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 22:38:55 +08:00 |
|
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 |
|
uncle6me-web
|
525faaf5d0
|
fix(cypher-executor): /cypher/search 不再誤報執行期原生零件 not_found(Arcrun#88)
病因:/cypher/search 只查 component registry(SUBMISSIONS_KV,經 submitComponent/
index-only 才有記錄);而 component-loader.ts 能直接解析、從不查 registry 的一整類
零件(trigger_workflow/BUILTIN_COMPONENTS/LOGIC_BINDING_MAP/WASM_HTTP_RUNNER_IDS,
如 if_control/http_request/switch)從未被 submit 過。leo21c 實例實測:
GET /components/catalog 回 404「零件 catalog 不存在」,search 因此對這些零件誠實地
回「兩庫都查過沒有」——但它們其實跑得動(leo 08-11 探測工作流已證)。
修法:從 component-loader.ts 匯出 RUNTIME_NATIVE_COMPONENT_IDS(既有三份執行期
解析白名單的聯集,非新清單),search-nodes.ts 在查 registry 之前先比對,
不受 registry 是否可達/是否已 backfill 影響。真正不存在的零件仍誠實回
not_found/unknown,not_found 的分型建議與相似候選機制不變。
刻意不做:不掃描 registry/components/* 目錄當清單來源——那含已標記待刪的死碼
(km_writer/kbdb_upsert_block),07-30 曾把這類死碼誤灌進 registry;也不投資
SUBMISSIONS_KV 的 backfill 腳本——decisions-summary.md D29 已定調 SUBMISSIONS_KV
併入「KV 退休戰」,不宜再加投資。
測試:cypher-executor/tests/search-nodes-runtime-native.test.ts 7 case 全綠,
複現 registry unreachable/registry 可達但目錄空(leo21c 實例的真實症狀)兩種情境。
全 suite 迴歸:357 pass(較修前 350 pass 多 7 個新測試),既有 14 個失敗與修前
數量、內容完全相同(pre-existing,與本次改動無關)。
|
2026-08-11 21:51:21 +08:00 |
|
uncle6me-web
|
37e13fc9bf
|
merge: 併入 main 最新(b6ef0f0 三元組 library 補標 proxy+測試)保持分支不落後
|
2026-08-11 18:46:03 +08:00 |
|
uncle6me-web
|
674e1b4fa2
|
fix(kbdb): D69 節流世代核對+標庫共用 D1 額度,補「挑哪一批」的統一 SelectionCriteria(Arcrun#85)
leo 逐行複核 fix/embed-backfill-d68 後點出的破口+二次裁決(票上全文見 Leo/Arcrun#85):
一、向量化優先序(今天寫的立刻/本週在跑的先跑/有查詢紀錄的庫優先/半年前慢慢跑)表達
不出來——策略要能從外面(工作流)指定,不能焊死在資料層。新增 embed.ts 的
`SelectionCriteria`(owner_id/source/library/since/until),backfillEmbeddings 與
reconcileEmbedGeneration 共用同一套形狀;「按庫」那一層現在有資料可用即可運作(見下)。
二、世代核對(reconcileEmbedGeneration)不打 AI 但逐筆寫 D1,47 萬筆候選 ≈ 4.7 倍 D1
100,000 rows/日免費額度,先前零保護。新增 actions/maintenance-quota.ts(單一 entries
列/日的共用計數器,精神同 execution-log.ts/embed.ts 既有慣例,不新增表)。
三、leo 二度裁決:「標庫」與「時間分層」其實是一件事,判定標準要從第一天同時容納兩者,
不能先做一半再回頭改。新增 actions/library-backfill.ts 的 backfillEntryLibraryTags——
呼叫端(ingest/daemon/Arcrun#87)決定要貼哪個庫、用 page_names(Gitea 原稿卡名精準
點名,leo 定案的正解)或 source_prefix/page_name_prefix 過渡 fallback 篩選候選,base
只負責安全、節流地寫入。owner_id 刻意必填(leo 點出「補錯 owner 等於白做」——實查卡片
掛在 owner_id=bfezv28v,換成 'leo' 查卻是空的)。
D69:reconcile 與標庫 backfill 共用同一顆「今天還剩多少 D1 寫入額度」計數器(不共用的話
其中一個會把另一個的閘繞過去);新增 POST /entries/backfill-library + GET .../status,
擴充 POST /embed/backfill 與 /embed/reconcile 吃 library/since/until 參數。
測試:92 → 39 個新增/擴充案例覆蓋 since/until/library 篩選、reconcile 額度真的擋
(含「拿掉 cap 會變紅」反向驗證)、標庫 backfill 冪等/owner_id 必填/page_names 精準比對、
以及兩個操作共用同一顆額度計數器的跨模組驗證(雙向:先 reconcile 耗盡再標庫、反之亦然)。
kbdb 全套 192 個測試綠燈,tsc --noEmit 除既有 auth.test.ts 舊缺陷外無新增錯誤。
紅線:未併 main、未部署、未動任何實例的 is_embedded 旗標(只在本地 SQLite 測試治具跑過)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 18:39:55 +08:00 |
|
uncle6me-web
|
b6ef0f07dc
|
fix(kbdb): 開通 PATCH /kbdb/records/:id proxy + 補三元組 library 補標測試
leo 2026-08-11:貼 wiki 卡「這裏就有 3 個三元組,如果三元組是 0 一定是有錯」——
三元組沒有不見(新式 1,633 筆/舊式 636 筆都還在),問題是地圖看不到:新式三元組
只有 171 筆填了 library slot,其餘因寫入時 template 還沒有該 slot 而被靜默丟棄
(record-crud.ts createRecord 只替「已宣告」的 slot 建值,不是替 caller 傳的
values 建值——順序錯了值就消失,不報錯)。
本次改動:
- cypher-executor/src/routes/kbdb-proxy.ts:補 PATCH /kbdb/records/:recordId。
基本盤(kbdb/src/routes/records.ts)早有這個端點(mira-dissolve T2.1 的
updateRecord),但這條 proxy 之前只轉發 GET/POST /kbdb/records,外部(工作流/
CLI/補標腳本)打不到——能力在,通道沒開,補標三元組只能繞去改表(違 D38)。
純轉發、無業務邏輯,比照既有 PATCH /kbdb/entries/:id 慣例。
- kbdb/tests/triplet-library-backfill.test.ts:真 node:sqlite 驗三件事——
①源頭順序(ensure-slot 必須在 write 之前,事後補救不了已寫的那筆,重現+
驗證正解)②存量補標(地圖輸出前後對照)③不會重複做(同批跑兩次,第二次
touch 0 筆)。順手記錄一個相關但不在本次範圍的小落差:liveTripletCountsByLibrary
的 'general' fallback 桶與 recomputeLibraryMap 的精確比對語意沒對齊。
- cypher-executor/tests/kbdb-records-patch-proxy.test.ts:新端點的租戶閘/
參數驗證/轉發/404 透傳。
全數綠燈:kbdb 181/181、cypher-executor 新增 8 案全過(既有 14 案失敗為
pre-existing,已用 git stash 比對確認與本次改動無關)。
kbdb-graph-plugin(實際的三元組寫入端 createTriplet/ingestEnvelope)需要同款
修正(library 補進 TRIPLET_SLOTS + 從 source_uri 推導),但該 repo 現行
0 份 active SDD,其 sdd-guard 明確要求人決定要不要開啟——本次不越界代為決定,
留在報告中交回。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 16:48:31 +08:00 |
|
uncle6me-web
|
1d6dde4a01
|
fix(kbdb/embed): D68 補算向量照新到舊排序+每日額度上限+修復舊世代 is_embedded 誤判
leo 2026-08-11 拍板(D68,system-dev/wiki/decisions-summary.md):補算向量要照時間
由新到舊、且每天有額度上限,不能一次把 Workers AI 每日免費 10,000 neurons 燒光
(與萃取共用同一份額度,見 ops-facts.md)。對應 Leo/Arcrun#85 列出的三個缺口:
① 補算是由舊到新(ORDER BY created_at ASC)② 沒有每日額度上限 ③ 沒有任何自動觸發。
改動:
- backfillEmbeddings:ORDER BY created_at DESC(新到舊),並在打 AI 前依
env.EMBED_BACKFILL_DAILY_LIMIT(未設用推導出的預設值 1800,算式見 embed.ts 註解)
截斷候選、額度用完即停手不再打 AI。額度用量存在 entries 表單一列
(entry_type='embed_backfill_usage',UTC 日期切),不新增表(D38)。
- embedOnWrite / backfillEmbeddings 成功嵌入後在既有 content_hash 欄位蓋上
現行模型名(世代戳記),修復 leo21c 資料還原案:從備份整批灌回的列帶著對已退役
768 維索引的 is_embedded=1,現行 1024 維索引永遠不會補到它們。
- 新增 reconcileEmbedGeneration + POST /embed/reconcile:對 is_embedded=1 但
content_hash 非現行世代的候選,問 Vectorize.getByIds 是否真的在現行 index——
在→只補 content_hash 不打 AI;不在→重置 is_embedded=0 交回正常 backfill 佇列。
- 新增 kbdb/tests/embed-backfill.test.ts(改走真 SQLite,比舊版手刻假 DB 更硬):
14 個測試涵蓋新到舊排序、額度真的擋(含「拿掉 cap 會變紅」的反向驗證)、
世代核對端到端(reconcile → 重置 → backfill 真的補回來)、既有行為不迴歸。
現況誠實回報:目前沒有任何東西會自動觸發補算(無 cron/scheduled handler)——
唯一的「自動」路徑是 entries.ts 的語意搜尋回 0 命中時 fire-and-forget 觸發一次
(既有行為,本次未改動),仍需人或 CC 主動呼叫 /embed/backfill 或掛排程。
紅線:未動 leo 正式實例 leo21c;未動資料層形狀(三表不變,仍走既有 content_hash
bookkeeping 欄);未 push main,本 commit 在獨立分支 fix/embed-backfill-d68。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 16:42:54 +08:00 |
|
uncle6me-web
|
507620e313
|
merge: Arcrun 統一編譯零件,成品放固定位置 .worker-builds/(Arcrun#80)
總管 2026-08-11 審過後併入:
- 照本 repo 既有慣例(.component-builds 底下的 wasm 本來就 commit 進來),不新造第二套
- 每顆成品自帶 source_commit + content_sha256 ⇒ 答得出「我是哪個版本編出來的」到單顆層級
- 重現性已驗:兩個不同路徑深度的獨立 clone 分別裝依賴分別編譯,
5 顆的 content_sha256 與 wasm sha256 逐位元相同(manifest 只差 generated_at)
⇒ 這正是 arcrun-rag#39 的驗收條件,也是 #72 那個死結的解
待觀察(不擋本次):每次編譯會 commit 數萬行成品進原始碼 repo,長期會脹。
|
2026-08-11 13:54:19 +08:00 |
|
Leo
|
1e94f8451e
|
Arcrun#80: commit tier2 worker 官方編譯成品到固定位置 .worker-builds/
從本 repo 的 cypher-executor / kbdb / .component-builds/http_request /
registry/components/code / mcp 五個原始碼目錄,用 scripts/build-worker-artifacts.mjs
編出 5 顆成品,commit 進 repo(與既有 .component-builds/*/component.wasm 同一套
「固定位置、任何人直接拿」慣例)。
每顆 manifest 條目自帶 source_commit(該原始碼目錄最後改動的 commit)與
content_sha256,答得出「我是哪個版本的原始碼編出來的」到單顆層級。
重現性已驗證(見 commit 說明外的驗證記錄):同一個 commit 在兩個獨立 clone
(不同磁碟路徑深度,刻意模擬雲端 vs 地端的目錄結構差異)分別裝依賴、分別編譯,
5 顆的 content_sha256 與 wasm module sha256 逐位元相同,manifest.json 唯一差異
是 generated_at 時間戳。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 13:45:33 +08:00 |
|
Leo
|
dcb6ad693b
|
fix(build-worker-artifacts): dirty 判斷排除自己的輸出目錄
.worker-builds/ 是本腳本的輸出,在成品寫出、commit 之前永遠是 untracked——
拿它判斷「原始碼乾不乾淨」是自己把自己判成髒的假陽性。git status pathspec
排除 .worker-builds 後才是「原始碼有沒有未 commit 變更」的真實訊號。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 13:45:33 +08:00 |
|
Leo
|
3b0238bb28
|
Arcrun#80: 新增 tier2 worker 官方編譯腳本(成品固定位置的地基)
背景:安裝器(arcrun-rag)過去自己對 Arcrun 原始碼跑 esbuild,導致同一份原始碼在
不同機器編出不同位元組(見 arcrun-rag changelog 1.4.33 段、arcrun-rag#72):
① esbuild 把入口路徑寫進產物內部註解,該路徑預設相依於執行時 cwd
② 各 worker 目錄用不同套件管理器裝 node_modules,夾帶不同版本間接依賴
本腳本解法:
- absWorkingDir 固定為「本腳本自己算出的 repo 根目錄」,entry 一律用相對路徑餵給
esbuild——不管 clone 放在磁碟哪個絕對路徑,esbuild 內部產生的相對路徑字串相同
- 不自己跑 install,強制要求呼叫者先用該目錄既有 lockfile 裝好依賴(pnpm frozen /
npm ci),避免「install 方式不同 → 依賴版本不同 → 位元組不同」
- 每顆 worker 的 manifest entry 自帶 source_commit(該目錄最後改動的 commit),
答到單顆層級,不是整包一個 source 欄位
涵蓋 5 顆 tier2 worker(與 arcrun-rag bundle-components.mjs CORE_COMPONENTS 對齊):
cypher-executor / kbdb / http_request / code / mcp。
scripts/ 自帶 package.json + pnpm-lock.yaml 鎖 esbuild 版本(0.24.0)——
工具版本本身也是重現性的輸入之一。
驗證:本地跑通 5/5 build;byte-identical 重現性驗證見後續 commit(兩個獨立 clone
比對 sha256)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-11 13:45:33 +08:00 |
|
uncle6me-web
|
788295ed71
|
merge: 忘記密碼的錯誤訊息改白話+健康檢查回報寄信有沒有接上(arcrun-rag#38/#69/#25)
總管 2026-08-11 審過後併入:
- 拿掉行話「實例」;拿掉「請管理員直接幫你改密碼」(單人使用者就是管理員=死路,#25 同病)
- 改成給一條他自己走得完的路:重新跑一次安裝/更新
- /health 增回報 mail_relay_configured,讓安裝器判斷得出這台要不要重推
|
2026-08-11 13:24:34 +08:00 |
|
uncle6me-web
|
797e7f751c
|
fix(portal): 忘記密碼斷點——安裝器從沒注入 PORTAL_MAIL_RELAY_BASE(arcrun-rag#38/#69/#25)
leo 被鎖在自己的系統外面:1.4.35 已出 prod,「忘記密碼」畫面在線上,但按下去回
503「這台實例還沒有設定寄信服務」。根因在 products/arcrun-rag 的安裝器——
`grep -rn PORTAL_MAIL_RELAY installer/` 是零命中,即使 landing 那半(郵差)
D62 已經寫好。這個 repo 這邊改兩件配合修:
- /health 多回 `mail_relay_configured`(布林,不洩漏網址)——安裝器判斷「要不要
重推」只比 bundle_version,但這個 var 是這次才第一次被注入,跟版本號無關;
純比版本號的話,已經在最新版的實例(如 leo 那台)永遠不會因為「按更新」而
重推,這個 var 永遠補不進去。安裝器那邊會讀這個欄位決定要不要強制重推。
- portal.ts 的 503 訊息改白話(不提「實例」)+給一條使用者自己走得完的路
(重新執行安裝/更新),不再說「請管理員幫你改密碼」——單人使用者的管理員
就是他自己,那是死路(同源病灶:arcrun-rag#25)。
安裝器那半的修法(PORTAL_MAIL_RELAY_BASE 注入+stale 判斷)在
products/arcrun-rag 同批修(installer/oauth-prototype/worker.js)。
測試:cypher-executor `npx vitest run tests/portal-auth.test.ts tests/health.test.ts`
37/37 綠(35+2,既有測試無回歸;本次未新增測試——改動是訊息文案與健康檢查欄位,
行為已由既有測試覆蓋的路徑保護)。
|
2026-08-11 12:59:57 +08:00 |
|
uncle6me-web
|
d1c44a5878
|
merge: 忘記密碼與改密碼合成同一個機制(D62,arcrun-rag#69/#25)
總管 2026-08-11 逐筆審過後併入:
- 三筆:8d49d88 機制/9a29eb5 瀏覽器實測抓到的兩個前端缺陷/c76e10d 回歸測試
- #66 的修正 417d69c 在本分支血緣裡,併入不會弄丟已出貨的行為
- 試併零衝突;併入後 cypher-executor 測試 360 項、通過 346
(14 個失敗與 main 併入前完全相同,是既有紅燈,非本次造成)
- 併入後多出的 8 項全數通過=D62 的回歸測試
⚠️ 併入 main ≠ 出貨。要送到用戶手上仍需走出貨管線並由 leo 解保險。
|
2026-08-11 11:14:13 +08:00 |
|
uncle6me-web
|
a7e23badf2
|
WIP(kbdb): 關鍵字搜尋改成斷詞——⚠️ 未完成驗證,agent 被中途停止
【為什麼要這個】leo 2026-08-10:「沒有 MCP 你就是瞎的」。
總管實測(有對照組)證明搜尋對 AI 結構上不可用:
kbdb_search("Gemini 逃生口") → 0 筆 ← 兩個詞從不相鄰
kbdb_search("Gemini") → 864 行 ← 知識明明在
kbdb_search("local arcrun") → 5 筆 ← 這兩字在內容裡剛好相鄰
⇒ 現況是拿整個查詢字串去 LIKE,不拆詞。而 AI 問的永遠是問句 ⇒ 永遠回 0。
【這筆做了什麼】CJK/英數分段切詞、CJK 與 ASCII 停用詞、
單詞份量上限(避免長詞獨大)、相對分數截斷;附 search-tokenize.test.ts。
🔴 【誠實聲明】**這筆沒有驗完就被停了**(leo 要求全體停工專心 MCP 進安裝清單)。
未做:前後對照的實際輸出、回歸(原本查得到的不能變查不到)、相關性不崩壞。
**接手的人不要當成已驗證的東西**,先跑那三項再說。
分支保留於 gitea,隨時可接回去。相關:Leo/mira#4 總管留言。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 763de199b5)
|
2026-08-11 10:02:09 +08:00 |
|
Leo
|
eebb691426
|
merge: 傳播空窗期不再銷毀 session(arcrun-rag#66)
leo 2026-08-10 本人被這個鎖在 stage 外面,並靠修好後的重設流程走回來——
這是「收的人自己驗到」,不是我們自己測過。
病:認證的家是 Workers Secret,改它會產生 worker 新版本,既有 isolate 讀到舊 env。
舊碼在那個空窗裡讀不到 record 就直接 SESSIONS_KV.delete()
⇒ 帶著完全有效的 token,登入狀態被當場銷毀,secret 鋪開也回不來。
#55 補的加速器重試只加在登入路徑,session 驗證這道門沒有。
實測空窗約 45 秒(不是 #55 記的 ≥15 秒):
改完密碼後舊密碼一路到 t+44.1s 仍登得進去,t+47.1s 才開始 401。
三段修法:
1. 先問加速器再判定(與登入路徑同一支 hydrateFromAccelerator)
2. 永不因「讀不到」刪 session——刪是 best-effort 清潔工,清掉的卻是使用者唯一的憑據
3. 仍讀不到且在空窗 → 503 auth_store_propagating,不是 401
(前端看到 401 就清 localStorage,後端不刪也沒用)
前端 boot() 收斂成「只有 401 才算被登出」。
驗證:stage 改密碼後同一 token 打 90 秒 → 200x30 / 401x0;瀏覽器實跑仍在站內;
tests/portal-auth.test.ts 27/27 綠。
|
2026-08-10 14:19:04 +00:00 |
|
Claude
|
c76e10d314
|
test(portal): 補 #66 與 D62 的回歸測試(8 條,35/35 綠)
#66(三條,斷言的是「session 有沒有被刪掉」而不只是狀態碼)
- 傳播空窗(加速器 key 還在)+讀不到 record → 503 auth_store_propagating,**KV 那筆還在**
- 非空窗+讀不到 record → 401 擋下,**KV 那筆仍然還在**(刪是清潔工,清掉的卻是唯一憑據)
- session 內容本身壞掉 → 401 且**該刪**(那是確定的事實,不是暫時讀不到)
⇒ 前兩條在舊碼上必紅:舊碼在這兩個情況都會 SESSIONS_KV.delete()
D62(五條)
- 帶 reset_token 呼叫 /portal/password/change:**不必登入、不必現有密碼**;
且「先刪再回」=同一條連結第二次必定 400 reset_token_invalid,KV 裡也真的沒了
- 先「看一眼」票(GET /portal/password/reset)不會消耗它
- 亂猜 / 格式不對的 token → 400
- 沒 reset_token 又沒登入 → 401(修改密碼那一格仍要身分)
- 沒設代寄服務 → /portal/password/forgot 誠實 503 mail_relay_not_configured(不假裝寄出去了)
擺放順序有註解說明:#66 那組會故意把 per-isolate overlay 灌成空的(模擬空窗),
而 overlay 是模組級全域變數不隨 test 重置,故必須排在檔案最後。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R8vF2zS2XpaZjzkC75Fjss
|
2026-08-10 13:55:53 +00:00 |
|
Claude
|
9a29eb5af6
|
fix(portal): 瀏覽器實測抓到的兩個前端缺陷——連結被路由吃掉、.hide 從來沒有在藏東西
兩個都是「HTTP 200 看不出來、真的開瀏覽器才會現形」的(規則五點五③)。
① `#/reset?token=…` 被路由正規化吃掉
route() 的第一行是「raw 不在 VIEWS 就換成 HOME」,而 reset 不是站內的 view
⇒ 使用者點信裡的連結,網址被改寫成 `#/search`、修改密碼畫面根本沒出現,
**而且 token 一起被丟掉**——連結一次有效,等於這條連結就這樣廢了。
改:route() 開頭先看有沒有 reset token,有就走 showReset 並 return。
(boot() 那條只顧得到「整頁重新載入」;hash 變動這條路以前沒人守。)
② `.hide` 一直只有三條有 scope 的規則(#tabbar .tab / #sidenav .nav / .modebtn)
沒有通用的 `.hide { display:none }` ⇒ 任何其他元素掛上 class="hide" **完全沒被藏起來**,
看起來有藏、其實沒藏。實測畫面:登入頁還沒按「忘記密碼」就已經露出 email 欄與「寄出連結」;
連結模式下「現有密碼」那一格也照樣顯示(D62 的「差別只有一格」當場破功)。
補通用規則,帶 !important 以蓋過 inline display(#forgot-box 有)。
既有三種用法意圖一致(都是要藏),行為不變。
stage 瀏覽器實測(youlin,playwright):忘記密碼 → 取連結 → 點進去 →
不輸入現有密碼設新密碼 → 自動回登入頁 → 用新密碼登入成功 → 進站 →
設定頁同一份表單(現有密碼那一格回來)改密碼 → **沒有被踢出去、可以繼續操作**。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R8vF2zS2XpaZjzkC75Fjss
|
2026-08-10 13:51:41 +00:00 |
|
Claude
|
8d49d883c0
|
feat(portal): 改密碼與忘記密碼合成同一個機制,用連結不用一次性密碼(D62)
leo 2026-08-10 拍板:「這兩個機制其實是一個機制,可以簡化。」
修改密碼:輸入**現有的** → 輸入新的 → 覆蓋
忘記密碼:收到**「修改密碼」連結** → **不輸入現有密碼(忽略)** → 輸入新的 → 覆蓋
⇒ 同一個畫面、同一條寫入路徑,差別只有「現有密碼」那一格。
後端(cypher-executor/src/routes/portal.ts)
- POST /portal/password/change =**那一支**。帶 reset_token 就走連結那一格(忽略 current),
沒帶就要登入 + 正確的 current。兩條路在 writeNewPassword 之後完全相同。
/portal/me/password 保留成**別名轉呼同一支**(不留第二份實作,兩份必然漂移)。
- POST /portal/password/forgot(公開)/GET /portal/password/reset(看票,不消耗)
- 連結的安全性(承 D50,不可退讓):**一次有效**(用掉即刪,先刪再回)、
**會過期**(KV TTL 30 分鐘)、**與註冊辨識碼不同源**(現場 crypto 亂數,
只活在本實例 SESSIONS_KV,與 landing SIGNUPS 那組安裝辨識碼毫無關係)。
KV 存的是 token 的 sha256,不是 token 本身。
- 🔴 不做一次性密碼(leo:「不要發一次性密碼太麻煩」)。
- 🔴 已否決不准寫回來的三條(D50):console 密碼救援/重裝重設密碼/直接用固定辨識碼。
中央代寄(arcrun-rag landing 那半在該 repo)
leo 給的職責切法:實例產生連結、管一次性/過期;arcrun.dev 只是郵差。
必須這樣切的硬理由:用戶自己的實例**沒有 send_email binding**,根本寄不了信。
🔴 總管紅線:**絕不把整條 URL 交給郵差**——寄件網域帶 DKIM,肯收「任意 URL+任意 email」
就是一台開放的釣魚中繼,燒的是整個網域信譽、不可逆。
故只交出本實例 origin + 一張回呼票,並新增 POST /portal/password/relay-verify:
郵差**回頭打這個 origin** 問「這張票是你發的嗎」,冒用別人網域會被那台實例自己否認
⇒ 主機屬於呼叫方這件事由郵差親自確認,不是相信宣稱。
信裡的連結落點 GET /portal/password/reset-link(主機刻意=被確認過的那個 origin)。
前端(console-ui/public/portal/index.html)
- 登入頁「忘記密碼」入口,**在 portal 不在 console**(leo:「是對 portal 不是對 console,
這樣 youlin 雖然忘記,我還是可以去 portal 忘記密碼。」)
- 拆掉登入頁原有的「用管理主控台密碼救援自己」連結與「忘記密碼請聯絡管理員」
——兩條都是 D50 已否決的做法(console 與 portal 是同一組帳密的兩個鑰匙孔)。
- #v-reset 殼**沒有自己的密碼欄位**:真正的表單是設定頁那唯一一份 #pw-form,
進入連結模式時被原封不動搬過去,只切換「現有密碼」那一格顯不顯示。
同一個表單元素、同一支送出函式 —— D62「同一個畫面」的字面落地。
驗證:tsc 與 baseline 同為 23 個既有錯誤(零新增);27/27 既有測試綠;前端 JS node --check 過。
stage 實測見交付回報。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R8vF2zS2XpaZjzkC75Fjss
|
2026-08-10 13:23:44 +00:00 |
|