-- Phase 3 (v1.1.9): group-owned, environment-scoped secrets.
--
-- Until now `secrets` was strictly per-app: PK `(app_id, name)`, one
-- envelope per app. Phase 3 makes secrets inheritable down the group tree
-- (blueprint §11 bullet 3 / docs/design §3), exactly like `vars` (0048):
-- a secret may be owned by an app OR an ancestor group, and a descendant
-- app resolves the nearest one, environment-filtered.
--
-- Reshape (mirrors `vars`):
--   * `group_id`  — nullable FK→groups, CASCADE (a deleted group drops its
--     secrets, same as its vars).
--   * `app_id`    — made nullable (was NOT NULL); the exactly-one CHECK now
--     enforces "owned by an app XOR a group".
--   * `environment_scope` — `'*'` (env-agnostic) or a concrete env name;
--     the resolver filters on it. Existing rows backfill to `'*'`, so every
--     current app secret stays env-agnostic and resolves unchanged.
--   * PK `(app_id, name)` → two PARTIAL unique indexes, one per owner, both
--     keyed `(owner, environment_scope, name)`.
--
-- CRYPTO INVARIANT (audit 2026-06-11 H-D1): the v1 AAD is
-- `secret:{app_id}:{name}` for app secrets and `secret:group:{group_id}:{name}`
-- for group secrets — it does NOT include `environment_scope`. Adding the
-- column therefore leaves every existing ciphertext decryptable byte-for-byte;
-- the app-owner AAD is unchanged and the group namespace is disjoint.

ALTER TABLE secrets
    ADD COLUMN group_id UUID REFERENCES groups(id) ON DELETE CASCADE,
    ADD COLUMN environment_scope TEXT NOT NULL DEFAULT '*';

-- Drop the old composite PK first: `app_id` cannot lose NOT NULL while it
-- is a primary-key column.
ALTER TABLE secrets
    DROP CONSTRAINT secrets_pkey;

ALTER TABLE secrets
    ALTER COLUMN app_id DROP NOT NULL;

ALTER TABLE secrets
    ADD CONSTRAINT secrets_owner_exactly_one
        CHECK ((group_id IS NULL) <> (app_id IS NULL));

-- One secret per (owner, env, name). Partial so each owner column only
-- constrains its own rows; the resolver and every upsert restate the
-- predicate as the ON CONFLICT arbiter.
CREATE UNIQUE INDEX secrets_app_uidx
    ON secrets (app_id, environment_scope, name) WHERE app_id IS NOT NULL;
CREATE UNIQUE INDEX secrets_group_uidx
    ON secrets (group_id, environment_scope, name) WHERE group_id IS NOT NULL;

-- Owner lookup index for the group side (the app side keeps idx_secrets_app).
CREATE INDEX idx_secrets_group ON secrets (group_id) WHERE group_id IS NOT NULL;
