feat(graph): owner_id 寫入鏈必經(根治無主資料)——配合 base owner-mandatory(D28)

- kbdb-client requireOwner 守衛:漏 owner 插件端即 throw,不再靜默送 owner:''
- owner 一路 thread:persistNodes/ingestEnvelope/entity-crud/triplet-crud 全必填
- 病灶修:POST /triplets/ingest 原本沒傳 owner→加 owner_id query+400
- 寫入 route 缺 owner→400;vitest 23→27
- 註:gloss-bridge 分支的 backfill/gloss-entry 另需套 owner 必填(附說明),不跨支疊改
This commit is contained in:
Claude
2026-07-05 11:00:30 +00:00
parent b53c5c94a9
commit 4190fbcf90
16 changed files with 152 additions and 54 deletions
+20 -8
View File
@@ -5,17 +5,29 @@ import { normalizeEntity } from '../src/actions/entity-normalize';
import { mockClient } from './mock-client';
describe('entity-crud', () => {
it('建立後可 exact 查回(大小寫不敏感)', async () => {
it('建立後可 exact 查回(大小寫不敏感)owner slot 帶真 owner', async () => {
const c = mockClient();
await createEntity(c, 'InkStone');
const found = await findEntityByName(c, 'inkstone');
const ent = await createEntity(c, 'InkStone', 'leo');
const found = await findEntityByName(c, 'inkstone', 'leo');
expect(found?.canonical).toBe('InkStone');
// owner 落底:record 掛在 leo 名下(非無主),用錯 owner 查不到。
const rec = await c.getRecord(ent.id);
expect(rec?.values.owner).toBe('leo');
expect(await findEntityByName(c, 'inkstone', 'someone-else')).toBeNull();
});
it('漏 owner → 插件端 fail(不寫出無主 entity', async () => {
const c = mockClient();
await expect(
// @ts-expect-error 蓄意漏 owner:型別上 owner 必填,執行期也應 throw
createEntity(c, 'NoOwner'),
).rejects.toThrow(/owner/i);
});
it('listEntities 列出', async () => {
const c = mockClient();
await createEntity(c, 'A');
await createEntity(c, 'B');
await createEntity(c, 'A', 'leo');
await createEntity(c, 'B', 'leo');
const all = await listEntities(c);
expect(all.map((e) => e.canonical).sort()).toEqual(['A', 'B']);
});
@@ -24,8 +36,8 @@ describe('entity-crud', () => {
describe('normalizeEntity', () => {
it('已存在回 canonical,不存在建新回原值', async () => {
const c = mockClient();
await createEntity(c, 'InkStone');
expect(await normalizeEntity(c, 'INKSTONE')).toBe('InkStone');
expect(await normalizeEntity(c, '新公司')).toBe('新公司');
await createEntity(c, 'InkStone', 'leo');
expect(await normalizeEntity(c, 'INKSTONE', 'leo')).toBe('InkStone');
expect(await normalizeEntity(c, '新公司', 'leo')).toBe('新公司');
});
});