CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE TABLE IF NOT EXISTS model_catalog_providers ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), provider_key text NOT NULL UNIQUE, display_name text NOT NULL, provider_type text NOT NULL DEFAULT 'openai_compatible', capability_schema jsonb NOT NULL DEFAULT '{}'::jsonb, default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'active', metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_model_catalog_provider_status ON model_catalog_providers(status); CREATE TABLE IF NOT EXISTS base_model_catalog ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), provider_id uuid REFERENCES model_catalog_providers(id) ON DELETE SET NULL, provider_key text NOT NULL, canonical_model_key text NOT NULL UNIQUE, provider_model_name text NOT NULL, model_type text NOT NULL, display_name text NOT NULL, capabilities jsonb NOT NULL DEFAULT '{}'::jsonb, base_billing_config jsonb NOT NULL DEFAULT '{}'::jsonb, default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb, pricing_version integer NOT NULL DEFAULT 1, status text NOT NULL DEFAULT 'active', metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_base_model_catalog_provider ON base_model_catalog(provider_key, model_type, status); CREATE INDEX IF NOT EXISTS idx_base_model_catalog_capabilities ON base_model_catalog USING gin(capabilities); CREATE TABLE IF NOT EXISTS integration_platforms ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), provider text NOT NULL, platform_key text NOT NULL UNIQUE DEFAULT ('platform_' || replace(gen_random_uuid()::text, '-', '')), name text NOT NULL, base_url text, auth_type text NOT NULL DEFAULT 'bearer', credentials jsonb NOT NULL DEFAULT '{}'::jsonb, config jsonb NOT NULL DEFAULT '{}'::jsonb, visibility_scope text NOT NULL DEFAULT 'global', tenant_id text, tenant_key text, default_pricing_mode text NOT NULL DEFAULT 'inherit_discount', default_discount_factor numeric NOT NULL DEFAULT 1, retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb, rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb, priority integer NOT NULL DEFAULT 100, dynamic_priority integer, status text NOT NULL DEFAULT 'enabled', disabled_reason text, cooldown_until timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz ); CREATE INDEX IF NOT EXISTS idx_integration_platforms_provider_status ON integration_platforms(provider, status); CREATE INDEX IF NOT EXISTS idx_integration_platforms_status_priority ON integration_platforms(status, priority, dynamic_priority); CREATE INDEX IF NOT EXISTS idx_integration_platforms_cooldown ON integration_platforms(cooldown_until); CREATE INDEX IF NOT EXISTS idx_integration_platforms_tenant_scope ON integration_platforms(visibility_scope, tenant_id, tenant_key, status); CREATE TABLE IF NOT EXISTS model_pricing_rules ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), scope_type text NOT NULL, scope_id uuid, resource_type text NOT NULL, unit text NOT NULL, base_price numeric NOT NULL, currency text NOT NULL DEFAULT 'resource', base_weight jsonb NOT NULL DEFAULT '{}'::jsonb, dynamic_weight jsonb NOT NULL DEFAULT '{}'::jsonb, effective_from timestamptz, effective_to timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_model_pricing_scope ON model_pricing_rules(scope_type, scope_id, resource_type); CREATE INDEX IF NOT EXISTS idx_model_pricing_effective ON model_pricing_rules(effective_from, effective_to); CREATE TABLE IF NOT EXISTS gateway_user_groups ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), group_key text NOT NULL UNIQUE, name text NOT NULL, description text, source text NOT NULL DEFAULT 'gateway', priority integer NOT NULL DEFAULT 100, recharge_discount_policy jsonb NOT NULL DEFAULT '{}'::jsonb, billing_discount_policy jsonb NOT NULL DEFAULT '{}'::jsonb, rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb, quota_policy jsonb NOT NULL DEFAULT '{}'::jsonb, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'active', created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_gateway_user_groups_status_priority ON gateway_user_groups(status, priority); CREATE TABLE IF NOT EXISTS gateway_tenants ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_key text NOT NULL UNIQUE, source text NOT NULL DEFAULT 'gateway', external_tenant_id text, name text NOT NULL, description text, default_user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL, plan_key text, billing_profile jsonb NOT NULL DEFAULT '{}'::jsonb, rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb, auth_policy jsonb NOT NULL DEFAULT '{}'::jsonb, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'active', synced_at timestamptz, source_updated_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, UNIQUE(source, external_tenant_id) ); CREATE INDEX IF NOT EXISTS idx_gateway_tenants_source_external ON gateway_tenants(source, external_tenant_id) WHERE external_tenant_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_gateway_tenants_status ON gateway_tenants(status, created_at DESC); CREATE TABLE IF NOT EXISTS gateway_users ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_key text NOT NULL UNIQUE, source text NOT NULL DEFAULT 'gateway', external_user_id text, username text NOT NULL, display_name text, email text, phone text, avatar_url text, password_hash text, gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL, tenant_id text, tenant_key text, default_user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL, roles jsonb NOT NULL DEFAULT '[]'::jsonb, auth_profile jsonb NOT NULL DEFAULT '{}'::jsonb, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'active', last_login_at timestamptz, synced_at timestamptz, source_updated_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, UNIQUE(source, external_user_id) ); CREATE INDEX IF NOT EXISTS idx_gateway_users_source_external ON gateway_users(source, external_user_id) WHERE external_user_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_gateway_users_status ON gateway_users(status, created_at DESC); CREATE INDEX IF NOT EXISTS idx_gateway_users_tenant ON gateway_users(tenant_id, tenant_key, status); CREATE TABLE IF NOT EXISTS gateway_user_group_memberships ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), group_id uuid NOT NULL REFERENCES gateway_user_groups(id) ON DELETE CASCADE, principal_type text NOT NULL, principal_id text NOT NULL, source text NOT NULL DEFAULT 'gateway', priority integer NOT NULL DEFAULT 100, effective_from timestamptz, effective_to timestamptz, status text NOT NULL DEFAULT 'active', metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(group_id, principal_type, principal_id) ); CREATE INDEX IF NOT EXISTS idx_user_group_membership_principal ON gateway_user_group_memberships(principal_type, principal_id, status); CREATE INDEX IF NOT EXISTS idx_user_group_membership_effective ON gateway_user_group_memberships(effective_from, effective_to); CREATE TABLE IF NOT EXISTS gateway_tenant_invitations ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES gateway_tenants(id) ON DELETE CASCADE, invite_code text NOT NULL UNIQUE, role text NOT NULL DEFAULT 'user', user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL, max_uses integer, used_count integer NOT NULL DEFAULT 0, expires_at timestamptz, status text NOT NULL DEFAULT 'active', metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_by uuid REFERENCES gateway_users(id) ON DELETE SET NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_gateway_invitations_tenant ON gateway_tenant_invitations(tenant_id, status); CREATE INDEX IF NOT EXISTS idx_gateway_invitations_expiry ON gateway_tenant_invitations(expires_at); CREATE TABLE IF NOT EXISTS gateway_api_keys ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL, gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE CASCADE, tenant_id text, tenant_key text, user_id text, key_prefix text NOT NULL, key_hash text NOT NULL UNIQUE, name text NOT NULL, scopes jsonb NOT NULL DEFAULT '[]'::jsonb, user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL, rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb, quota_policy jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'active', expires_at timestamptz, last_used_at timestamptz, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz ); CREATE INDEX IF NOT EXISTS idx_gateway_api_keys_owner ON gateway_api_keys(gateway_user_id, status, created_at DESC); CREATE INDEX IF NOT EXISTS idx_gateway_api_keys_prefix ON gateway_api_keys(key_prefix, status); CREATE TABLE IF NOT EXISTS gateway_wallet_accounts ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL, gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE CASCADE, tenant_id text, tenant_key text, user_id text, currency text NOT NULL DEFAULT 'resource', balance numeric NOT NULL DEFAULT 0, frozen_balance numeric NOT NULL DEFAULT 0, total_recharged numeric NOT NULL DEFAULT 0, total_spent numeric NOT NULL DEFAULT 0, status text NOT NULL DEFAULT 'active', metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(gateway_user_id, currency) ); CREATE INDEX IF NOT EXISTS idx_gateway_wallet_accounts_tenant ON gateway_wallet_accounts(gateway_tenant_id, status); CREATE TABLE IF NOT EXISTS gateway_wallet_transactions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), account_id uuid NOT NULL REFERENCES gateway_wallet_accounts(id) ON DELETE CASCADE, gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL, gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE SET NULL, direction text NOT NULL, transaction_type text NOT NULL, amount numeric NOT NULL, balance_before numeric NOT NULL, balance_after numeric NOT NULL, idempotency_key text, reference_type text, reference_id text, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_gateway_wallet_transactions_account ON gateway_wallet_transactions(account_id, created_at DESC); CREATE UNIQUE INDEX IF NOT EXISTS uniq_gateway_wallet_tx_idempotency ON gateway_wallet_transactions(account_id, idempotency_key) WHERE idempotency_key IS NOT NULL; CREATE TABLE IF NOT EXISTS gateway_recharge_orders ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL, gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE CASCADE, tenant_id text, tenant_key text, user_id text, amount numeric NOT NULL, bonus_amount numeric NOT NULL DEFAULT 0, payable_amount numeric NOT NULL, currency text NOT NULL DEFAULT 'resource', channel text NOT NULL DEFAULT 'manual', status text NOT NULL DEFAULT 'created', external_order_id text, idempotency_key text, paid_at timestamptz, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_gateway_recharge_orders_user ON gateway_recharge_orders(gateway_user_id, created_at DESC); CREATE UNIQUE INDEX IF NOT EXISTS uniq_gateway_recharge_order_idempotency ON gateway_recharge_orders(gateway_user_id, idempotency_key) WHERE idempotency_key IS NOT NULL; CREATE TABLE IF NOT EXISTS platform_models ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), platform_id uuid NOT NULL REFERENCES integration_platforms(id) ON DELETE CASCADE, base_model_id uuid REFERENCES base_model_catalog(id) ON DELETE SET NULL, model_name text NOT NULL, model_alias text, model_type text NOT NULL, display_name text NOT NULL DEFAULT '', capability_override jsonb NOT NULL DEFAULT '{}'::jsonb, capabilities jsonb NOT NULL DEFAULT '{}'::jsonb, pricing_mode text NOT NULL DEFAULT 'inherit_discount', discount_factor numeric, billing_config_override jsonb NOT NULL DEFAULT '{}'::jsonb, billing_config jsonb NOT NULL DEFAULT '{}'::jsonb, permission_config jsonb NOT NULL DEFAULT '{}'::jsonb, retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb, rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb, enabled boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(platform_id, model_name, model_type) ); CREATE INDEX IF NOT EXISTS idx_platform_models_base ON platform_models(base_model_id); CREATE INDEX IF NOT EXISTS idx_platform_models_lookup ON platform_models(model_type, model_name, enabled); CREATE INDEX IF NOT EXISTS idx_platform_models_alias ON platform_models(model_alias); CREATE INDEX IF NOT EXISTS idx_platform_models_capabilities ON platform_models USING gin(capabilities); CREATE TABLE IF NOT EXISTS gateway_tasks ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), external_task_id text, kind text NOT NULL, run_mode text NOT NULL DEFAULT 'production', user_id text NOT NULL, gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE SET NULL, user_source text NOT NULL DEFAULT 'gateway', gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL, tenant_id text, tenant_key text, api_key_id text, user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL, user_group_key text, user_group_policy_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb, model text NOT NULL, model_type text, request jsonb NOT NULL DEFAULT '{}'::jsonb, normalized_request jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'queued', queue_key text NOT NULL DEFAULT 'default', priority integer NOT NULL DEFAULT 100, idempotency_key text, remote_task_id text, remote_task_payload jsonb, simulation_profile jsonb, simulation_seed text, locked_by text, locked_at timestamptz, heartbeat_at timestamptz, next_run_at timestamptz NOT NULL DEFAULT now(), attempt_count integer NOT NULL DEFAULT 0, max_attempts integer NOT NULL DEFAULT 1, result jsonb, billings jsonb, error text, error_code text, error_message text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz ); CREATE INDEX IF NOT EXISTS idx_gateway_tasks_queue ON gateway_tasks(status, next_run_at, priority, created_at); CREATE INDEX IF NOT EXISTS idx_gateway_tasks_lease ON gateway_tasks(status, heartbeat_at); CREATE INDEX IF NOT EXISTS idx_gateway_tasks_user_created ON gateway_tasks(user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_gateway_tasks_external ON gateway_tasks(external_task_id); CREATE UNIQUE INDEX IF NOT EXISTS uniq_gateway_tasks_idempotency ON gateway_tasks(user_id, idempotency_key) WHERE idempotency_key IS NOT NULL; CREATE TABLE IF NOT EXISTS gateway_task_attempts ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE, attempt_no integer NOT NULL, platform_id uuid REFERENCES integration_platforms(id) ON DELETE SET NULL, platform_model_id uuid REFERENCES platform_models(id) ON DELETE SET NULL, client_id text, queue_key text NOT NULL, status text NOT NULL, retryable boolean NOT NULL DEFAULT false, simulated boolean NOT NULL DEFAULT false, remote_task_id text, request_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb, response_snapshot jsonb, error_code text, error_message text, started_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz, UNIQUE(task_id, attempt_no) ); CREATE INDEX IF NOT EXISTS idx_gateway_attempts_task ON gateway_task_attempts(task_id); CREATE INDEX IF NOT EXISTS idx_gateway_attempts_client ON gateway_task_attempts(client_id, started_at DESC); CREATE TABLE IF NOT EXISTS gateway_task_events ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE, seq bigint NOT NULL, event_type text NOT NULL, status text, phase text, progress numeric, message text, payload jsonb NOT NULL DEFAULT '{}'::jsonb, simulated boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE(task_id, seq) ); CREATE INDEX IF NOT EXISTS idx_gateway_events_task_created ON gateway_task_events(task_id, created_at); CREATE TABLE IF NOT EXISTS gateway_task_callback_outbox ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE, event_id uuid REFERENCES gateway_task_events(id) ON DELETE SET NULL, seq bigint NOT NULL, callback_url text NOT NULL, payload jsonb NOT NULL, status text NOT NULL DEFAULT 'pending', attempts integer NOT NULL DEFAULT 0, next_attempt_at timestamptz NOT NULL DEFAULT now(), last_error text, delivered_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(task_id, seq, callback_url) ); CREATE INDEX IF NOT EXISTS idx_task_callback_outbox_pending ON gateway_task_callback_outbox(status, next_attempt_at); CREATE INDEX IF NOT EXISTS idx_task_callback_outbox_task ON gateway_task_callback_outbox(task_id, seq); CREATE TABLE IF NOT EXISTS runtime_client_states ( client_id text PRIMARY KEY, platform_id uuid REFERENCES integration_platforms(id) ON DELETE SET NULL, provider text NOT NULL, method_name text NOT NULL, queue_key text NOT NULL, running_count integer NOT NULL DEFAULT 0, waiting_count integer NOT NULL DEFAULT 0, limiter_ratio numeric NOT NULL DEFAULT 0, cooldown_until timestamptz, last_assigned_at timestamptz, last_error text, updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_runtime_client_queue ON runtime_client_states(queue_key, cooldown_until); CREATE INDEX IF NOT EXISTS idx_runtime_client_platform ON runtime_client_states(platform_id); CREATE TABLE IF NOT EXISTS gateway_upload_assets ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), task_id uuid REFERENCES gateway_tasks(id) ON DELETE SET NULL, source text NOT NULL, server_main_file_id text, url text NOT NULL, object_key text, content_type text, size bigint, checksum text, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_gateway_upload_task ON gateway_upload_assets(task_id); CREATE INDEX IF NOT EXISTS idx_gateway_upload_file ON gateway_upload_assets(server_main_file_id); CREATE TABLE IF NOT EXISTS gateway_retry_policies ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), scope_type text NOT NULL, scope_key text NOT NULL, enabled boolean NOT NULL DEFAULT true, policy jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(scope_type, scope_key) ); CREATE TABLE IF NOT EXISTS gateway_rate_limit_policies ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), scope_type text NOT NULL, scope_key text NOT NULL, policy jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(scope_type, scope_key) ); CREATE TABLE IF NOT EXISTS gateway_rate_limit_counters ( scope_type text NOT NULL, scope_key text NOT NULL, metric text NOT NULL, window_start timestamptz NOT NULL, limit_value numeric NOT NULL, used_value numeric NOT NULL DEFAULT 0, reserved_value numeric NOT NULL DEFAULT 0, reset_at timestamptz NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY(scope_type, scope_key, metric, window_start) ); CREATE TABLE IF NOT EXISTS gateway_concurrency_leases ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE, attempt_id uuid REFERENCES gateway_task_attempts(id) ON DELETE SET NULL, scope_type text NOT NULL, scope_key text NOT NULL, lease_value numeric NOT NULL DEFAULT 1, acquired_at timestamptz NOT NULL DEFAULT now(), expires_at timestamptz NOT NULL, released_at timestamptz ); CREATE INDEX IF NOT EXISTS idx_concurrency_leases_active ON gateway_concurrency_leases(scope_type, scope_key, released_at, expires_at); CREATE INDEX IF NOT EXISTS idx_concurrency_leases_task ON gateway_concurrency_leases(task_id); CREATE TABLE IF NOT EXISTS settlement_outbox ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE, event_type text NOT NULL DEFAULT 'task.settlement.requested', payload jsonb NOT NULL, status text NOT NULL DEFAULT 'pending', attempts integer NOT NULL DEFAULT 0, next_attempt_at timestamptz NOT NULL DEFAULT now(), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(task_id, event_type) );