BEGIN; DROP INDEX runtime_config_one_published_key; CREATE TABLE ops_resource_reference ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), resource_type varchar(32) NOT NULL CHECK (resource_type IN ('PROVIDER_CREDENTIAL','VOICE_RESOURCE','MODEL_RESOURCE')), reference_name varchar(200) NOT NULL CHECK (reference_name ~ '^[A-Za-z][A-Za-z0-9_.:/-]{2,199}$'), display_name varchar(200) NOT NULL CHECK (length(trim(display_name))>0), status varchar(16) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE','REVOKED')), verified_at timestamptz NOT NULL, last_rotated_at timestamptz, created_by uuid NOT NULL, row_version bigint NOT NULL DEFAULT 1 CHECK (row_version>0), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT ops_resource_creator_tenant_fk FOREIGN KEY (created_by,tenant_id) REFERENCES user_account(id,tenant_id), CONSTRAINT ops_resource_reference_key UNIQUE (tenant_id,reference_name) ); CREATE INDEX ops_resource_reference_status_idx ON ops_resource_reference(tenant_id,status,resource_type); CREATE TABLE ops_config_evaluation ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), config_version_id uuid NOT NULL REFERENCES runtime_config_version(id) ON DELETE CASCADE, suite_version varchar(100) NOT NULL CHECK (length(trim(suite_version))>0), result varchar(16) NOT NULL CHECK (result IN ('PASSED','FAILED')), metrics jsonb NOT NULL CHECK (jsonb_typeof(metrics)='object'), report_ref varchar(300) NOT NULL CHECK (length(trim(report_ref))>0), evaluated_by uuid NOT NULL, evaluated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT ops_evaluation_actor_tenant_fk FOREIGN KEY (evaluated_by,tenant_id) REFERENCES user_account(id,tenant_id), CONSTRAINT ops_evaluation_version_suite_key UNIQUE (config_version_id,suite_version) ); CREATE INDEX ops_config_evaluation_result_idx ON ops_config_evaluation(tenant_id,config_version_id,result,evaluated_at DESC); CREATE TABLE ops_device_group ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), name varchar(200) NOT NULL CHECK (length(trim(name))>0), selector jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(selector)='object'), status varchar(16) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE','ARCHIVED')), created_by uuid NOT NULL, row_version bigint NOT NULL DEFAULT 1 CHECK (row_version>0), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT ops_device_group_creator_tenant_fk FOREIGN KEY (created_by,tenant_id) REFERENCES user_account(id,tenant_id), CONSTRAINT ops_device_group_id_tenant_key UNIQUE (id,tenant_id) ); CREATE UNIQUE INDEX ops_device_group_active_name_key ON ops_device_group(tenant_id,lower(name)) WHERE status='ACTIVE'; CREATE TABLE ops_device_group_member ( group_id uuid NOT NULL, tenant_id uuid NOT NULL, device_id uuid NOT NULL, added_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (group_id,device_id), CONSTRAINT ops_device_group_member_group_fk FOREIGN KEY (group_id,tenant_id) REFERENCES ops_device_group(id,tenant_id) ON DELETE CASCADE, CONSTRAINT ops_device_group_member_device_fk FOREIGN KEY (device_id,tenant_id) REFERENCES device(id,tenant_id) ); CREATE INDEX ops_device_group_member_device_idx ON ops_device_group_member(tenant_id,device_id); CREATE TABLE runtime_config_rollout ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), config_version_id uuid NOT NULL REFERENCES runtime_config_version(id), config_key varchar(100) NOT NULL, scope_type varchar(24) NOT NULL CHECK (scope_type IN ('TENANT','SUBJECT','DEVICE_GROUP')), subject_id uuid, device_group_id uuid, rollout_percent integer NOT NULL CHECK (rollout_percent BETWEEN 1 AND 100), max_concurrent_calls integer NOT NULL CHECK (max_concurrent_calls BETWEEN 1 AND 100000), cohort_seed integer NOT NULL DEFAULT 0, status varchar(16) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE','PAUSED','ENDED')), reason varchar(500) NOT NULL CHECK (length(trim(reason))>0), activated_by uuid NOT NULL, activated_at timestamptz NOT NULL DEFAULT now(), paused_at timestamptz, ended_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT runtime_rollout_actor_tenant_fk FOREIGN KEY (activated_by,tenant_id) REFERENCES user_account(id,tenant_id), CONSTRAINT runtime_rollout_subject_tenant_fk FOREIGN KEY (subject_id,tenant_id) REFERENCES subject_profile(id,tenant_id), CONSTRAINT runtime_rollout_group_tenant_fk FOREIGN KEY (device_group_id,tenant_id) REFERENCES ops_device_group(id,tenant_id), CONSTRAINT runtime_rollout_scope_check CHECK ( (scope_type='TENANT' AND subject_id IS NULL AND device_group_id IS NULL) OR (scope_type='SUBJECT' AND subject_id IS NOT NULL AND device_group_id IS NULL AND rollout_percent=100) OR (scope_type='DEVICE_GROUP' AND subject_id IS NULL AND device_group_id IS NOT NULL) ) ); CREATE UNIQUE INDEX runtime_rollout_active_tenant_key ON runtime_config_rollout(tenant_id,config_key) WHERE status='ACTIVE' AND scope_type='TENANT'; CREATE UNIQUE INDEX runtime_rollout_active_subject_key ON runtime_config_rollout(tenant_id,config_key,subject_id) WHERE status='ACTIVE' AND scope_type='SUBJECT'; CREATE UNIQUE INDEX runtime_rollout_active_group_key ON runtime_config_rollout(tenant_id,config_key,device_group_id) WHERE status='ACTIVE' AND scope_type='DEVICE_GROUP'; CREATE INDEX runtime_rollout_selection_idx ON runtime_config_rollout(tenant_id,config_key,status,scope_type); COMMIT;