-- §5.5 (v1.2 Hierarchies): extension-point markers — opt-in module polymorphism. -- -- An extension point marks a module NAME at a node (app or group) as one -- descendants are *expected* to provide or override. Imports of an EP name -- resolve DYNAMICALLY against the inheriting app's view (the app can supply -- its own module), instead of the Phase 4b lexical default (sealed to the -- importing script's defining node). See docs §5.5. -- -- This table holds only the MARKER (owner, name) — it is pure declaration, -- structurally identical to a `secrets` name. The optional DEFAULT BODY is -- just a co-located `kind = 'module'` script of the same name at this node -- (Phase 4b already stores/resolves/caches those); there is no body column -- here. So a marker is config, not code → ON DELETE CASCADE (unlike the -- module body's RESTRICT in 0050). -- -- Ownership is polymorphic (mirrors vars/secrets/scripts): exactly one of -- (app_id, group_id) is set, with per-owner partial-unique LOWER(name) -- indexes for case-insensitive uniqueness. CREATE TABLE extension_points ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id UUID REFERENCES groups(id) ON DELETE CASCADE, app_id UUID REFERENCES apps(id) ON DELETE CASCADE, CONSTRAINT extension_points_owner_exactly_one CHECK ((group_id IS NULL) <> (app_id IS NULL)), name TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- One marker per (owner, name), case-insensitive. Partial because the owner -- is split across two nullable columns. CREATE UNIQUE INDEX extension_points_group_uidx ON extension_points (group_id, LOWER(name)) WHERE group_id IS NOT NULL; CREATE UNIQUE INDEX extension_points_app_uidx ON extension_points (app_id, LOWER(name)) WHERE app_id IS NOT NULL; -- Lookup indexes for the resolver's chain join + list-by-owner. CREATE INDEX extension_points_group_id_idx ON extension_points (group_id) WHERE group_id IS NOT NULL; CREATE INDEX extension_points_app_id_idx ON extension_points (app_id) WHERE app_id IS NOT NULL;