-- t-paliad-190 / Fristen Phase 3 Slice 10 — staging table for the -- fuzzy-match orphans produced by mig 090. Per design §3.I + m's Q10 -- ruling: legacy paliad.deadlines rows whose title can't be uniquely -- bound to a deadline_rule via fuzzy matching are NOT silently left -- NULL — they're logged here so a legal-review pass can hand-link -- the ambiguous tail. -- -- Mig 089 ships the table; mig 090 does the actual backfill + -- populates this table. Numbering reflects the dependency order -- (the backfill SELECTs into this table, so the table must exist -- first). -- -- Schema notes: -- - deadline_id is the FK to paliad.deadlines.id with ON DELETE -- CASCADE so a hand-deletion of an orphan deadline cleans up -- its staging row too. (Deadlines are normally archived, not -- deleted; the cascade is defensive.) -- - project_id stays denormalised so the admin orphan-review UI -- can group orphans by project without re-joining deadlines. -- - reason is a free-text discriminator: 'no_match' | 'ambiguous' -- today; the editor in Slice 11 may add 'manual_unbound' or -- similar in the future. -- - resolved_at + resolved_rule_id are NULL on insert; the admin -- orphan-review UI sets them when an editor hand-links the row, -- so the table doubles as an audit trail of the legal-review -- pass. The matching paliad.deadlines.rule_id is updated at the -- same time (the UPDATE on deadlines fires its own audit row -- once an audit trigger lives on that table; today no trigger, -- so the staging row is the audit artefact). -- -- RLS: admin-only read. The orphan list contains real deadline titles -- + project ids, so non-admins should not see it. The Slice 11 rule -- editor surface gates this further. CREATE TABLE IF NOT EXISTS paliad.deadline_rule_backfill_orphans ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), deadline_id uuid NOT NULL REFERENCES paliad.deadlines(id) ON DELETE CASCADE, title text NOT NULL, project_id uuid, proceeding_code text, reason text NOT NULL CHECK (reason IN ('no_match', 'ambiguous', 'no_project', 'manual_unbound')), candidate_count int NOT NULL DEFAULT 0, candidate_rule_ids uuid[] NOT NULL DEFAULT '{}', resolved_at timestamptz, resolved_rule_id uuid REFERENCES paliad.deadline_rules(id) ON DELETE SET NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS deadline_rule_backfill_orphans_deadline_id_idx ON paliad.deadline_rule_backfill_orphans (deadline_id); CREATE INDEX IF NOT EXISTS deadline_rule_backfill_orphans_unresolved_idx ON paliad.deadline_rule_backfill_orphans (created_at DESC) WHERE resolved_at IS NULL; COMMENT ON TABLE paliad.deadline_rule_backfill_orphans IS 'Slice 10 (mig 089/090, t-paliad-190): staging for legacy ' 'paliad.deadlines rows that the fuzzy-match backfill could not ' 'uniquely bind to a deadline_rule. Each row holds the deadline ' 'context + the candidate rule IDs the matcher found (0 → ' '''no_match''; ≥2 → ''ambiguous'') so a legal-review pass can ' 'hand-link without rerunning the match. resolved_at + ' 'resolved_rule_id flip when the admin orphan-review UI binds the ' 'row.'; -- RLS: admin-only read. ALTER TABLE paliad.deadline_rule_backfill_orphans ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS deadline_rule_backfill_orphans_select ON paliad.deadline_rule_backfill_orphans; CREATE POLICY deadline_rule_backfill_orphans_select ON paliad.deadline_rule_backfill_orphans FOR SELECT USING ( EXISTS ( SELECT 1 FROM paliad.users u WHERE u.id = auth.uid() AND u.global_role = 'global_admin' ) );