Skip to content

Commit 0f9b03d

Browse files
asalsyscursoragent
andcommitted
chore: add Cursor rule for version-gated feature flags
Track repo-specific Cursor rule so contributors use validatedVersionGatedFeatureFlag from app/util/remoteFeatureFlag instead of duplicating version-check helpers. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent d08ec55 commit 0f9b03d

3 files changed

Lines changed: 99 additions & 1 deletion

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
description: Use shared version-gated feature flag helpers; never duplicate hasMinimumRequiredVersion or validatedVersionGatedFeatureFlag
3+
globs: app/**/*.{ts,tsx}
4+
alwaysApply: false
5+
---
6+
7+
# Version-Gated Feature Flags
8+
9+
## Canonical source (SSOT)
10+
11+
All version-gated feature flag logic lives in `app/util/remoteFeatureFlag/index.ts`:
12+
13+
- `validatedVersionGatedFeatureFlag` — preferred for remote flags shaped as `{ enabled, minimumVersion }` (or progressive-rollout wrappers)
14+
- `hasMinimumRequiredVersion` — version comparison only; import when needed, never redefine
15+
16+
## Required patterns
17+
18+
### ✅ Use `validatedVersionGatedFeatureFlag` in selectors
19+
20+
```typescript
21+
import { validatedVersionGatedFeatureFlag, VersionGatedFeatureFlag } from '../../../util/remoteFeatureFlag';
22+
23+
export const selectMyFeatureEnabled = createSelector(
24+
selectRemoteFeatureFlags,
25+
(remoteFeatureFlags) => {
26+
const remoteFlag = remoteFeatureFlags?.myFeature as unknown as VersionGatedFeatureFlag;
27+
return validatedVersionGatedFeatureFlag(remoteFlag) ?? false;
28+
},
29+
);
30+
```
31+
32+
### ✅ Non-standard flag shapes — map, don't duplicate
33+
34+
Configs that use `active` instead of `enabled` (e.g. `depositConfig`) must map to the canonical shape and call the shared helper:
35+
36+
```typescript
37+
import { validatedVersionGatedFeatureFlag } from '../../../util/remoteFeatureFlag';
38+
39+
return (
40+
validatedVersionGatedFeatureFlag({
41+
enabled: depositConfig.active ?? false,
42+
minimumVersion: depositConfig.minimumVersion ?? '',
43+
}) ?? false
44+
);
45+
```
46+
47+
### ✅ Import `hasMinimumRequiredVersion` only from the util
48+
49+
```typescript
50+
import { hasMinimumRequiredVersion } from '../../../util/remoteFeatureFlag';
51+
```
52+
53+
Never copy the `compare-versions` + `getVersion` pattern into feature modules.
54+
55+
## Forbidden patterns
56+
57+
### ❌ Do not define local version-gating helpers
58+
59+
```typescript
60+
// FORBIDDEN — duplicate of app/util/remoteFeatureFlag
61+
function hasMinimumRequiredVersion(minVersion: string) { ... }
62+
const validatedVersionGatedFeatureFlag = (flag: unknown) => { ... }
63+
```
64+
65+
### ❌ Do not inline version checks for feature flags
66+
67+
```typescript
68+
// FORBIDDEN
69+
import { getVersion } from 'react-native-device-info';
70+
import compareVersions from 'compare-versions';
71+
72+
const ok = compareVersions.compare(getVersion(), flag.minimumVersion, '>=');
73+
```
74+
75+
### ❌ Do not add duplicate utils under feature folders
76+
77+
Do not create files like:
78+
79+
- `app/components/UI/**/utils/hasMinimumRequiredVersion.ts`
80+
- `app/core/redux/slices/**/hasMinimumRequiredVersion.ts`
81+
82+
## Where logic belongs
83+
84+
| Layer | Responsibility |
85+
|-------|----------------|
86+
| `RemoteFeatureFlagController` | Fetch/store raw flags — **no** client version gating |
87+
| `app/util/remoteFeatureFlag` | Version comparison + `validatedVersionGatedFeatureFlag` |
88+
| Selectors (`featureFlagController`, `**/selectors/featureFlags`) | Call shared helpers; return `boolean` |
89+
| Hooks / components | `useSelector(selectXEnabled)` — not local version math |
90+
91+
## Enforcement
92+
93+
- **REJECT** new local definitions of `hasMinimumRequiredVersion` or `validatedVersionGatedFeatureFlag`
94+
- **REJECT** new `compare-versions` + `getVersion` usage for feature-flag gating outside `app/util/remoteFeatureFlag`
95+
- **REQUIRE** imports from `app/util/remoteFeatureFlag` for all version-gated flag checks

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,12 @@ release-signoffs.json
217217
.claude/skills/
218218
.claude/commands/
219219
.agents/skills/
220-
.cursor/rules/
220+
.cursor/rules/*
221+
!.cursor/rules/*.mdc
221222
.cursor/skills/
222223
.cursor/commands/
223224
# Carve-outs: IDE/bugbot config that must stay tracked
225+
!.cursor/rules/*.mdc
224226
!.cursor/BUGBOT.md
225227
!.cursor/hooks.json
226228
!.cursor/worktrees.json

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ General coding, UI, deeplink-handler, and PR-creation guidance now lives in the
115115
- **Components**: Design system first → `component-library` second → custom last
116116
- **Styling**: Use `useTailwind()` hook, `Box`/`Text` components, design tokens
117117
- **Testing**: Mandatory for all code, AAA pattern, mock everything external
118+
- **Version-gated feature flags**: Use shared helpers from `app/util/remoteFeatureFlag` — see [`.cursor/rules/version-gated-feature-flags.mdc`](.cursor/rules/version-gated-feature-flags.mdc)
118119
- **Commands**: ONLY use yarn (never npm/npx)
119120

120121
## Environment Setup

0 commit comments

Comments
 (0)