Skip to content

Feature Flags

Feature flags are managed in the PostHog dashboard and evaluated in-process on both backend and frontend. The repository owns only the flag keys and their fail-open defaults — the value used when PostHog cannot answer.

Backend package: backend/flags/. Frontend hook: frontend/src/hooks/useFeatureFlag.ts.

Design

  • Single source of truth for keys: backend/flags/registry.py defines the FeatureFlag enum (the enum value is the PostHog flag key) and a FlagSpec per flag (key, description, default, scope).
  • Fail-open everywhere: if analytics is disabled, no key is configured, the flag is undefined, or evaluation errors, the code resolves to the flag's registry default. Flag evaluation never raises and never breaks the caller.
  • Scope: each flag is backend, frontend, or both.

Registered flags

Key Default Scope Purpose
debate true backend Global kill for the confidence-gated LLM debate stage
feed-fred true backend Disable the FRED macro feed (analysis degrades without it)
feed-news true backend Disable the news feed (news/sentiment analysts degrade)
frontend-catalyst true frontend Reference frontend kill-switch on the Catalyst surface

Kill latency for construction-time flags

Some flags (e.g. feed-fred) are evaluated once at object construction time, not per fetch — for example NewsAnalyst builds its EconomicCalendar at factory time. Kill latency therefore depends on when those objects are next reconstructed, not on the instant the flag flips in PostHog.

Backend evaluation

from backend.flags.registry import FeatureFlag
from backend.flags import service as flags

if flags.is_enabled(FeatureFlag.FEED_NEWS):
    ...  # news feed path

is_enabled(flag, *, distinct_id=None) (backend/flags/service.py) wraps the shared PostHog client with local evaluation (only_evaluate_locally=True) and falls back to spec_for(flag).default. When no distinct_id is supplied it evaluates as the "system" identity.

Frontend evaluation

import { useFeatureFlag } from "@/hooks/useFeatureFlag";

const catalystEnabled = useFeatureFlag("frontend-catalyst"); // fail-open true

useFeatureFlag(key, defaultValue = true) reads a PostHog flag reactively via @/lib/analytics/posthog, returning defaultValue until flags load and whenever the flag is undefined.

Adding a flag

  1. Add an entry to the FeatureFlag enum and a matching FlagSpec in backend/flags/registry.py (set a sensible fail-open default and the correct scope).
  2. Create the flag in the PostHog dashboard using the same key.
  3. Gate code with flags.is_enabled(FeatureFlag.YOUR_FLAG) (backend) or useFeatureFlag("your-flag") (frontend).