-- +mixmaker Up CREATE TABLE discord_managed_roles ( scope text NOT NULL CHECK (scope IN ('global', 'event')), event_id text NOT NULL DEFAULT '', team_id text NOT NULL DEFAULT '', kind text NOT NULL CHECK (kind IN ('tank', 'damage', 'support', 'team', 'captain')), discord_role_id text NOT NULL UNIQUE, role_name text NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (scope, event_id, team_id, kind) ); CREATE TABLE discord_role_assignments ( discord_role_id text NOT NULL REFERENCES discord_managed_roles(discord_role_id) ON DELETE CASCADE, discord_user_id text NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (discord_role_id, discord_user_id) ); CREATE TABLE discord_role_sync_jobs ( id bigserial PRIMARY KEY, event_id text NOT NULL, action text NOT NULL CHECK (action IN ('reconcile', 'teardown')), generation bigint NOT NULL, role_snapshot jsonb NOT NULL DEFAULT '[]'::jsonb, status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed')), attempts integer NOT NULL DEFAULT 0, available_at timestamptz NOT NULL DEFAULT now(), last_error text NOT NULL DEFAULT '', created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), completed_at timestamptz, UNIQUE (event_id, action, generation) ); CREATE INDEX discord_role_sync_jobs_claim_idx ON discord_role_sync_jobs(status, available_at, id); -- +mixmaker Down DROP TABLE IF EXISTS discord_role_sync_jobs; DROP TABLE IF EXISTS discord_role_assignments; DROP TABLE IF EXISTS discord_managed_roles;