98b221b435
對應 issue #1(頂層 mira-dissolve T3)。 - contracts/ingest-candidate.json:ingest→graph 邊界契約(自頂層搬入) - contracts/README.md:標明候選(輸入)≠已存(triplet) - docs/3-specs/ingest-contract/design.md + tasks.md: - ensureTemplate 改 slot-diff 補丁(取代 early-return,免遷移腳本) - 補 KbdbClient.updateRecord(base PATCH /records/:id) - ingest 流程:驗證(422)→idempotency(uri+hash)→先 append 後 deprecate - triplet template 增 source_uri+content_hash slot 承載 idempotency - 跨 repo 協調點(3.6 圖工具併 KBDB MCP)明列需 arcrun 配合 總管已認可四個設計決定(issue #1 comment)。鐵律:零建表/零 SQL/零 migration。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
contracts/ — graph 插件邊界契約
本目錄放 graph 插件對外的 JSON Schema 契約。兩份契約是不同方向的東西,別混。
兩份契約的分工
| 檔案 | 是什麼 | 方向 | 誰產 / 誰收 |
|---|---|---|---|
ingest-candidate.json |
輸入候選(envelope) | ingest 插件 → graph 插件 | ingest 萃取後送進來,graph 收下處理 |
triplet.json |
已存三元組(graph 內部存的單筆 S-P-O) | graph 內部 / 對查詢面 | graph 寫進基本盤 records 後的形態 |
核心區別:ingest-candidate 是「還沒進庫的一批原始萃取」,triplet 是「已經正規化、存好、可查的單筆」。
- ingest 只送原始
(subject, predicate, object) + confidence,禁送 graph 領域欄位(id/clusters/bridge_score/created_at/ triplet 上的*_entity_type)。送了 → graph 以 422 拒收(additionalProperties: false)。 - 正規化、clusters、bridge_score、embed、
status/superseded_by取代邏輯——全是 graph 領域,ingest 一無所知(純餵食器)。
ingest-candidate envelope 重點
- 一個 envelope = 一個來源檔(canonical MD)一次萃取的產物。
source.uri(穩定識別)+source.content_hash(快照鍵)共同決定 idempotency:- 同 uri + 同 hash → no-op(
{skipped:true})。 - 同 uri + 新 hash → deprecate-then-append(舊 active 翻
status=deprecated+superseded_by,append 新批 active)。
- 同 uri + 同 hash → no-op(
nodes[](選填)帶gloss/entity_type—— 節點屬性,不是邊屬性,故獨立於triplets[]。graph 用 gloss 去 embed(每節點一句,非裸詞)。
完整 schema 與欄位說明見 ingest-candidate.json 的 description / $comment。
本 repo 對此契約的實作(端點、取代邏輯、測試)見 docs/3-specs/ingest-contract/。