Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add `feature-flags` skill with a repo-agnostic base and a MetaMask Mobile overlay for version-gated remote flags. Marked `base: true` so it installs even when its domain is filtered out. ([#147](https://github.com/MetaMask/skills/pull/147))
- Add `analytics` skill (`platform/analytics`, moved from `coding`) with a repo-agnostic base and a MetaMask Mobile overlay for the canonical tracking API. Marked `base: true` so it installs even when its domain is filtered out. ([#140](https://github.com/MetaMask/skills/pull/140))

## [0.3.1]
Expand Down
71 changes: 71 additions & 0 deletions domains/platform/skills/feature-flags/repos/metamask-mobile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
repo: metamask-mobile
parent: feature-flags
---

# Feature flags — MetaMask Mobile

Human-facing file map: `docs/readme/version-gated-feature-flags.md`.

## Canonical API

| Role | Path |
|------|------|
| Helper | `app/util/remoteFeatureFlag/index.ts` → `validatedVersionGatedFeatureFlag`, `hasMinimumRequiredVersion` |
| Raw flags | `RemoteFeatureFlagController.remoteFeatureFlags` (no client version gating) |
| Selectors | `app/selectors/featureFlagController/` or `**/selectors/featureFlags/` |
| Names (when the flag is overrideable in dev tools) | `app/constants/featureFlags.ts` → `FeatureFlagNames` |

`validatedVersionGatedFeatureFlag` compares against the native binary version from `react-native-device-info` `getVersion()`, not `package.json`. Progressive-rollout wrappers `{ name, value: { enabled, minimumVersion } }` are unwrapped in the helper.

`hasMinimumRequiredVersion` is the lower-level compare used inside the helper. Import it from the util only for a standalone version check.

Multi-version flags shaped `{ versions: { "7.53.0": value } }` are resolved at fetch time by the controller. Consumers read the processed value — no helper call for that shape.

## Requirements

- Selectors call `validatedVersionGatedFeatureFlag`. UI and hooks only `useSelector(selectXEnabled)`.
- Non-standard flag shapes (e.g. `active` instead of `enabled`) map to `{ enabled, minimumVersion }` before the helper.
- Remote flag wins when valid. Use `?? env/local` when the helper returns `undefined` (invalid shape, or `OVERRIDE_REMOTE_FEATURE_FLAGS=true`).

```ts
import { createSelector } from 'reselect';
import { selectRemoteFeatureFlags } from '../index';
import {
validatedVersionGatedFeatureFlag,
type VersionGatedFeatureFlag,
} from '../../../util/remoteFeatureFlag';

export const selectMyFeatureEnabled = createSelector(
selectRemoteFeatureFlags,
(remoteFeatureFlags) => {
const localFlag = process.env.MM_MY_FEATURE_ENABLED === 'true';
const remoteFlag =
remoteFeatureFlags?.myFeature as unknown as VersionGatedFeatureFlag;

return validatedVersionGatedFeatureFlag(remoteFlag) ?? localFlag;
},
);
```

Non-standard shape:

```ts
validatedVersionGatedFeatureFlag({
enabled: depositConfig.active ?? false,
minimumVersion: depositConfig.minimumVersion ?? '',
}) ?? false;
```

UI:

```ts
const isEnabled = useSelector(selectMyFeatureEnabled);
```

## Reject

- Local copies of `hasMinimumRequiredVersion` or `validatedVersionGatedFeatureFlag`
- Inline `compare-versions` + `getVersion` for feature-flag gating outside `app/util/remoteFeatureFlag`
- Version checks in hooks or components (`getVersion()`, `compare-versions`, or a local helper)
- Duplicate util files under `app/components/UI/**/utils/` or `app/core/redux/slices/**/`
27 changes: 27 additions & 0 deletions domains/platform/skills/feature-flags/skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: feature-flags
description: >-
Version-gated remote feature flags. Use when adding, migrating, or
reviewing a remote or version-gated feature flag, writing a flag
selector, or gating UI on a flag boolean.
maturity: stable
base: true
---

# Feature flags

Use this skill for remote and version-gated feature flags.

## When to use

- Adding or migrating a remote / version-gated feature flag
- Writing or updating a flag selector
- Gating UI or hooks on a flag boolean
- Reviewing a PR that introduces or changes flag evaluation

## Workflow

1. Evaluate the flag in a selector. Return a boolean.
2. Map a non-standard remote shape to `{ enabled, minimumVersion }` before the shared helper.
3. Consume that boolean in UI or hooks. Do not re-run version math there.
4. Cover the selector with collocated tests (enabled, disabled, invalid, fallback).