-- supabase/migrations/20260712000017_closure_lock_addenda.sql -- Phase 5: closed incidents become immutable (addenda-only), per PRD §4 Phase 4. CREATE TABLE incident_addenda ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), author UUID NOT NULL REFERENCES users(id), body TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX incident_addenda_incident_idx ON incident_addenda(incident_id); ALTER TABLE incident_addenda ENABLE ROW LEVEL SECURITY; CREATE POLICY "addenda_read" ON incident_addenda FOR SELECT USING ( auth_user_role() IN ('hse', 'admin', 'supervisor', 'management') OR EXISTS ( SELECT 1 FROM incidents i WHERE i.id = incident_id AND i.reported_by = auth.uid() ) ); CREATE POLICY "addenda_insert" ON incident_addenda FOR INSERT WITH CHECK ( author = auth.uid() AND auth_user_role() IN ('hse', 'admin', 'supervisor') ); -- No UPDATE/DELETE policies: addenda are append-only. -- DB-level lock: once closed, the incident row cannot change or be deleted. CREATE OR REPLACE FUNCTION public.prevent_closed_incident_change() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ BEGIN RAISE EXCEPTION 'incident % is closed and locked — add an addendum instead', OLD.id; END; $$; CREATE TRIGGER incidents_closed_lock_update BEFORE UPDATE ON incidents FOR EACH ROW WHEN (OLD.status = 'closed') EXECUTE FUNCTION prevent_closed_incident_change(); CREATE TRIGGER incidents_closed_lock_delete BEFORE DELETE ON incidents FOR EACH ROW WHEN (OLD.status = 'closed') EXECUTE FUNCTION prevent_closed_incident_change();