Feature Flags
Honest status: there is no centralized feature-flag system in the MIA codebase today. No GrowthBook, no Statsig, no Firestore-backed flag collection. The repo uses two ad-hoc mechanisms.
1. Environment variables
Flag-like booleans flipped via env vars and read at function/service startup. Examples:
USE_OPENROUTER— switch the default LLM provider.ENABLE_ONBOARDING— gate the AI onboarding route on the frontend.TEMPORAL_ADDRESS— not a flag per se, but acts like one (presence/absence changes whether scheduling is wired up).
Env files: .env.<project-id> (loaded at import via scripts/generate-env.ts), or Firebase Secret Manager for secrets. See Operations.
2. Per-company AI config (admin-controlled)
The closest thing to a kill-switch / cohort tool in the codebase today. Lets an admin route one company onto a different LLM provider / model without redeploying.
RTDB paths:
Companies/{companyId}/aiConfig—{ provider, model, updatedAt }or null. Per-company override.GlobalAiConfig—{ provider, model, updatedAt }. Global default.
Admin endpoints (in the companies module, gated by an inline isAdmin() check):
| Endpoint | Purpose | File |
|---|---|---|
GET /ai-config/global | Current global default (no admin gate) | functions/modules/companies/endpoints/apis/ai-usage.router.ts:176 |
PATCH /ai-config/global | Set global default (admin) | ai-usage.router.ts:181 |
GET /companies/:id/ai-config | Read per-company override (admin) | ai-usage.router.ts:120 |
PATCH /companies/:id/ai-config | Set per-company override (admin) | ai-usage.router.ts:132 |
DELETE /companies/:id/ai-config | Clear per-company override (admin) | ai-usage.router.ts:152 |
provider is constrained to 'anthropic' | 'openrouter'; model is a free-form string the provider adapter understands.
3. MIA access control (per-company + per-manager)
The gate for who can see the Mia tab in the navbar and call /api/chat. Three steps, all must pass.
| RTDB path | Purpose | Who writes |
|---|---|---|
CompanySettings/{cid}/aiAgentEnabled | Org-level toggle. If false or missing, no one in this company sees Mia. | Pivot internal admin (OwnerDashboard) |
Companies/{cid}/pivotAiAgentAccess/{userId} | Per-manager allowlist — { enabled: true } grants access. owner / head-office roles bypass this check. | Company owner (or head-office) |
CompanySettings/{cid}/aiAgentPersona | UI persona name: 'Mia' (default) or 'zack' (legacy alternate). Applied per-company. | Company-level setting; used in the chat header, planner intro line, and UI labels. |
Frontend gate logic at src/components/NavBar/NavBar.tsx:84-109 and src/routes/PivotAiAgentDashboard/PivotAiAgentGlobal.tsx:497. See Security Model: Who can use MIA for the full layered explanation.
Important caveat: the per-manager allowlist is checked on the frontend only. The chat endpoint /api/chat doesn't enforce a separate Mia-specific allow list — only the standard auth + scope middleware. A determined manager could bypass the UI and still call the API directly. See TODOs for the open work to make this server-enforced.
What this means in practice
- You can't roll a generic feature out to 10% of companies today.
- You can kill-switch a misbehaving AI model per company by setting
aiConfigto a fallback provider — that ships immediately, no redeploy. - A/B testing arbitrary features requires manual cohort assignment in some bespoke spot in code.
Note: earlier versions of these docs referenced a Companies/{cid}/miaSettings path. That path is not written anywhere in the codebase — disregard. Per-company AI configuration lives at aiConfig as described above.
Planned work
Adding a proper flag collection (e.g., Firestore FeatureFlags/{flagKey} with enabled, rolloutPercent, companyAllowlist) is open. See Reference: TODOs.