feat(ingest): POST /triplets/ingest 寫入端 + deprecate-then-append (T3.2-3.5)
對應 issue #1 T3 B 段。 - templates: TRIPLET_SLOTS 加 status/superseded_by/source_uri/content_hash; ENTITY_SLOTS 加 gloss;recordToTriplet 映射新欄位(缺省 status=active 相容舊資料) - kbdb-client: ensureTemplate 改 slot-diff 補丁(既有 template 走 PATCH /templates/:id 補缺 slot,取代 early-return → 免遷移腳本);新增 updateRecord(PATCH /records/:id) - triplet-ingest action(88 行純函式):Zod strict 鏡射 ingest-candidate 契約 → idempotency(uri+hash 同→no-op)→ 先 append 後 deprecate(無「全無 active」空窗) - POST /triplets/ingest route:strict 驗證失敗 → 422(禁送 graph 領域欄位) - queryTriplets 預設 active-only(traverse/search/neighbors 皆經此), includeDeprecated opt-out 供 rollback/考古 - 6 測試案全綠(vitest 16 passed);mock-client 同步 slot-diff + updateRecord gates: zero SQL / zero migration / 無 D1·Vectorize·AI 綁定 / dry-run bundle 乾淨 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+21
-3
@@ -113,12 +113,25 @@ export class KbdbClient {
|
||||
// --- templates(= 替代建表;插件要新類型只能建 template) ---
|
||||
|
||||
async ensureTemplate(name: string, slots: string[], description?: string): Promise<void> {
|
||||
const existing = await this.req<{ id?: string } | { error: string }>(
|
||||
const existing = await this.req<{ id?: string; slots?: string[] } | { error: string }>(
|
||||
'GET',
|
||||
`/templates/${encodeURIComponent(name)}`,
|
||||
).catch(() => null);
|
||||
if (existing && (existing as any).id) return;
|
||||
await this.req('POST', '/templates', { name, slots, description, created_by: 'kbdb-graph' });
|
||||
|
||||
// 全新 template → 建。
|
||||
if (!existing || !(existing as any).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 ?? []);
|
||||
const missing = slots.filter((s) => !have.has(s));
|
||||
if (missing.length === 0) return;
|
||||
await this.req('PATCH', `/templates/${encodeURIComponent((existing as any).id)}`, {
|
||||
slots: [...have, ...missing],
|
||||
});
|
||||
}
|
||||
|
||||
// --- records(= template 實例,填 slot) ---
|
||||
@@ -141,6 +154,11 @@ export class KbdbClient {
|
||||
}
|
||||
}
|
||||
|
||||
/** 翻 record 的 slot 值(base PATCH /records/:id)。deprecate(翻 status)與 rollback 都靠它。 */
|
||||
async updateRecord(recordId: string, values: Record<string, string>): Promise<void> {
|
||||
await this.req('PATCH', `/records/${encodeURIComponent(recordId)}`, { values });
|
||||
}
|
||||
|
||||
async listRecordsByTemplate(template: string, owner_id?: string): Promise<BaseRecord[]> {
|
||||
const { records } = await this.req<{ records: BaseRecord[] }>(
|
||||
'GET',
|
||||
|
||||
Reference in New Issue
Block a user