Add a runtime feature flag (AppConfig-backed on prod, secret fallback off-prod), global by default or optionally gated by workspace id, org id, user id, or platform admin
66
80%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.agents/skills/add-feature-flag/SKILL.mdYou add a runtime feature flag to Sim that can change on prod with no redeploy (AWS AppConfig). Prefer a global on/off flag unless the rollout actually needs per-workspace, per-organization, per-user, or platform-admin targeting. When AppConfig isn't the source of truth, the flag falls back to a single secret (on/off only).
env-flags.ts@/lib/core/config/feature-flags.ts): runtime global on/off by default, optionally scoped by workspaceId/userId/orgId/admin. This skill.@/lib/core/config/env-flags.ts): deploy-time capability/environment detection (isProd, isHosted, isBillingEnabled). A module-load boolean. Do not add gated flags here.If the user wants a fixed per-deployment toggle, send them to env-flags.ts instead.
A flag's gating rule lives only in the hosted AppConfig document. It is ON for a context when any configured clause matches:
interface FeatureFlagRule {
enabled?: boolean // global default for everyone
workspaceIds?: string[] // allowlisted workspace ids
orgIds?: string[] // allowlisted organization ids
userIds?: string[] // allowlisted user ids
adminEnabled?: boolean // platform admins (user.role === 'admin')
}Critically, none of this is expressible in code — gating (especially adminEnabled) can only be set through AppConfig, so no environment can grant access from a code literal. Off-AppConfig (self-hosted/OSS/local), a flag is simply on or off, derived from its fallback secret.
Confirm the granularity before editing code. If the user has not already specified it, stop and ask:
Should
<flag-name>be a global on/off flag (recommended), or does it need rollout targeting by workspace, organization, user, and/or platform admin?
env-flags.ts instead.Define the flag. Add one entry to the FEATURE_FLAGS registry in apps/sim/lib/core/config/feature-flags.ts. Each entry is the flag's whole definition — name (kebab-case key), description, and the fallback secret consulted when AppConfig isn't the source of truth (truthy ⇒ on globally):
const FEATURE_FLAGS = {
'<flag-name>': {
description: '<what this gates>',
fallback: '<FLAG_SECRET>',
},
}fallback is the env/secret key (typed as keyof typeof env), so add <FLAG_SECRET> to apps/sim/lib/core/config/env.ts first (and the deployment's secret store) — it won't typecheck otherwise. Do not add workspace/org/user/admin defaults here — that gating exists only in AppConfig. Adding the entry makes <flag-name> a valid FeatureFlagName.
Gate the call site at the chosen granularity. For the recommended global mode, pass no context:
import { isFeatureEnabled } from '@/lib/core/config/feature-flags'
if (await isFeatureEnabled('<flag-name>')) {
// gated behavior
}Do not fetch, resolve, or thread through user or organization context solely for a global flag.
For scoped rollout, pass only the dimensions the user selected. Admin status is resolved internally, so ordinary callers pass userId, not a role:
import { isFeatureEnabled } from '@/lib/core/config/feature-flags'
if (await isFeatureEnabled('<flag-name>', { workspaceId, userId, orgId })) {
// gated behavior
}workspaceId; organization targeting uses orgId; user and platform-admin targeting require userId.userId, the admin clause resolves to false without a DB read.{ userId, isAdmin: true } to skip the role lookup.(Prod) configure in AppConfig. The infra feature-flags profile schema is permissive, so a new flag needs no infra change. Operators add the flag to the hosted feature-flags document using enabled for global rollout or only the selected workspaceIds/orgIds/userIds/adminEnabled clauses for scoped rollout, then start a sim-<env>-fast deployment (see the AppConfig runbook in the infra README — same flow as access-control). The fallback secret only applies when AppConfig is disabled.
Test only new evaluation logic. A flag that reuses the existing clauses is already covered by apps/sim/lib/core/config/feature-flags.test.ts; add no per-flag case. When you change how flags evaluate (a new clause kind, a new fallback path), add a case there that passes the test-audit authoring gate.
Clean up after rollout. When the feature ships to everyone, delete the flag's entry from FEATURE_FLAGS, the <FLAG_SECRET> env entry, the AppConfig document, and the call sites. Leaving dead flags around is the main failure mode of flag systems.
kebab-case.fetch or a new AppConfig client — always go through isFeatureEnabled / getFeatureFlags.dbReplica) and is resolved lazily, so an admin-gated flag adds at most one cheap replica read, and only when adminEnabled is the deciding clause.8725250
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.