| 1 | --- |
| 2 | name: feature-flags |
| 3 | description: Use when feature flag tests fail, flags need updating, understanding @gate pragmas, debugging channel-specific test failures, or adding new flags to React. |
| 4 | --- |
| 5 | |
| 6 | # React Feature Flags |
| 7 | |
| 8 | ## Flag Files |
| 9 | |
| 10 | | File | Purpose | |
| 11 | |------|---------| |
| 12 | | `packages/shared/ReactFeatureFlags.js` | Default flags (canary), `__EXPERIMENTAL__` overrides | |
| 13 | | `packages/shared/forks/ReactFeatureFlags.www.js` | www channel, `__VARIANT__` overrides | |
| 14 | | `packages/shared/forks/ReactFeatureFlags.native-fb.js` | React Native, `__VARIANT__` overrides | |
| 15 | | `packages/shared/forks/ReactFeatureFlags.test-renderer.js` | Test renderer | |
| 16 | |
| 17 | ## Gating Tests |
| 18 | |
| 19 | ### `@gate` pragma (test-level) |
| 20 | |
| 21 | Use when the feature is completely unavailable without the flag: |
| 22 | |
| 23 | ```javascript |
| 24 | // @gate enableViewTransition |
| 25 | it('supports view transitions', () => { |
| 26 | // This test only runs when enableViewTransition is true |
| 27 | // and is SKIPPED (not failed) when false |
| 28 | }); |
| 29 | ``` |
| 30 | |
| 31 | ### `gate()` inline (assertion-level) |
| 32 | |
| 33 | Use when the feature exists but behavior differs based on flag: |
| 34 | |
| 35 | ```javascript |
| 36 | it('renders component', async () => { |
| 37 | await act(() => root.render(<App />)); |
| 38 | |
| 39 | if (gate(flags => flags.enableNewBehavior)) { |
| 40 | expect(container.textContent).toBe('new output'); |
| 41 | } else { |
| 42 | expect(container.textContent).toBe('legacy output'); |
| 43 | } |
| 44 | }); |
| 45 | ``` |
| 46 | |
| 47 | ## Adding a New Flag |
| 48 | |
| 49 | 1. Add to `ReactFeatureFlags.js` with default value |
| 50 | 2. Add to each fork file (`*.www.js`, `*.native-fb.js`, etc.) |
| 51 | 3. If it should vary in www/RN, set to `__VARIANT__` in the fork file |
| 52 | 4. Gate tests with `@gate flagName` or inline `gate()` |
| 53 | |
| 54 | ## Checking Flag States |
| 55 | |
| 56 | Use `/flags` to view states across channels. See the `flags` skill for full command options. |
| 57 | |
| 58 | ## `__VARIANT__` Flags (GKs) |
| 59 | |
| 60 | Flags set to `__VARIANT__` simulate gatekeepers - tested twice (true and false): |
| 61 | |
| 62 | ```bash |
| 63 | /test www <pattern> # __VARIANT__ = true |
| 64 | /test www variant false <pattern> # __VARIANT__ = false |
| 65 | ``` |
| 66 | |
| 67 | ## Debugging Channel-Specific Failures |
| 68 | |
| 69 | 1. Run `/flags --diff <channel1> <channel2>` to compare values |
| 70 | 2. Check `@gate` conditions - test may be gated to specific channels |
| 71 | 3. Run `/test <channel> <pattern>` to isolate the failure |
| 72 | 4. Verify flag exists in all fork files if newly added |
| 73 | |
| 74 | ## Common Mistakes |
| 75 | |
| 76 | - **Forgetting both variants** - Always test `www` AND `www variant false` for `__VARIANT__` flags |
| 77 | - **Using @gate for behavior differences** - Use inline `gate()` if both paths should run |
| 78 | - **Missing fork files** - New flags must be added to ALL fork files, not just the main one |
| 79 | - **Wrong gate syntax** - It's `gate(flags => flags.name)`, not `gate('name')` |