Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
114 lines
3.7 KiB
PL/PgSQL
114 lines
3.7 KiB
PL/PgSQL
-- Phase 7.1 — Registry provenance (ADR-0003 C-1, ticket 125589)
|
|
-- Enforces non-null title / created_by / created_at on documents-registry rows.
|
|
--
|
|
-- Strategy
|
|
-- * created_at is already DEFAULT CURRENT_TIMESTAMP on table_rows — nothing to add.
|
|
-- * created_by is nullable; backfill legacy NULLs to user_id=1 (system) for
|
|
-- rows in universal_tables.table_type = 'documents_registry'.
|
|
-- * title lives inside JSONB data; column-level NOT NULL is not available.
|
|
-- Enforce via BEFORE INSERT OR UPDATE trigger scoped to documents
|
|
-- registries, checking data->>'name' or data->>'title' non-empty.
|
|
-- * Same trigger rejects NULL created_by on documents-registry rows so app
|
|
-- bugs cannot resurrect orphan provenance.
|
|
--
|
|
-- Applies to DEV only in this pass. PROD schedule = separate ticket.
|
|
|
|
BEGIN;
|
|
|
|
-- ---- Pre-check ---------------------------------------------------------
|
|
DO $$
|
|
DECLARE
|
|
orphan_created_by int;
|
|
orphan_created_at int;
|
|
orphan_name int;
|
|
total int;
|
|
BEGIN
|
|
SELECT
|
|
COUNT(*) FILTER (WHERE tr.created_by IS NULL),
|
|
COUNT(*) FILTER (WHERE tr.created_at IS NULL),
|
|
COUNT(*) FILTER (WHERE (tr.data->>'name') IS NULL AND (tr.data->>'title') IS NULL),
|
|
COUNT(*)
|
|
INTO orphan_created_by, orphan_created_at, orphan_name, total
|
|
FROM table_rows tr
|
|
JOIN universal_tables ut ON ut.id = tr.table_id
|
|
WHERE ut.table_type = 'documents_registry';
|
|
|
|
RAISE NOTICE 'Phase 7.1 pre-check: total=%, null_created_by=%, null_created_at=%, null_title=%',
|
|
total, orphan_created_by, orphan_created_at, orphan_name;
|
|
END$$;
|
|
|
|
-- ---- Backfill NULL created_by ------------------------------------------
|
|
UPDATE table_rows tr
|
|
SET created_by = 1
|
|
FROM universal_tables ut
|
|
WHERE ut.id = tr.table_id
|
|
AND ut.table_type = 'documents_registry'
|
|
AND tr.created_by IS NULL;
|
|
|
|
-- ---- Backfill NULL created_at (defensive; should already be 0) ---------
|
|
UPDATE table_rows tr
|
|
SET created_at = CURRENT_TIMESTAMP
|
|
FROM universal_tables ut
|
|
WHERE ut.id = tr.table_id
|
|
AND ut.table_type = 'documents_registry'
|
|
AND tr.created_at IS NULL;
|
|
|
|
-- ---- Enforcement trigger ----------------------------------------------
|
|
CREATE OR REPLACE FUNCTION registry_provenance_check()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_table_type text;
|
|
v_title text;
|
|
BEGIN
|
|
SELECT table_type INTO v_table_type
|
|
FROM universal_tables
|
|
WHERE id = NEW.table_id;
|
|
|
|
IF v_table_type IS DISTINCT FROM 'documents_registry' THEN
|
|
RETURN NEW;
|
|
END IF;
|
|
|
|
IF NEW.created_by IS NULL THEN
|
|
RAISE EXCEPTION 'registry_provenance: created_by is NULL (table_id=%)', NEW.table_id
|
|
USING ERRCODE = '23502';
|
|
END IF;
|
|
|
|
IF NEW.created_at IS NULL THEN
|
|
RAISE EXCEPTION 'registry_provenance: created_at is NULL (table_id=%)', NEW.table_id
|
|
USING ERRCODE = '23502';
|
|
END IF;
|
|
|
|
v_title := COALESCE(NULLIF(NEW.data->>'name', ''), NULLIF(NEW.data->>'title', ''));
|
|
IF v_title IS NULL THEN
|
|
RAISE EXCEPTION 'registry_provenance: data.name/title is empty (table_id=%)', NEW.table_id
|
|
USING ERRCODE = '23502';
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS registry_provenance_trg ON table_rows;
|
|
CREATE TRIGGER registry_provenance_trg
|
|
BEFORE INSERT OR UPDATE ON table_rows
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION registry_provenance_check();
|
|
|
|
-- ---- Post-check --------------------------------------------------------
|
|
DO $$
|
|
DECLARE
|
|
orphan_after int;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO orphan_after
|
|
FROM table_rows tr
|
|
JOIN universal_tables ut ON ut.id = tr.table_id
|
|
WHERE ut.table_type = 'documents_registry'
|
|
AND (tr.created_by IS NULL OR tr.created_at IS NULL);
|
|
|
|
IF orphan_after > 0 THEN
|
|
RAISE EXCEPTION 'Phase 7.1 post-check failed: % orphan rows remain', orphan_after;
|
|
END IF;
|
|
RAISE NOTICE 'Phase 7.1 post-check: OK (0 orphan rows)';
|
|
END$$;
|
|
|
|
COMMIT;
|