Skip to content

Commit 9e29de3

Browse files
authored
Merge pull request #4211 from anyproto/ios-5420-restructure-and-update-ai-flow-feature-toggles
IOS-5420 Add feature toggle engineer skill
2 parents 5465161 + f91b06a commit 9e29de3

File tree

3 files changed

+358
-1
lines changed

3 files changed

+358
-1
lines changed

.claude/hooks/skill-rules.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,53 @@
280280
"gh pr comment"
281281
]
282282
}
283+
},
284+
"feature-toggle-developer": {
285+
"type": "domain",
286+
"priority": "high",
287+
"description": "Smart router to feature toggle removal (→ feature-toggle-developer.md). Systematic toggle removal, automated cleanup detection, unused code identification",
288+
"promptTriggers": {
289+
"keywords": [
290+
"remove toggle",
291+
"delete toggle",
292+
"enable toggle",
293+
"feature flag",
294+
"feature toggle",
295+
"remove feature flag",
296+
"delete feature flag",
297+
"enable feature flag",
298+
"cleanup after toggle",
299+
"unused code",
300+
"orphaned code",
301+
"defaultValue: true",
302+
"defaultValue: false",
303+
"FeatureFlags.",
304+
"toggle removal"
305+
],
306+
"intentPatterns": [
307+
"(remove|delete|eliminate).*?(toggle|feature flag|feature toggle)",
308+
"(enable|activate|turn on).*?(toggle|feature flag|permanently)",
309+
"(cleanup|clean up|remove unused).*?(code|component|class|file)",
310+
"(find|identify|check for).*?(unused|orphaned|dead).*?(code|component)",
311+
"(after|completed).*?(toggle|feature flag).*?(removal|cleanup)"
312+
]
313+
},
314+
"fileTriggers": {
315+
"pathPatterns": [
316+
"**/FeatureDescription+Flags.swift",
317+
"**/FeatureFlags+Flags.swift",
318+
"**/.claude/skills/feature-toggle-developer.md"
319+
],
320+
"contentPatterns": [
321+
"FeatureFlags\\.",
322+
"static let.*=.*FeatureDescription",
323+
"defaultValue: true",
324+
"defaultValue: false",
325+
"if FeatureFlags\\.",
326+
"guard.*FeatureFlags\\.",
327+
"@State.*FeatureFlags"
328+
]
329+
}
283330
}
284331
},
285332
"config": {
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
# Feature Toggle Developer
2+
3+
**Status**: Active
4+
**Auto-activates on**: Feature flag/toggle removal, cleanup after refactoring toggles
5+
**Related Skills**: `code-generation-developer`, `ios-dev-guidelines`, `code-review-developer`
6+
7+
## Purpose
8+
9+
Guides the systematic removal of feature toggles (feature flags) from the codebase with automated cleanup detection. Ensures no orphaned code, unused components, or forgotten files remain after toggle removal.
10+
11+
## When This Skill Activates
12+
13+
- User mentions: "remove toggle", "delete feature flag", "enable feature toggle permanently"
14+
- User edits: `FeatureDescription+Flags.swift`
15+
- After completing toggle removal: helps identify cleanup opportunities
16+
17+
## Feature Toggle Removal Workflow
18+
19+
### Phase 1: Pre-Removal Analysis
20+
21+
Before removing any toggle, gather complete information:
22+
23+
1. **Find the toggle definition**:
24+
```bash
25+
# Location: Modules/AnytypeCore/AnytypeCore/Utils/FeatureFlags/FeatureDescription+Flags.swift
26+
rg "static let toggleName" --type swift
27+
```
28+
29+
2. **Check the defaultValue** to determine which branch to keep:
30+
- `defaultValue: true` → Keep the TRUE branch, remove FALSE branch
31+
- `defaultValue: false` → Keep the FALSE branch, remove TRUE branch
32+
33+
3. **Search for ALL usages**:
34+
```bash
35+
rg "toggleName" --type swift
36+
```
37+
38+
4. **Identify usage patterns**:
39+
- Direct: `if FeatureFlags.toggleName { ... }`
40+
- Inverted: `if !FeatureFlags.toggleName { ... }` or `guard !FeatureFlags.toggleName`
41+
- Compound: `if FeatureFlags.toggleName && otherCondition { ... }`
42+
- Assignment: `let value = FeatureFlags.toggleName ? a : b`
43+
- State: `@State private var toggle = FeatureFlags.toggleName`
44+
45+
5. **List affected files** and present to user for review
46+
47+
### Phase 2: Toggle Removal
48+
49+
Systematic removal process:
50+
51+
1. **Remove conditional checks and simplify**:
52+
53+
**Example 1 - Simple conditional (defaultValue: true)**:
54+
```swift
55+
// BEFORE
56+
if FeatureFlags.toggleName {
57+
// feature code
58+
}
59+
60+
// AFTER (keep true branch)
61+
// feature code
62+
```
63+
64+
**Example 2 - Ternary operator (defaultValue: true)**:
65+
```swift
66+
// BEFORE
67+
VStack(spacing: FeatureFlags.toggleName ? 8 : 0)
68+
69+
// AFTER
70+
VStack(spacing: 8)
71+
```
72+
73+
**Example 3 - Inverted logic (defaultValue: true)**:
74+
```swift
75+
// BEFORE
76+
guard !FeatureFlags.toggleName else { return }
77+
oldCode()
78+
79+
// AFTER (flag is true, so guard fails, remove entire block)
80+
// [entire block deleted]
81+
```
82+
83+
**Example 4 - State variable (defaultValue: true)**:
84+
```swift
85+
// BEFORE
86+
@State private var toggle = FeatureFlags.toggleName
87+
if toggle { newUI() } else { oldUI() }
88+
89+
// AFTER
90+
newUI()
91+
// Note: @State variable removed in cleanup phase
92+
```
93+
94+
2. **Remove feature flag definition**:
95+
```swift
96+
// Delete from: Modules/AnytypeCore/AnytypeCore/Utils/FeatureFlags/FeatureDescription+Flags.swift
97+
static let toggleName = FeatureDescription(...)
98+
```
99+
100+
3. **Run code generation**:
101+
```bash
102+
make generate
103+
```
104+
This updates `FeatureFlags+Flags.swift` automatically.
105+
106+
4. **Verify removal**:
107+
```bash
108+
rg "toggleName" --type swift # Should return no results
109+
```
110+
111+
### Phase 3: Automated Cleanup Detection ⭐
112+
113+
**CRITICAL**: After toggle removal, systematically check for orphaned code:
114+
115+
#### 3.1 Unused State Variables
116+
117+
Search for `@State` variables that were only used for the toggle:
118+
119+
```bash
120+
# Look for patterns like: @State private var someToggle = FeatureFlags.toggleName
121+
rg "@State.*=.*FeatureFlags" --type swift
122+
```
123+
124+
**Action**: Remove the entire `@State` variable declaration if it's no longer used.
125+
126+
#### 3.2 Unused View Components
127+
128+
When a toggle controlled which UI component to show, one component may now be unused:
129+
130+
**Detection Pattern**:
131+
- Toggle switched between ComponentA and ComponentB
132+
- After removal, only one is used
133+
- Search for the unused component's name across codebase
134+
135+
**Example from vaultBackToRoots**:
136+
```swift
137+
// BEFORE
138+
if !vaultBackToRootsToggle {
139+
SpaceCardLabel(...) // This became unused
140+
} else {
141+
NewSpaceCardLabel(...)
142+
}
143+
144+
// AFTER
145+
NewSpaceCardLabel(...)
146+
147+
// CLEANUP: SpaceCardLabel is now unused
148+
rg "SpaceCardLabel" --type swift # Check if used anywhere else
149+
# If only in its own file DELETE the file
150+
```
151+
152+
**Action**:
153+
1. Search for unused component name: `rg "UnusedComponentName" --type swift`
154+
2. If only found in its definition file and comments DELETE the file
155+
3. Update references in comments
156+
157+
#### 3.3 Unused ViewModels / Service Classes
158+
159+
Toggle removal may leave entire classes unused:
160+
161+
**Detection**:
162+
```bash
163+
# For each major component that was conditionally used:
164+
rg "UnusedViewModel" --type swift
165+
rg "class UnusedViewModel" --type swift
166+
```
167+
168+
**Action**: Delete unused ViewModels, their files, and DI registrations.
169+
170+
#### 3.4 Unused Imports
171+
172+
After simplification, `import AnytypeCore` may only have been needed for `FeatureFlags`:
173+
174+
**Detection**:
175+
- File imports `AnytypeCore`
176+
- Only usage was `FeatureFlags.toggleName`
177+
- After removal, no other `AnytypeCore` usage
178+
179+
**Action**: Remove unused import.
180+
181+
#### 3.5 Orphaned Parameters / Properties
182+
183+
Toggle-gated functionality may have parameters that are no longer needed:
184+
185+
**Example**:
186+
```swift
187+
// BEFORE
188+
func configure(showFeature: Bool) {
189+
if showFeature && FeatureFlags.toggle { ... }
190+
}
191+
192+
// AFTER toggle removal
193+
func configure(showFeature: Bool) {
194+
if showFeature { ... }
195+
}
196+
197+
// POTENTIAL CLEANUP: Is showFeature still needed?
198+
```
199+
200+
**Action**: Review function signatures and remove unnecessary parameters.
201+
202+
#### 3.6 Test Cleanup
203+
204+
Toggle removal affects tests:
205+
206+
**Check**:
207+
1. Mock objects with toggle-related properties
208+
2. Test cases specifically for toggle behavior
209+
3. Test setup code with toggle configurations
210+
211+
**Files to check**:
212+
```bash
213+
rg "toggleName" AnyTypeTests/ --type swift
214+
rg "toggleName" "Anytype/Sources/PreviewMocks/" --type swift
215+
```
216+
217+
**Action**: Update or remove tests for deleted code paths.
218+
219+
### Phase 4: Final Verification
220+
221+
Before committing:
222+
223+
1. **Grep verification**:
224+
```bash
225+
rg "toggleName" --type swift # Should be empty
226+
```
227+
228+
2. **Compilation check**:
229+
- Remind user to verify compilation in Xcode
230+
- Claude cannot verify this due to caching
231+
232+
3. **Generate updated commit message**:
233+
```
234+
IOS-XXXX Removed [toggleName] toggle
235+
```
236+
237+
4. **Review cleanup summary**:
238+
- List all files modified
239+
- List all files deleted
240+
- Note any remaining manual checks needed
241+
242+
## Cleanup Checklist Template
243+
244+
Use this checklist after every toggle removal:
245+
246+
```markdown
247+
## Cleanup Verification for [toggleName]
248+
249+
- [ ] Toggle definition removed from FeatureDescription+Flags.swift
250+
- [ ] `make generate` run successfully
251+
- [ ] All conditional usage removed
252+
- [ ] No grep results for toggle name
253+
- [ ] Unused @State variables removed
254+
- [ ] Unused view components identified and deleted
255+
- [ ] Unused ViewModels/services deleted
256+
- [ ] Unused imports removed (especially AnytypeCore)
257+
- [ ] Orphaned function parameters removed
258+
- [ ] Tests updated (check AnyTypeTests/ and PreviewMocks/)
259+
- [ ] Comments referencing old component updated
260+
- [ ] Xcode compilation verified (by user)
261+
```
262+
263+
## Common Patterns & Pitfalls
264+
265+
### Inverted Logic
266+
267+
**Watch out for** `!FeatureFlags.toggle`:
268+
269+
```swift
270+
// If defaultValue: true
271+
if !FeatureFlags.toggle {
272+
oldCode() // This branch NEVER runs, delete it
273+
}
274+
```
275+
276+
### Compound Conditions
277+
278+
**Simplify** conditions properly:
279+
280+
```swift
281+
// BEFORE (defaultValue: true)
282+
if FeatureFlags.toggle && userHasPermission {
283+
showFeature()
284+
}
285+
286+
// AFTER
287+
if userHasPermission {
288+
showFeature()
289+
}
290+
```
291+
292+
### Guard Statements
293+
294+
**Be careful** with guards:
295+
296+
```swift
297+
// BEFORE (defaultValue: true)
298+
guard !FeatureFlags.toggle else { return }
299+
performOldBehavior()
300+
301+
// AFTER (toggle is true, guard returns, entire block is dead code)
302+
// DELETE ENTIRE BLOCK
303+
```
304+
305+
## Integration with Other Skills
306+
307+
- **code-generation-developer**: References for `make generate` command and troubleshooting
308+
- **ios-dev-guidelines**: Swift refactoring patterns, import management
309+
- **code-review-developer**: Cleanup standards, ensuring no orphaned code in PRs

.claude/skills/skills-manager/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,14 @@ echo "Update space settings localization for membership tiers" | .claude/hooks/u
140140

141141
**Location**: `.claude/skills/*/SKILL.md`
142142

143-
**The 6 skills**:
143+
**The 7 skills**:
144144
1. `ios-dev-guidelines` - Swift/iOS patterns
145145
2. `localization-developer` - Localization
146146
3. `code-generation-developer` - Feature flags, make generate
147147
4. `design-system-developer` - Icons, typography, colors
148148
5. `skills-manager` - This skill (meta!)
149149
6. `code-review-developer` - Code review standards
150+
7. `feature-toggle-developer` - Feature toggle removal, cleanup detection
150151

151152
### Configuration
152153

0 commit comments

Comments
 (0)