-- Tier 2 cross-cutting: feature flags. -- -- Lets ops kill a misbehaving block without a redeploy + supports -- percentage rollouts ("turn checkin_pwa on for 25% of users while we -- watch the error rate"). Flag values are loaded on every check — -- they're tiny, and we want to be able to flip a switch and see the -- next request honour it without restart. -- -- The default state of any unknown flag is "enabled" so the table -- only needs rows for the explicit kills + rollouts. This is the -- right safe default for code that gates a NEW feature: the -- developer wires the gate, ships, the feature is live; ops can -- write a row to take it back later without redeploy. CREATE TABLE feature_flags ( key TEXT PRIMARY KEY, enabled BOOLEAN NOT NULL DEFAULT TRUE, percent_rollout SMALLINT NOT NULL DEFAULT 100 CHECK (percent_rollout >= 0 AND percent_rollout <= 100), note TEXT, updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Pre-seed the Tier 2 flags so an operator can flip them without -- having to remember the canonical key. INSERT INTO feature_flags (key, enabled, percent_rollout, note) VALUES ('editable_rsvp', TRUE, 100, 'Tier 2 Block A — guests can edit their RSVP via PATCH /rsvp.'), ('checkin_pwa', TRUE, 100, 'Tier 2 Block H — day-of scanner + magic-link ticket.'), ('smarter_fraud', TRUE, 100, 'Tier 2 Block G — per-event thresholds, allowlists, feedback.'), ('geo_jump', TRUE, 100, 'Tier 2 Block G — geo_jump scoring feature on top of GeoIP.'), ('broadcasts', TRUE, 100, 'Tier 2 Block F — custom broadcasts on top of auto-reminders.'), ('custom_branding', TRUE, 100, 'Tier 2 Block D — per-event logo / cover / colour overrides.') ON CONFLICT (key) DO NOTHING;