Raxx · internal docs

internal · gated

Antlers flag RBAC audit — 2026-05-15 UTC

Auditor: qa-agent
Dispatched by: Kristerpher Henderson
Date: 2026-05-15 UTC
Refs: #1449, docs/architecture/rbac-antlers-surface-audit.md (PR #2082)
Invariant under test: Every customer-facing flag surface in the Antlers React app must hide denied features, never gray them out or badge them "Coming soon."
Memory anchor: feedback_hide_dont_gray_unavailable_features, locked 2026-05-11 UTC.


Method

Static code audit of frontend/trademaster_ui/src/ — the live Playwright environment was not available in this run (no dev server running in this worktree). All findings are grounded in the component source and confirmed against the issue thread. The prior architecture doc (docs/architecture/rbac-antlers-surface-audit.md) was used as a reference baseline; this audit independently re-derives conclusions from source.

Grep patterns used: useFlag, isEnabled, Coming soon, disabled, opacity, pointer-events, bg.*secondary, badge.*secondary, subscription_required.

Scope: surface: antlers flags in feature_flags.yaml plus flags in App.js controlling routing. Analytics toggles (demo_posthog_events, demo_clarity, landing_posthog_events, render_id_emission) and console flags were excluded — they have no customer-visible denied state.

Total flag call sites reviewed: 28 across 14 source files.


Surfaces reviewed

# Surface Flag File Denied behavior Verdict
1 Settings → Platform Features tab — Options Backtesting row options_backtest Settings.js L617–624 Flag OFF: row renders with gray secondary Badge reading "Coming soon" and a tooltip. Row is always visible. FAIL
2 Settings → Platform Features tab — Live Mode Ring row live_mode_ring Settings.js L627–640 Flag OFF: row renders with gray secondary Badge reading "Coming soon." Row is always visible. FAIL
3 Settings → Sentinel ring nav item live_mode_ring Settings.js L348–355 Flag OFF: nav item is hidden via {isEnabled('live_mode_ring') && …}. Correct. PASS
4 LiveModeRing overlay component live_mode_ring LiveModeRing.js L171–177 Flag OFF: visible = false; ring does not render. Hidden. PASS
5 /trade route — Trade Window link in header trade_window_v1 Header.js L553–561 Flag OFF: Nav.Link not rendered. Hidden by absence. PASS
6 TradeWindow page component trade_window_v1 TradeWindow.js L171 Flag OFF: component returns null. Hidden. PASS
7 RouteGuard — session-aware redirect table route_guard RouteGuard.js L107–112 Flag OFF: guard is bypassed; children render ungated. Not a denied-state display issue — a v1 blocker on ops side. PASS (display)
8 /signup route passkey_signup_ui App.js L181–183 Flag OFF: route not registered. Hidden by absence. PASS
9 /login passkey flow passkey_login_ui App.js L347 (shell branch) Flag OFF: login route not registered. Hidden by absence. PASS
10 /verify-email/* routes email_verification_ui App.js L184–189 Flag OFF: routes not registered. Hidden by absence. PASS
11 /onboarding/* wizard routes onboarding_wizard_v1 App.js L202–213 Flag OFF: route tree not registered. Hidden by absence. PASS
12 /demo visitor flow route demo_flow_ui App.js L194–196 Flag OFF: route not registered. Hidden by absence. PASS
13 Dashboard — DashboardCE vs Dashboard antlers_visual_port_v1 App.js L222 Flag OFF: Dashboard.js renders instead. No denied state — fallback is another working page. PASS
14 Full-bleed app shell raxx_app_shell App.js L332 Flag OFF: legacy Bootstrap shell renders. No denied state. PASS
15 Dashboard — first-run rail dashboard_first_run DashboardCE.js L233–262 Flag OFF: first-run rail absent; standard dashboard renders. Hidden. PASS
16 Header tray — obfuscate toggle antlers_obfuscate_mode Header.js L56 Flag OFF: toggle absent from header. Hidden. PASS
17 Header tray — help drawer icon help_drawer_v1 Header.js L57, RaxxAppShell.js L47 Flag OFF: ? icon absent. Hidden. PASS
18 Header tray — v2.1 help icon help_icon_v21 Header.js L58, RaxxAppShell.js L48 Flag OFF: new icon absent; v2.0 icon or nothing renders. Hidden. PASS
19 Demo overlay CTA variant demo_founders_cta_variant DemoConversionOverlay.js L106 Flag OFF: standard CTA renders. No denied state. PASS
20 Demo mode kill-switch antlers_demo_mode DemoContext.js L28 Flag OFF: demo mode disabled globally. No UI denied state. PASS (but see Finding F-3 below — flag missing from YAML)
21 Quebec signup block quebec_geoblock SignupPageEmailFormStep Flag OFF: province check skipped; no block card shown. No denied state — ops flip before launch. PASS (display)
22 EU signup block signup_geoblock_eu PasskeyEnrollStep path Flag ON (default): EU block card renders when EU jurisdiction detected. No gray-out — full replacement card. PASS
23 OptionsBacktestComingSoon component ENABLE_OPTIONS_BACKTEST OptionsBacktestComingSoon.js Component exists, renders a "coming soon" card — but has no non-test caller in current source. Dead component in current build. PASS (not wired) — see Finding F-4
24 DataFeed subscription_required badge N/A (feed metadata) DataFeedSelector.js L136, DataFeedStatus.js L290, Settings.js L394 Warning badge on subscription-required feeds. This is informational metadata on a user-controlled feed config, not a feature-flag denied state. Users can still select the feed. Not a hide-don't-gray violation. PASS
25 Backtesting feed disabled option N/A (user config) Backtesting.js L283 <option disabled> for user-disabled data feeds. User-controlled config, not a feature-flag gate. Not a hide-don't-gray violation. PASS

Findings

F-1 (FAIL) — Settings → "Coming soon" badge: options_backtest

File: frontend/trademaster_ui/src/pages/Settings.js, lines 617–624
Behavior: When options_backtest is OFF, the "Options Backtesting" row in Settings → Platform Features renders with a gray Badge bg="secondary" reading "Coming soon" and an OverlayTrigger tooltip explaining the licensing status. The row is unconditionally visible to all authenticated users.
Violation: The invariant requires hidden, not badged. The tooltip copy ("Data licensing in progress — options historical data will unlock this feature") compounds the violation by framing the feature as locked/unavailable-for-upgrade.
Required fix: Wrap the entire feature-flag-row div in {isEnabled('options_backtest') && (…)} so the row is absent when the flag is OFF. Remove or relocate the introductory copy at line ~600 ("Features shown as 'Coming soon' are in active development…") — once the rows are hidden, this copy describes a pattern that no longer exists.
Follow-up issue: see below.

F-2 (FAIL) — Settings → "Coming soon" badge: live_mode_ring

File: frontend/trademaster_ui/src/pages/Settings.js, lines 627–640
Behavior: When live_mode_ring is OFF, the "Live Mode Ring" row renders with a gray Badge bg="secondary" reading "Coming soon." Row is unconditionally visible.
Violation: Same pattern as F-1.
Note: The Sentinel ring nav item in the Settings left nav (line 348) is already correctly gated with {isEnabled('live_mode_ring') && …}. This nav item passes. Only the Platform Features tab row is failing.
Required fix: Wrap the feature-flag-row div at line 627 in {isEnabled('live_mode_ring') && (…)}.
Follow-up issue: same issue as F-1 (both fixes belong in one card).

F-3 (FLAG GAP) — antlers_demo_mode missing from feature_flags.yaml

File: frontend/trademaster_ui/src/context/DemoContext.js, line 28
Behavior: DemoContext.js calls isEnabled('antlers_demo_mode') as a kill-switch. This flag name is absent from backend_v2/api/feature_flags.yaml. When window.__FLAGS__ does not define it, the flag silently resolves to false — demo mode is globally disabled regardless of hostname or URL param.
Classification: Not a denied-state display violation. A flag inventory gap. Demo mode being silently off could mask a bug if demo_flow_ui is flipped ON for testing.
Required action: Operator must decide — (a) add antlers_demo_mode to feature_flags.yaml with a B1 migration row, or (b) remove the isEnabled('antlers_demo_mode') call and derive kill-switch from demo_flow_ui directly. The decision must happen before the demo surface is live.
Follow-up issue: separate card from F-1/F-2.

F-4 (DEAD CODE) — OptionsBacktestComingSoon component is unwired

File: frontend/trademaster_ui/src/components/OptionsBacktestComingSoon.js
Behavior: Component renders a full-page "Options backtesting — coming soon" card. Its utils/featureFlags.js docstring says callers should render this component when isOptionsBacktestEnabled is false. But no non-test file imports or renders this component in the current codebase. The component would be a violation of the invariant if wired in.
Classification: Not currently a live violation (the component is dead). But it is a trap — a future developer could wire it in and reintroduce the "coming soon" pattern.
Required action: Either delete the component (preferred, since its purpose conflicts with the invariant), or add a code comment explicitly prohibiting its use per the 2026-05-11 hide-don't-gray decision and open a card to remove it cleanly.
Follow-up issue: low-urgency cleanup, but should not survive to v1 launch given it is a "coming soon" component by design.


Follow-up issues filed

Two issues filed from this audit:

  1. [F-1 + F-2] Settings "Coming soon" badges must be hidden — area:frontend, type:bug, v1 blocker
    Hide both options_backtest and live_mode_ring rows in Settings → Platform Features tab when their flags are OFF. Remove the introductory "features shown as Coming soon" copy. ~5 lines of JSX per row.

  2. [F-3] antlers_demo_mode flag missing from feature_flags.yaml — area:frontend, type:bug
    Operator decision required: add YAML entry + B1 migration row, or remove the isEnabled call and derive from demo_flow_ui.

  3. [F-4] Delete OptionsBacktestComingSoon component — area:frontend, type:tech-debt
    Component conflicts with the hide-don't-gray invariant by design. No non-test callers. Remove before v1 to prevent accidental re-introduction.


Recommendation

The Antlers codebase is mostly compliant with the hide-don't-gray invariant: 23 of 25 surfaces reviewed either hide by absence (route not registered, component returns null) or render a full replacement view (no partial graying). Two surfaces in Settings — the "Options Backtesting" and "Live Mode Ring" rows in the Platform Features tab — are active violations and must be remediated before the 2026-05-23 UTC launch. The fix is mechanical (wrap each row in an isEnabled conditional), not architectural. The OptionsBacktestComingSoon dead component and the antlers_demo_mode YAML gap are lower urgency but should not reach v1 unresolved: the dead component is a trap for a future "coming soon" regression, and the YAML gap will silently disable demo mode if demo_flow_ui is ever turned ON for testing without the companion YAML entry. Pre-launch verdict: needs action on F-1 and F-2 before 2026-05-23 UTC.