feat(suppress): polymorphic owner on template_suppressions (M1.1)
Reshape the app-only suppression marker to a group/app polymorphic owner (mirrors 0056/0057 + the 0051/0052 config markers): nullable group_id (CASCADE), nullable app_id, exactly-one CHECK, per-owner partial unique indexes. Lets a [group] decline a template it inherits from a higher ancestor for its whole subtree; consumption filters land in M1.3/M1.4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
38
crates/manager-core/migrations/0060_group_suppressions.sql
Normal file
38
crates/manager-core/migrations/0060_group_suppressions.sql
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
-- §11 tail (v1.2 Hierarchies): GROUP-level template suppression.
|
||||||
|
--
|
||||||
|
-- 0058 let an APP decline an inherited group template (for that app only). A
|
||||||
|
-- group operator often wants the same opt-out for a whole subtree: "no
|
||||||
|
-- descendant of this group runs the ancestor's `audit` template." Until now
|
||||||
|
-- only an app could suppress; a group could not.
|
||||||
|
--
|
||||||
|
-- Reshape `template_suppressions` to a POLYMORPHIC owner, exactly like the
|
||||||
|
-- group triggers/routes reshape (0056/0057) and the config markers (0051/0052):
|
||||||
|
-- * `group_id` — nullable FK→groups, ON DELETE CASCADE (config, not code).
|
||||||
|
-- * `app_id` — made nullable; the exactly-one CHECK enforces app XOR group.
|
||||||
|
-- * the per-app unique index becomes per-owner partials.
|
||||||
|
--
|
||||||
|
-- A GROUP suppression declines a template the group INHERITS from a higher
|
||||||
|
-- ancestor, for the group's whole subtree. Inheritance-only still holds: the
|
||||||
|
-- dispatch filters gate to group-owned rows, and a suppression only matches on
|
||||||
|
-- the suppressing owner's ancestor chain — a group can decline what it
|
||||||
|
-- inherits, never a sibling subtree's, never its own descendants' own rows.
|
||||||
|
|
||||||
|
ALTER TABLE template_suppressions
|
||||||
|
ADD COLUMN group_id UUID REFERENCES groups(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE template_suppressions
|
||||||
|
ALTER COLUMN app_id DROP NOT NULL;
|
||||||
|
|
||||||
|
ALTER TABLE template_suppressions
|
||||||
|
ADD CONSTRAINT template_suppressions_owner_exactly_one
|
||||||
|
CHECK ((group_id IS NULL) <> (app_id IS NULL));
|
||||||
|
|
||||||
|
DROP INDEX template_suppressions_uidx;
|
||||||
|
CREATE UNIQUE INDEX template_suppressions_app_uidx
|
||||||
|
ON template_suppressions (app_id, target_kind, reference) WHERE app_id IS NOT NULL;
|
||||||
|
CREATE UNIQUE INDEX template_suppressions_group_uidx
|
||||||
|
ON template_suppressions (group_id, target_kind, reference) WHERE group_id IS NOT NULL;
|
||||||
|
|
||||||
|
-- The trigger anti-join / route rebuild look up by both owners now.
|
||||||
|
CREATE INDEX template_suppressions_group_idx
|
||||||
|
ON template_suppressions (group_id) WHERE group_id IS NOT NULL;
|
||||||
@@ -379,10 +379,11 @@ table: secrets
|
|||||||
|
|
||||||
table: template_suppressions
|
table: template_suppressions
|
||||||
id: uuid NOT NULL default=gen_random_uuid()
|
id: uuid NOT NULL default=gen_random_uuid()
|
||||||
app_id: uuid NOT NULL
|
app_id: uuid NULL
|
||||||
target_kind: text NOT NULL
|
target_kind: text NOT NULL
|
||||||
reference: text NOT NULL
|
reference: text NOT NULL
|
||||||
created_at: timestamp with time zone NOT NULL default=now()
|
created_at: timestamp with time zone NOT NULL default=now()
|
||||||
|
group_id: uuid NULL
|
||||||
|
|
||||||
table: topics
|
table: topics
|
||||||
app_id: uuid NOT NULL
|
app_id: uuid NOT NULL
|
||||||
@@ -616,8 +617,10 @@ indexes on secrets:
|
|||||||
|
|
||||||
indexes on template_suppressions:
|
indexes on template_suppressions:
|
||||||
template_suppressions_app_idx: public.template_suppressions USING btree (app_id)
|
template_suppressions_app_idx: public.template_suppressions USING btree (app_id)
|
||||||
|
template_suppressions_app_uidx: public.template_suppressions USING btree (app_id, target_kind, reference) WHERE (app_id IS NOT NULL)
|
||||||
|
template_suppressions_group_idx: public.template_suppressions USING btree (group_id) WHERE (group_id IS NOT NULL)
|
||||||
|
template_suppressions_group_uidx: public.template_suppressions USING btree (group_id, target_kind, reference) WHERE (group_id IS NOT NULL)
|
||||||
template_suppressions_pkey: public.template_suppressions USING btree (id)
|
template_suppressions_pkey: public.template_suppressions USING btree (id)
|
||||||
template_suppressions_uidx: public.template_suppressions USING btree (app_id, target_kind, reference)
|
|
||||||
|
|
||||||
indexes on topics:
|
indexes on topics:
|
||||||
topics_pkey: public.topics USING btree (app_id, name)
|
topics_pkey: public.topics USING btree (app_id, name)
|
||||||
@@ -848,8 +851,10 @@ constraints on secrets:
|
|||||||
[FOREIGN KEY] secrets_group_id_fkey: FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE
|
[FOREIGN KEY] secrets_group_id_fkey: FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE
|
||||||
|
|
||||||
constraints on template_suppressions:
|
constraints on template_suppressions:
|
||||||
|
[CHECK] template_suppressions_owner_exactly_one: CHECK (((group_id IS NULL) <> (app_id IS NULL)))
|
||||||
[CHECK] template_suppressions_target_kind_check: CHECK ((target_kind = ANY (ARRAY['trigger'::text, 'route'::text])))
|
[CHECK] template_suppressions_target_kind_check: CHECK ((target_kind = ANY (ARRAY['trigger'::text, 'route'::text])))
|
||||||
[FOREIGN KEY] template_suppressions_app_id_fkey: FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE
|
[FOREIGN KEY] template_suppressions_app_id_fkey: FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE
|
||||||
|
[FOREIGN KEY] template_suppressions_group_id_fkey: FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE
|
||||||
[PRIMARY KEY] template_suppressions_pkey: PRIMARY KEY (id)
|
[PRIMARY KEY] template_suppressions_pkey: PRIMARY KEY (id)
|
||||||
|
|
||||||
constraints on topics:
|
constraints on topics:
|
||||||
@@ -934,3 +939,4 @@ constraints on vars:
|
|||||||
0057: group routes
|
0057: group routes
|
||||||
0058: template suppressions
|
0058: template suppressions
|
||||||
0059: sealed templates
|
0059: sealed templates
|
||||||
|
0060: group suppressions
|
||||||
|
|||||||
Reference in New Issue
Block a user