Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/adamayoung/popcorn --skill sync-feature-flags명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | sync-feature-flags |
| description | Sync feature flags from Statsig gates to code |
Syncs Statsig feature gates (source of truth) to FeatureFlag.swift in code. Handles adding new flags, updating changed names/descriptions, and removing deleted flags with full cascading cleanup.
This skill syncs Statsig → Code (Statsig is the source of truth). For the reverse direction — creating a new Statsig gate when adding a feature flag in code — see "Feature flag creation" under Project-Specific Rules in CLAUDE.md. New gates are created with mcp__statsig__Create_Gate, enabled for the development environment only.
| File | Role |
|---|---|
Platform/FeatureAccess/Sources/FeatureAccess/Models/FeatureFlag.swift | Static flag definitions + allFlags array |
Platform/FeatureAccess/Tests/FeatureAccessTests/FeatureFlagTests.swift | Count assertion + ID arguments list |
Features/*/Sources/*/*.swift | *Dependencies, view model, and View files that consume flags |
Features/*/Tests/*/*.swift | View model tests covering feature flags (*ViewModelTests.swift) |
Adapters/Contexts/*/Sources/*/*.swift | Adapter FeatureFlagProvider implementations |
Adapters/Contexts/*/Tests/*/*.swift | Adapter provider tests |
App/Features/AppRoot/AppRootDependencies.swift | Root dependencies with navigation-level flags |
App/Features/AppRoot/AppRootViewModel.swift | Root view model mirroring navigation-level flags |
PopcornUITests/FeatureFlag.swift | UI test feature flag enum (mirrors flag IDs as String raw values) |
Statsig gate IDs use snake_case. Swift properties use camelCase.
| Statsig Gate ID | Swift Property | Swift Name |
|---|---|---|
media_search | .mediaSearch | "Media Search" |
explore_discover_movies | .exploreDiscoverMovies | "Explore Discover Movies" |
plot_remix_game | .plotRemixGame | "Plot Remix Game" |
tv_series_intelligence | .tvSeriesIntelligence | "TV Series Intelligence" |
Conversion rules:
_, capitalize each word after the first → camelCase property name_, capitalize each word → Title Case nameexplore → .explore, "Explore")Verify the Statsig MCP server is available by checking for mcp__statsig__Get_List_of_Gates in the tool list. If unavailable, tell the user:
"The Statsig MCP server is not available. Please configure it and try again."
Stop here if unavailable.
Call mcp__statsig__Get_List_of_Gates to retrieve all feature gates. Extract id, name, and description from each gate.
Read Platform/FeatureAccess/Sources/FeatureAccess/Models/FeatureFlag.swift and parse:
static let property: extract id, name, descriptionallFlags array entriesMatch gates to code flags by id. Categorize into three groups:
| Category | Condition |
|---|---|
| New | Gate ID exists in Statsig but not in code |
| Updated | Gate ID exists in both but name or description differ |
| Removed | Flag ID exists in code but not in Statsig |
Present the diff to the user before making any changes:
Feature Flag Sync Report:
New (3):
+ media_search_v2 — "Media Search V2" — "Controls access to Media Search V2"
+ cast_details — "Cast Details" — "Controls access to Cast Details"
+ ...
Updated (1):
~ explore — name: "Explore" → "Explore Tab"
Removed (1):
- backdrop_focal_point — "Backdrop Focal Point"
Proceed with sync?
If all three categories are empty, report "Feature flags are already in sync." and stop.
For new flags, add to FeatureFlag.swift:
static let property at the end of the last extension (before the closing }):/// Controls access to <name>.
static let <camelCaseProperty> = FeatureFlag(
id: "<snake_case_id>",
name: "<Name from Statsig>",
description: "<Description from Statsig>"
)
.<camelCaseProperty> to the allFlags arrayEmpty Statsig descriptions: If a gate has no description, generate one: "Controls access to <name>"
For updated flags, edit the existing static let to match the new name and/or description from Statsig. Also update the doc comment if the description changed.
Update PopcornUITests/FeatureFlag.swift to mirror changes:
case with camelCase name and snake_case raw value, in the matching MARK sectioncaseFeatureFlag.swiftFor each removed flag, perform a full impact search before asking for confirmation.
For a flag named .myFlag with ID my_flag, search for:
# Direct flag references
rg "\.myFlag" -- find .myFlag references in code
rg "isMyFlagEnabled" -- find *Dependencies / view model / View properties
rg "my_flag" -- find ID string references in tests
Show the user every file and usage that will be affected:
Removing flag: .myFlag ("my_flag")
Affected files:
Dependencies: Features/SomeFeature/Sources/.../SomeDependencies.swift
- var isMyFlagEnabled: @Sendable () throws -> Bool
- live(services:) isMyFlagEnabled closure
- preview isMyFlagEnabled closure
ViewModel: Features/SomeFeature/Sources/.../SomeViewModel.swift
- isMyFlagEnabled property
- init parameter
- updateFeatureFlags() assignment
View: Features/SomeFeature/Sources/.../SomeView.swift
- if viewModel.isMyFlagEnabled { ... }
Tests: Features/SomeFeature/Tests/.../SomeViewModelTests.swift
- All test methods referencing isMyFlagEnabled
Adapter: Adapters/.../FeatureFlagProvider.swift (if applicable)
- isMyFlagEnabled() method
Confirm removal of .myFlag? (y/n)
When the user confirms removal of a flag, remove in this order:
static let property and allFlags entry*Dependencies.swift):
var is<Flag>Enabled property from the struct (and its init parameter/assignment)live(services:)#if DEBUG preview valuelet featureFlags = services.featureFlags line*ViewModel.swift):
is<Flag>Enabled property and its init parameterupdateFeatureFlags()guard is<Flag>Enabled checks and their associated logic*View.swift):
if viewModel.is<Flag>Enabled { ... } conditionals*ViewModelTests.swift):
*Dependencies factory (stubDependencies) to drop the flag closureFeatureFlagProviderFeatureFlagProviding protocolAfter all additions, updates, and removals:
#expect(FeatureFlag.allFlags.count == <new_count>)
allFlags array — same order, same IDs.After all changes are applied, present a summary:
Feature Flag Sync Complete:
Added: 3 flags
Updated: 1 flag
Removed: 1 flag
Files modified:
- Platform/FeatureAccess/Sources/FeatureAccess/Models/FeatureFlag.swift
- Platform/FeatureAccess/Tests/FeatureAccessTests/FeatureFlagTests.swift
- ... (list other modified files)
After all changes:
Remember to run the pre-PR checklist:
make lint,/build,/test(formatting is applied automatically by the PostToolUse hook)
Do NOT run these automatically — let the user invoke them.
// In the *Dependencies struct (a plain Sendable struct of closures):
public var is<Flag>Enabled: @Sendable () throws -> Bool
// In live(services:):
is<Flag>Enabled: { featureFlags.isEnabled(.<flagProperty>) }
// In the #if DEBUG preview value:
is<Flag>Enabled: { true }
The view model mirrors each *Dependencies flag closure into an observable property:
// Property (read-only to the view):
public private(set) var is<Flag>Enabled = false
// init parameter (defaults to false so previews/tests can override):
is<Flag>Enabled: Bool = false
// Refresh from dependencies (called from didAppear() / updateFeatureFlags()):
func updateFeatureFlags() {
is<Flag>Enabled = (try? dependencies.is<Flag>Enabled()) ?? false
}
if viewModel.is<Flag>Enabled {
// Conditional UI
}
Instantiate the view model with a stub *Dependencies and assert on the mirrored property after calling updateFeatureFlags(). Four required cases per feature:
truefalsefalsetrue// Domain protocol:
public protocol FeatureFlagProviding: Sendable {
func is<Flag>Enabled() throws(FeatureFlagProviderError) -> Bool
}
// Adapter:
func is<Flag>Enabled() throws(FeatureFlagProviderError) -> Bool {
featureFlags.isEnabled(.<flagProperty>)
}
To find all usage of a flag with property name myFlag and ID my_flag:
# Static property references (*Dependencies live(services:), Adapter providers)
rg "\.myFlag\b"
# *Dependencies / view model / View property references
rg "isMyFlagEnabled"
# String ID references (tests, FeatureFlag.swift)
rg '"my_flag"'
# FeatureFlagProviding protocol methods (adapters)
rg "isMyFlagEnabled.*throws"
$ARGUMENTS