Review of aa2ca0a found and fixed:
- re-ingest duplicated ALL edges (upsert conflicted on valid_from=now(),
never fired) — migration 007 dedupes + partial unique index on current
edges; upsert now targets it. Regression-tested.
- export was a stub that overwrote seeds/*.yaml with 11-byte "version: 1"
files — implemented real deterministic export (ontology/inventory/policy,
cognition-layer excluded); round-trip is byte-stable (tested)
- DB password leaked in startup logs (slog JSON bypasses String()) —
Config now implements slog.LogValuer; regression-tested
- docker-compose had literal '***' as DB password — env-interpolated
- uuid.New() (v4) → uuid.NewV7() per ADR-0005
- no ontology validation on ingest — internal/ontology TypeTree: abstract
instantiation rejected, relationship endpoints hierarchy-validated,
cardinality enforced in-transaction, lifecycle states checked, default
state applied (Phase 1 gate items, R3-1)
- getOrCreateEntityID swallowed non-ErrNoRows errors
- migration runner now holds a session advisory lock on one connection
- Makefile: hardcoded /opt/homebrew/bin/go → go; test-db target
Tests: 4 unit suites + 7 integration tests (env-guarded, throwaway DB per
run): migrate idempotent, seed idempotent + no dup edges, abstract/edge/
cardinality rejection, blast_radius cycle termination, export round-trip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
19 lines
770 B
SQL
19 lines
770 B
SQL
-- Migration 007: one current edge per (source, target, type).
|
|
-- The seed upsert previously conflicted on (source_id, target_id, type,
|
|
-- valid_from) — valid_from is now() at insert, so the conflict never fired
|
|
-- and every re-ingest duplicated all current edges. Dedupe (keep earliest
|
|
-- valid_from), then enforce uniqueness on current edges with a partial
|
|
-- unique index the upsert can target.
|
|
|
|
DELETE FROM relationships r
|
|
USING relationships keep
|
|
WHERE r.valid_to IS NULL AND keep.valid_to IS NULL
|
|
AND r.source_id = keep.source_id
|
|
AND r.target_id = keep.target_id
|
|
AND r.type = keep.type
|
|
AND r.valid_from > keep.valid_from;
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_rel_current
|
|
ON relationships(source_id, target_id, type)
|
|
WHERE valid_to IS NULL;
|