feat: KBDB-graph 插件獨立 — 全面改寫成走基本盤 API(API-as-Wall)

按 leo 鐵律(2026-06-14)把插件從「直接 SQL 操作基本盤表」改寫成
「只透過基本盤 arcrun/kbdb HTTP API 讀寫」。零建表、零 migration、零 SQL。

- 新增 src/lib/kbdb-client.ts:唯一對外通道,封裝 entries/templates/records API
- 新增 src/lib/templates.ts:triplet/entity template 定義(替代建表)
- 改寫 21 個違規 action(triplet/graph/entity/search)→ 走 client,圖在插件層記憶體組裝
- 移除所有 migrations、D1/Vectorize/AI 綁定;embedding/語意搜尋歸基本盤 optional 模組
- index.ts 只掛 triplets/graph/entities/search 路由;基本盤路由歸 arcrun/kbdb
- 測試改走 mock client(純 node);裁剪 CLAUDE.md 只留 graph 插件 + 鐵律
- 修正 SDD design.md「讀現狀推翻鐵律」的錯誤判斷(共用 D1 → API-as-Wall)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 20:59:41 +08:00
commit efe8e165cf
62 changed files with 7671 additions and 0 deletions
+227
View File
@@ -0,0 +1,227 @@
# KBDB — Blocks Edit API
> **建立**2026-05-06
> **狀態**:草稿,待 richblack review
> **依賴**`matrix/kbdb/CLAUDE.md`(萬物皆 Block 架構,2026-02-28 鎖定)
> **驅動需求**`polaris/mira/.agents/specs/mira-app/design.md`(前端 inline edit 直寫 KBDB 的需求)
---
## 0. 背景
KBDB v3「萬物皆 Block」架構鎖定後,現役 routes 涵蓋 Block CRUD 多數操作,但**缺少編輯既有 block 的 PATCH endpoints**。具體缺口:
| 操作 | 現役狀態 |
|---|---|
| `POST /blocks/ingest`(建立) | ✅ 已有 |
| `GET /blocks/{id}`(讀取單筆) | ✅ 已有 |
| `GET /blocks/`(列表 / 查詢) | ✅ 已有 |
| `DELETE /blocks/{id}`(刪除) | ✅ 已有 |
| **`PATCH /blocks/{id}`(部分更新)** | ❌ **缺** |
| `POST /triplets/`(建立) | ✅ 已有 |
| **`PATCH /triplets/{id}` / `DELETE /triplets/{id}`** | ❌ **缺** |
| `PUT /templates/{name}` | ✅ 已有 |
| `PATCH /tasks/{id}/status` | ✅ 已有 |
→ 沒有 PATCH/UPDATE block 內容的 API,前端「inline edit 寫回 KBDB」做不了。
> **註**`src/routes/blocks.ts.bak` 有 PUT /:id 的舊實作可參考,但已被 v3 取代並停用。**不直接複用 .bak 檔案**,按 v3 規範重寫。
---
## 1. 範圍
本 SDD 涵蓋兩件事:
1. **補三組 endpoint**`PATCH /blocks/{id}`, `PATCH /triplets/{id}`, `DELETE /triplets/{id}`
2. **建三個 templates**`data-source-config`, `source-skill`, `wiki-page`(為 mira-app 準備)
**不在範圍內**
- 改 schemav3 鎖定,禁止 ALTER TABLE
- 改既有 endpoint 行為(純加新 endpoint
- 多用戶權限細分(partner key 已能做 org 隔離,沿用即可)
- 編輯歷史 / undo(未來 SDD
---
## 2. PATCH /blocks/{id}
### 2.1 用途
部分更新一個既有 block。前端 inline edit 場景:使用者點 edit icon,改 content / refs / tags,按儲存 → 送 PATCH。
### 2.2 規格
```
PATCH /blocks/{id}
Authorization: Bearer <api_key>
Content-Type: application/json
Body (所有欄位皆 optional,至少要有一個):
{
"content": "新的內容",
"tags": ["tag1", "tag2"], // 完整覆寫 tags 陣列
"refs": ["block-id-1", "block-id-2"], // 完整覆寫 refs 陣列
"slots": { "key": "value" }, // 完整覆寫 slots 物件
"source": "...", // 通常不改,但允許
"metadata_json": { ... } // 完整覆寫
}
Response 200:
{
"id": "...",
"content": "...",
"updated_at": "2026-05-06T...",
...完整 block 欄位
}
Response 400:
{ "error": "no fields to update" }
Response 403:
{ "error": "block belongs to different org" }
Response 404:
{ "error": "block not found" }
```
### 2.3 行為
- 只更新 body 內提供的欄位
- 自動更新 `updated_at`
- 自動重新計算 `content_hash`(如 content 變動)
- 自動觸發 embedding 重算(如 content 變動,async
- **權限**partner key 只能改自己 org 內的 block(透過 user_id 對應),internal token 可改任何 block
- **content_hash 衝突**partner key 不可修改 v3「`source``system` 的 admin 標記資料」(沿用既有 admin-preservation 規則)
### 2.4 實作位置
- Action: `src/actions/update-block.ts`< 100 行,按 KBDB CLAUDE.md 樂高法)
- Route: `src/routes/blocks.ts` 加新 OpenAPI route
- Test: `tests/blocks-update.test.ts`
---
## 3. Triplet 編輯:使用既有 `PUT /records/:id` + `DELETE /records/:id`
**設計修正(2026-05-06 實作時發現)**
v3 萬物皆 Block 架構下,triplet 是 `tpl-triplet` template 的 record(用 entry_values 存 subject/predicate/object slots)。**既有 `PUT /records/:id``DELETE /records/:id` 已涵蓋編輯/刪除需求**,無需新增 `PATCH /triplets/:id`
→ 前端編輯異見牆上的 triplet:
- 編輯:`PUT /records/{triplet_record_id}` body `{ values: { subject, predicate, object } }`
- 刪除:`DELETE /records/{triplet_record_id}`
**本 SDD 不再規劃新 triplet endpoint**
---
## 5. 三個新 Templates
按 KBDB v3 規範,新資料類型透過 template 定義,**不動 schema**。
### 5.1 template: `data-source-config`
每個資料源實例對應一個此 template 的 block。Mira 的「來源篩選」、cron workflow 的「每天去抓什麼」都讀這個。
```yaml
template_name: data-source-config
slots:
- name: string # "電子時報"、"我的 Logseq"
- channel: string # rss / telegram / km-writer / voice-stt / ai-comment / ai-canon
- config: object # channel-specific (e.g., rss: {url, schedule})
- skill_id: string? # 連到 source-skill block 的 id
- enabled: bool
- ai_comment: bool # 是否需要 AI 加註解
- ai_comment_style: string? # 提示給 claude_api 的風格
```
### 5.2 template: `source-skill`
每個 source 累積的「分析配方」(prompt + few-shot)。可在前端編輯、版本化。
```yaml
template_name: source-skill
slots:
- name: string # 例 "電子時報科技類分析"
- prompt: text # system_prompt 內容
- examples: text? # few-shot examplesmarkdown
- version: int
- based_on: string? # 上一版的 block id
```
### 5.3 template: `wiki-page`
AI 從河道對話合成的定稿。
```yaml
template_name: wiki-page
slots:
- entity_name: string
- summary: text # markdown
- key_blocks: array<string> # 引用的 source block ids
- conflicts: array<string>? # 標記為矛盾的 block ids
- generated_at: timestamp
- version: int
- based_on: string?
```
### 5.4 建立方式
不寫 SQL migrationv3 規範禁止)。改用 KBDB 既有的 `POST /templates`
```bash
curl -X POST https://kbdb.finally.click/templates \
-H "Authorization: Bearer <internal_token>" \
-d '{"name": "data-source-config", "slots": [...]}'
```
→ tasks.md 列為 P0 任務(用 internal token 一次性建好)。
---
## 6. 實作步驟
### Phase 1:補 endpoints
1.`src/actions/update-block.ts`(純函數,< 100 行,含權限檢查)
2.`tests/blocks-update.test.ts`(含 happy path、403、404、no-fields 三案)
3.`src/routes/blocks.ts` 的 PATCH routeOpenAPI 定義 + 呼叫 action
4.`src/routes/triplets.ts` PATCHwrapper+ DELETEalias
5. 部署 + smoke test
### Phase 2:建 templates
6. 用 internal token 呼叫 `POST /templates` 建三個 template
7. 驗證:用 partner key (mira 用的) 創建一個 `data-source-config` block 看能否寫成功
### Phase 3:補 OpenAPI spec
8. 確認新 routes 自動進 swagger.jsonOpenAPIHono 應該自動,需驗證)
---
## 7. 風險
- **embedding 重算成本**PATCH content 會觸發 vectorize 重算,頻繁 inline edit 可能拖慢。**對策**embedding 改為 async(行為已是 async,需確認)。
- **content_hash 計算遺漏**:忘記重算會讓查重失效。**對策**:在 action 內統一處理,不讓 route 層管。
- **partner key 越權**:必須驗 user_id 對應,不能讓 partner A 改 partner B 的 block。**對策**write tests 涵蓋此案。
- **三個 templates 命名衝突**:若 KBDB 已有同名 template 會 fail。**對策**:建立前先 GET /templates/{name} 檢查。
---
## 8. 不在範圍內
- 編輯歷史 / undo / version diff(未來 SDD
- Block soft deletev3 已有 hard deletesoftdelete 是 enhancement
- Bulk PATCH(一次改多個 block,未來看需求)
- Field-level permissions(特定欄位只能某些 user 改)
- WebSocket 通知 block 改了(即時協作)
---
## 9. 變更紀錄
| 版本 | 日期 | 內容 |
|---|---|---|
| v0 | 2026-05-06 | 初稿。對應 mira-app 的 inline edit 需求。 |
+68
View File
@@ -0,0 +1,68 @@
# KBDB Blocks Edit API — Tasks
> 對應 SDD[design.md](design.md)
> 上次更新:2026-05-06Phase 1 + Phase 2 完成)
---
## Phase 1:補 PATCH endpoint ✅ 完成
### 1. PATCH /blocks/{id}
- [x] 1.1 zod schema 直接放在 route 檔(既有 pattern
- [x] 1.2 寫 `src/actions/block-update.ts`96 行,符合樂高法 < 100
- 取既有 block + getBlock fallbackid 或 logseq_uuid
- 權限檢查:partner key 比對 user_id 前綴
- 自動重算 content_hash(如 content 變)
- 觸發 embedding async 重算(不阻塞 PATCH 回應)
- 寫回 D1
- 回傳更新後的 block
- [x] 1.3 寫 `tests/blocks-update.test.ts`**7 case 全通過**
- happy: content + content_hash 重算
- happy: tags + refs 同改
- 400: 無欄位
- 404: 不存在
- 403: partner 越權
- 200: partner 改自己 namespace
- content_hash 在只改 tags 時不變
- [x] 1.4 在 `src/routes/blocks.ts` 加 PATCH routeOpenAPI
- [x] 1.5 部署到 prodkbdb.finally.click+ smoke test 4 case 通過
### 2. Triplet 編輯:使用既有 `PUT/DELETE /records/:id`
- [x] 2.1 設計修正:v3 萬物皆 Blocktriplet 是 record,既有 endpoints 已涵蓋。本任務組無需新增 endpoint。
- [ ] 2.2 在 mira-app 前端「異見牆」實作呼叫 `PUT /records/:id`(待 mira 階段 3
### 3. OpenAPI spec 同步
- [x] 3.1 OpenAPIHono 自動產 swagger.jsonroute 用 createRoute 已自動納入)
- [ ] 3.2 部署後驗證 swagger UI 顯示新 route(待手動驗證)
---
## Phase 2:建三個 templates ✅ 完成
- [x] 4.1 確認 KBDB 內無同名 template(透過 GET /templates 確認)
- [x] 4.2 用 internal token POST /templates 建 `data-source-config`id: `tpl-data-source-config`
- [x] 4.3 用 internal token POST /templates 建 `source-skill`id: `tpl-source-skill`
- [x] 4.4 用 internal token POST /templates 建 `wiki-page`id: `tpl-wiki-page`
- [x] 4.5 驗證:3/3 templates 在 GET /templates 列表內
---
## 風險追蹤
- ~~風險 1partner key 跨 org 越權~~ — ✅ unit test 已涵蓋(403 partner 越權)
- ~~風險 2embedding 重算造成 D1 寫入 spike~~ — ✅ 改成 fire-and-forget(不 await),不阻塞 PATCH
- ~~風險 3content_hash 不一致~~ — ✅ unit test 驗證 hash 重算對應內容
## Known Issues(不在本 SDD 範圍,待另開)
- **`POST /blocks/ingest` 不寫入 `source` 欄位**input 接受 `source` 參數但僅用於 id slug 生成,未寫進 block 的 source 欄位(block-ingest.ts:84 的 INSERT 缺欄位)。對 mira 影響:所有 source 區分目前無效,需等 KBDB 修復或直接走 `POST /blocks` + slots。建議下一份 KBDB SDD `block-ingest-source-fix` 處理。
---
## 部署紀錄
- 2026-05-06: Worker version `b7df3c38-e138-41fb-a16c-cc9d2dfeebea` 部署上線
- Smoke test 通過:content 改寫 + hash 重算、tags 改寫、400 空 body、404 不存在