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.pydefines theFeatureFlagenum (the enum value is the PostHog flag key) and aFlagSpecper 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, orboth.
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¶
- Add an entry to the
FeatureFlagenum and a matchingFlagSpecinbackend/flags/registry.py(set a sensible fail-opendefaultand the correctscope). - Create the flag in the PostHog dashboard using the same key.
- Gate code with
flags.is_enabled(FeatureFlag.YOUR_FLAG)(backend) oruseFeatureFlag("your-flag")(frontend).