5 Commits

Author SHA1 Message Date
Claude b53c5c94a9 chore(deps): 同步 pnpm-lock.yaml 與 package.json(清掉早已移除套件的殘留鎖檔項)
補跑第一步 clone 後 fresh install 就撞坑:pnpm-lock.yaml 裡鎖著
@modelcontextprotocol/sdk、unpdf、@cloudflare/vitest-pool-workers、fast-check
四個套件,但 package.json 早就沒宣告它們了(歷史 trim 沒同步鎖檔)。
另外先誤用 `npm install` 疊加在既有 pnpm 目錄結構上,產生指向
node_modules/.pnpm/... 的斷頭 symlink,導致 wrangler deploy esbuild
"Could not resolve hono" 系列錯誤 — 清掉重跑 `pnpm install` 後才正常。
記錄給 progress-guard:clone 後第一步務必用 pnpm install,不要用 npm。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 08:58:03 +00:00
Claude 454ea8d240 fix(kbdb-client): ensureTemplate 誤判「不存在」導致重複建 template → base 500
補跑實測(POST /triplets/ingest 端到端跑不過)發現的真契約漂移,非單純部署坑:
- ensureTemplate() 假設 GET /templates/:name 回傳 {id, slots} 平鋪陣列,
  但 base(arcrun-kbdb)實際回 {success, template:{id, slots_json:"[...]"}}
  (多包一層 + slots 是 JSON 字串)。existing.id 永遠 undefined → 一律走「不存在」
  分支重複 POST 同名 template → base 對重名衝突沒有優雅的 409,直接 500,
  ingest 端到端全斷。
- 改為解 existing.template + JSON.parse(slots_json),修好後 POST /triplets/ingest
  → GET /triplets 端到端打通(見部署驗證 curl 記錄)。

同時讓 KbdbClient 支援可選 fetcher(service binding 的 Fetcher),
搭配上一個 commit 的 KBDB_BASE_SVC binding 繞開 workers.dev 互連封鎖;
沒有 binding 時(本地 dev/mock)仍 fallback 回全域 fetch,不影響既有單元測試。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 08:57:53 +00:00
Claude c7d8a74bee fix(deploy): leo21c self-host 撞牆修復 — secret 被 vars 覆蓋 + 死 zone route + workers.dev 互連被擋
T-kb-skeleton①部署到 leo21c 補跑實測發現三坑:
1. [vars] 宣告 KBDB_BASE_URL="",wrangler deploy 會用它覆蓋掉已存在的 remote
   secret(vars 優先權 > secret),導致部署後 KBDB_BASE_URL 被打回空字串。移除該鍵,
   一律只透過 `wrangler secret put` 設定。
2. [[routes]] custom_domain "kbdb-graph.finally.click" 的 zone 不在 leo21c 帳號下
   (CF API GET /zones?name=finally.click 回空),會讓 deploy 該路由失敗。註解掉,
   self-hosted 預設乾淨部署到 workers.dev。
3. 最關鍵:Worker 直接 fetch() 另一個 *.workers.dev Worker 會被 CF 擋
   (error code 1042,workers.dev 共享 zone 的 loop-prevention)。self-hosted 帳號通常
   沒自訂域名可繞,正規解法是 Service Binding(CF 內部直連,不經公開網路)。
   新增 [[services]] binding=KBDB_BASE_SVC -> arcrun-kbdb。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 08:57:43 +00:00
Leo 01f131a7a2 docs(wiki): /wiki-update — 補 T3(PR#2) + 補對齊(PR#3) 歷史 + 1 mistake
main 的 status.md 之前停在 2026-06-14(PR#2 squash 只搬檔未帶 wiki 內容更新)。
補齊三段歷史 + 更新現狀(兩 PR 已 merge、本 repo 無剩餘 task、三項跨 repo 待接)。
mistakes 記:補對齊/功能 PR 別混 template 基建遷移(PR#3 撞衝突的教訓)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 20:35:34 +08:00
uncle6 13db97bb54 fix(ingest): receiver Zod 追上 contract — 補向量化打標欄位 (issue #1 補對齊) (#3)
契約漂移修補:T3 的 strict Zod 鏡射舊 contract,ingest 照新 contract(ingest#1
升格)送向量化打標欄位會被 .strict() 擋成 422。方向 A:顯式加合法新欄位、保留
strict。

- 同步 contracts/ingest-candidate.json 副本到頂層單一真相源(mira-dissolve)。
- NodeSchema 加 id?/aliases?/embed?;EdgeSchema 加 predicate_embed?。strict() 保留
  → bridge_score/clusters 等 graph 領域禁送欄位仍 422。
- 落地:predicate_embed 透傳進 triplet slot;node 打標(embed/gloss/aliases)存進
  entity slot,供 base/KBDB embed 模組讀標執行(graph 不算向量,鐵律一致)。
- id 作 node 去重鍵:同卡多邊指到只存一筆 entity。
- persistNodes 拆成獨立 action(triplet-ingest.ts 回到 95 行,守樂高 100 行限制)。
- 測試 +4:帶向量化欄位通過、bridge_score/clusters 仍 422、同 id 去重。
  vitest 23 passed。零 SQL / 無 D1·Vectorize·AI 綁定 / dry-run 乾淨。

Co-authored-by: richblack <leo21c@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 20:28:13 +08:00
6 changed files with 115 additions and 1708 deletions
-1686
View File
File diff suppressed because it is too large Load Diff
+32 -8
View File
@@ -36,6 +36,12 @@ export class KbdbClient {
constructor(
private readonly baseUrl: string,
private readonly token?: string,
// 2026-07-03 補跑實測發現的坑:Cloudflare 會擋 Worker → 另一個 *.workers.dev
// Worker 的直連 fetcherror code 1042loop-prevention on shared workers.dev zone)。
// self-hosted 帳號通常沒有自訂域名可用,正規解法 = Service Binding
// wrangler.toml `[[services]]`),由 CF 內部直接路由、不經公開網路。
// 有綁定時優先走它;沒有(例如本地 dev/mock)則 fallback 回全域 fetch。
private readonly fetcher?: { fetch: typeof fetch },
) {
if (!baseUrl) {
throw new Error('KBDB_BASE_URL 未設定:插件需指向基本盤 API(不可直連 D1)');
@@ -46,7 +52,8 @@ export class KbdbClient {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (this.token) headers['Authorization'] = `Bearer ${this.token}`;
const res = await fetch(this.baseUrl.replace(/\/$/, '') + path, {
const doFetch = this.fetcher ? this.fetcher.fetch.bind(this.fetcher) : fetch;
const res = await doFetch(this.baseUrl.replace(/\/$/, '') + path, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
@@ -113,23 +120,34 @@ export class KbdbClient {
// --- templates= 替代建表;插件要新類型只能建 template) ---
async ensureTemplate(name: string, slots: string[], description?: string): Promise<void> {
const existing = await this.req<{ id?: string; slots?: string[] } | { error: string }>(
// 2026-07-03 補跑實測發現的坑:base 的 GET /templates/:name 回傳是
// { success, template: { id, slots_json: "[...]" } }(包一層 + slots 是 JSON 字串),
// 不是原本假設的 { id, slots } 平鋪陣列。誤判「不存在」會導致對已存在的 name 重複
// POST /templates,而 base 對重名衝突沒有回優雅的 409,是直接 500(見 kbdb-graph 實測)。
const existing = await this.req<{ template?: { id: string; slots_json?: string } }>(
'GET',
`/templates/${encodeURIComponent(name)}`,
).catch(() => null);
const tpl = existing?.template;
// 全新 template → 建。
if (!existing || !(existing as any).id) {
if (!tpl || !tpl.id) {
await this.req('POST', '/templates', { name, slots, description, created_by: 'kbdb-graph' });
return;
}
// 既有 template → 補缺 slot(不 early-return;否則 seed 後新增的 slot 永遠進不來)。
// 走 base PATCH /templates/:id 增 slot;既有環境免另跑遷移腳本即收斂。
const have = new Set((existing as any).slots ?? []);
let haveList: string[] = [];
try {
haveList = JSON.parse(tpl.slots_json ?? '[]');
} catch {
haveList = [];
}
const have = new Set(haveList);
const missing = slots.filter((s) => !have.has(s));
if (missing.length === 0) return;
await this.req('PATCH', `/templates/${encodeURIComponent((existing as any).id)}`, {
await this.req('PATCH', `/templates/${encodeURIComponent(tpl.id)}`, {
slots: [...have, ...missing],
});
}
@@ -175,7 +193,13 @@ function qs(params: Record<string, string | number | undefined>): string {
return parts.length ? `?${parts.join('&')}` : '';
}
/** 從 Bindings 建 client。KBDB_BASE_URL 未設時拋錯(不准 fallback 直連 D1)。 */
export function makeKbdbClient(env: { KBDB_BASE_URL?: string; KBDB_INTERNAL_TOKEN?: string }): KbdbClient {
return new KbdbClient(env.KBDB_BASE_URL ?? '', env.KBDB_INTERNAL_TOKEN);
/** 從 Bindings 建 client。KBDB_BASE_URL 未設時拋錯(不准 fallback 直連 D1)。
* 有 KBDB_BASE_SVCservice binding)時優先走它,繞開 workers.dev→workers.dev 的
* CF error 1042 封鎖;沒有就退回全域 fetch(本地 dev / 已有自訂域名時仍可用)。 */
export function makeKbdbClient(env: {
KBDB_BASE_URL?: string;
KBDB_INTERNAL_TOKEN?: string;
KBDB_BASE_SVC?: { fetch: typeof fetch };
}): KbdbClient {
return new KbdbClient(env.KBDB_BASE_URL ?? '', env.KBDB_INTERNAL_TOKEN, env.KBDB_BASE_SVC);
}
+5
View File
@@ -6,6 +6,11 @@ export type Bindings = {
KBDB_BASE_URL?: string; // 基本盤 arcrun/kbdb API 網址(leo: 可設定,先留空)
KBDB_INGEST_URL?: string; // ingest 服務網址(refresh 代轉對象;T4 就緒前留空)
KBDB_INTERNAL_TOKEN?: string;
// Service Bindingwrangler.toml [[services]])→ 直連基本盤 worker,繞開
// CF error 1042Worker 不能公開 fetch 另一個 *.workers.dev Worker)。
// 2026-07-03 補跑實測發現:self-hosted 帳號常無自訂域名,KBDB_BASE_URL 單靠公開
// fetch 在 workers.dev 對 workers.dev 場景會被 CF 擋,故新增此綁定作為正規解法。
KBDB_BASE_SVC?: Fetcher;
ENVIRONMENT: string;
API_KEY?: string;
};
+6
View File
@@ -51,6 +51,12 @@
原因: 基本盤 = D1 only(免費、無信用卡);embed 是可選加購層。插件混進來會破壞分層。
日期: 2026-06-14
⚠️ MISTAKE: 補對齊/功能 PR 混進 template 基建遷移 → 撞已 merge 的遷移、害衝突
症狀: PR#3receiver Zod 補對齊)從 PR#2 merge【前】的分支切 → 帶了一整批 template 1.9.x 遷移檔(.claude/→system-dev/40 個)。PR#2 已把那批搬進 main → PR#3 重複撞 = CONFLICTING/DIRTY,且真正的補對齊改動被淹沒。
正確做法: 功能/補對齊 PR 只放該功能的改動;template/基建遷移單獨一筆 PR。撞衝突時別在舊分支硬解一堆遷移衝突 → 從最新 origin/main 重切乾淨分支、只 cherry-pick 該功能的 code commit、force-with-lease 覆蓋 PR 分支(PR 自動更新、不用關掉重開)。切分支前先確認 base 是不是落後於已 merge 的東西。
原因: 分支從「即將被 merge 的另一支」之前切,會把對方的改動也一起帶上;對方 merge 後兩份就撞。核心改動本身不衝突,衝突全來自混進來的重複遷移。
日期: 2026-06-26
---
格式:
+52 -8
View File
@@ -1,10 +1,45 @@
# 當前狀態
> 更新時間:2026-06-14
> 更新時間:2026-06-26
> 每次 session 結束必須更新此檔(用 /wiki-update)。
---
## 最新(2026-06-26issue #1 補對齊 — receiver Zod 追上 contractPR #3 已 merge
[PR #3](https://github.com/uncle6me-web/kbdb-graph-plugin/pull/3) 已 merge 進 maincommit `13db97b`)。對應 [issue #1](https://github.com/uncle6me-web/kbdb-graph-plugin/issues/1) 總管補對齊 comment。
**起因(契約漂移)**T3 的 strict Zod 鏡射【當時】contractcontract 之後升格(ingest#1 向量化規範)加打標欄位 → ingest 照新 contract 送會被 `.strict()` 擋 422。總管裁定方向 Agraph 追上 contractcontract 是凍結單一真相源,實作追它)。
完成:
1.`contracts/ingest-candidate.json` 副本同步到頂層單一真相源(`InkStoneCo/system-dev/docs/3-specs/mira-dissolve/`)。
2. ✅ Zod 加契約合法新欄位(**保留 `.strict()`**):`NodeSchema`+`id?`/`aliases?`/`embed?``EdgeSchema`+`predicate_embed?`
3. ✅ 落地(向量化分工:ingest 打標、base 讀標執行、**graph 不算向量**):`predicate_embed` 透傳進 triplet slotnode 打標(`embed`/`gloss`/`aliases`)存進 entity slot`id` 作 node 去重鍵(同卡多邊只一筆)。`persistNodes` 拆獨立 action`src/actions/node-persist.ts`)。
4. ✅ 測試 +4:帶向量化欄位【通過】、`bridge_score`/`clusters` 仍【422】、同 id 去重。
**新增 plugin slot**(非改表、非改 contract):triplet `predicate_embed`entity `embed`/`node_id`
驗證:`vitest run` **23 passed**;零 SQL / 無 D1·Vectorize·AIdry-run 乾淨;action ≤100 行。
**過程教訓(已記 mistakes**PR#3 初版從 PR#2 merge 前切 → 混進 PR#2 已做的 template 1.9.x 遷移 40 檔 → 撞 main 衝突。解法=從最新 main 重切、只 cherry-pick 補對齊那筆 code commit、瘦身後 force-push。**補對齊/功能 PR 別混 template 基建遷移。**
---
## 前一筆(2026-06-26issue #1 T3 — ingest 寫入端 + graph 端 APIPR #2 已 merge
[PR #2](https://github.com/uncle6me-web/kbdb-graph-plugin/pull/2) 已 mergecommit `7a29dee`squash)。
完成:
1.**wiki 合併**:舊 `.claude/wiki/``system-dev/wiki/`(導入 system-dev-template)。
2.**ingest-contract SDD**`docs/3-specs/ingest-contract/`+ 搬入 `contracts/ingest-candidate.json`T3.1/3.8)。
3.**寫入端 + 取代**T3.23.5):`POST /triplets/ingest`、ensureTemplate slot-diff 補丁、`updateRecord`、idempotency、**先 append 後 deprecate**、active-only 查詢。
4.**get_source + refresh**T3.6/3.7):`GET /graph/source/:name``POST /graph/refresh`(純被動代轉,未設 `KBDB_INGEST_URL` 時誠實回 `forwarded:false`)、keyword 收斂(3.6d)。
驗證:`vitest run` 19 passedmock);zero SQL / 無 D1·Vectorize·AIdry-run 乾淨;action ≤100 行。
> 註:base `PATCH /records/:id` 已就緒(Arcrun #6 closed),ingest deprecate 即用此。
---
## 已完成(2026-06-14:按 leo 鐵律全面改寫 + 獨立成 repo)
HANDOFF 5 項待辦全做完:
@@ -21,15 +56,24 @@ HANDOFF 5 項待辦全做完:
design.md 原本「讀現狀(21 個直接 SQL)推翻鐵律、問要不要共用 D1」是**讀違規現狀推翻規則**的錯。已改正為 **API-as-Wall(走 API,非共用 D1,零建表/零 SQL)**,並記進 mistakes.md。
## 正在做 / 阻擋
- ✅ PR #2T3)、PR #3(補對齊)皆已 merge 進 main。graph repo 端實作面收斂,**本 repo 無剩餘可單獨做的 task**。
- [🔄] 剩三項皆「不在 graph 手上」,等跨 repo 接通 / 部署(見下)。
## 下次 session 第一件事
**實際部署**:等基本盤 `arcrun-kbdb` 上線/有網址後,跑 `bash scripts/install.sh` 一次到位
(自動查 CF subdomain 拼 `KBDB_BASE_URL``wrangler secret put``wrangler deploy`)。
現在不空跑部署(避免上線一個打不到基本盤的殼)。
main 已含 T3 + 補對齊。本 repo 端無待辦——下次動工多半是**新交辦**或**跨 repo 接通就緒後接 MCP 薄殼**。
若要實際部署:等基本盤 `arcrun-kbdb` 上線有網址後跑 `bash scripts/install.sh`(自動查 CF subdomain 拼 `KBDB_BASE_URL` → secret → deploy)。現不空跑(避免上線打不到基本盤的殼)。
## 待負責人確認 / 跨 repo 接通(全通才結 issue #1
- **MCP 註冊薄殼** — 圖工具(traverse/neighbors/source/refresh)併入 arcrun `u6u-mcp-server`。等:總管協調 arcrun**不另起 graph MCP**;待 Arcrun #7 部署驗。graph 端 HTTP API 已備好。
- **refresh 端到端** — 等:ingest repoT4)部署 + 設 `KBDB_INGEST_URL`;未設時誠實回 `forwarded:false`
- **semantic normalize** — 仍 exact-only,留接口;等:base embedArcrun #7code done 待部署)。**補對齊已把向量化打標(embed/predicate_embed/gloss/aliases)落地進 slot 供 base 讀**,base 模組就緒即可接。
## 已知缺口([→arcrun],待基本盤補)
- base `PUT /records/:id` → entity addAlias 用「重建 record」覆寫
- base 無 `DELETE /records/:id` → triplet/entity update/delete、pending confirm/reject 為 soft(不硬刪)。
- 語意搜尋 / embedding 屬基本盤 optional embed 模組,插件只做 keyword/exact
- arcrun 端 MCP/CLI 的 KBDB 薄殼仍待補(見 arcrun HANDOFF §2);插件目前直打基本盤 HTTP API。
- base `PATCH /records/:id` ✅ 已就緒(Arcrun #6 closed);但 base 仍無 `DELETE /records/:id` → triplet/entity delete、pending confirm/reject 為 soft(不硬刪)
- 語意搜尋 / embedding 屬基本盤 optional embed 模組,插件只做 keyword/exactgraph 不算向量,鐵律)。
- arcrun 端 MCP/CLI 的 KBDB 薄殼仍待補;插件目前直打基本盤 HTTP API
+20 -6
View File
@@ -10,15 +10,29 @@ workers_dev = true
[vars]
ENVIRONMENT = "development"
# 基本盤 arcrun/kbdb API 網址leo 2026-06-14:做成可設定,先留空)
# 部署前用 `wrangler secret put` 或在此填入,例如 https://arcrun-kbdb.<acct>.workers.dev
KBDB_BASE_URL = ""
# 基本盤 arcrun/kbdb API 網址:務必只透過 `wrangler secret put KBDB_BASE_URL` 設定
# 2026-07-03 補跑實測發現的坑:若這裡也宣告 KBDB_BASE_URL(哪怕留空字串),
# `wrangler deploy` 會用這個 [vars] 值覆蓋掉已存在的 remote secretvars 優先權 > secret),
# 導致部署後 KBDB_BASE_URL 被打回空字串、插件連不到基本盤。故此鍵不可出現在 [vars]。
[alias]
"zod/v3" = "zod"
"zod/v4" = "zod"
"zod/v4-mini" = "zod"
[[routes]]
pattern = "kbdb-graph.finally.click"
custom_domain = true
# 2026-07-03 補跑實測發現的坑:workers.dev 是共享 zoneWorker 直接 fetch()
# 另一個 *.workers.dev Worker 會被 CF 擋(error code 1042loop-prevention)。
# self-hosted 帳號通常沒自訂域名可繞,正規解法 = Service BindingCF 內部直連,
# 不經公開網路、不受 1042 影響。KBDB_BASE_URL secret 仍保留(可讀性/未來自訂域名時 fallback),
# 但實際讀寫走這個 binding(見 src/lib/kbdb-client.ts / makeKbdbClient)。
[[services]]
binding = "KBDB_BASE_SVC"
service = "arcrun-kbdb"
# 2026-07-03 補跑實測:custom_domain "kbdb-graph.finally.click" 這個 zone
# 不在 leo21c 帳號下(CF API GET /zones?name=finally.click 回空),
# self-hosted 到 leo21c 時此路由會部署失敗(但不影響 workers.dev route 的部署本身)。
# 註解掉以让 self-hosted 用戶乾淨部署到預設 workers.dev;有自訂域名需求者自行改回。
# [[routes]]
# pattern = "kbdb-graph.finally.click"
# custom_domain = true