Skip to content

feat(coding): add Mobile selector-patterns skill - #153

Merged
NicolasMassart merged 4 commits into
mainfrom
feat/MCWP-472_selector-patterns
Sep 11, 2026
Merged

feat(coding): add Mobile selector-patterns skill#153
NicolasMassart merged 4 commits into
mainfrom
feat/MCWP-472_selector-patterns

Conversation

@NicolasMassart

@NicolasMassart NicolasMassart commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Description

Opt-in coding/selector-patterns skill with a MetaMask Mobile overlay. Agents get a description-triggered guide for authoring and consuming Redux selectors: named select<Feature><Thing> selectors, createSelector / createDeepEqualSelector, leaf reads of state.engine.backgroundState, useSelector(selectX) in UI, and collocated tests with at least two state variants. Version-gated flag selectors stay on feature-flags. Memoization audits stay on performance.

Install/use pointers were added on the Mobile overlays for coding-guidelines, controller-integration (step 11 rewritten so object/array outputs can use createDeepEqualSelector), and performance.

MCWP-472 asked for an always-on Cursor rule (alwaysApply: true). This repo ships opt-in skills, so discovery is the description plus those install/use pointers.

Fixes: https://consensyssoftware.atlassian.net/browse/MCWP-472

Type of Change

  • New skill
  • Skill improvement/update
  • Bug fix
  • Documentation update
  • Other (please describe):

Skill Details (if adding a new skill)

Provider Name: MetaMask
Skill Name: selector-patterns
Brief Description: Redux selector authoring for MetaMask Mobile: named select<Feature><Thing> selectors in app/selectors/, createSelector / createDeepEqualSelector, leaf reads of state.engine.backgroundState, and useSelector(selectX) in UI. Use when adding, updating, or asking about selectors.

Checklist

  • I have read the CONTRIBUTING.md guidelines
  • My skill follows the SKILL_TEMPLATE.md format
  • I have tested this skill with an AI agent
  • My skill does not contain any secrets, private keys, or sensitive data
  • I have added appropriate documentation
  • My changes don't break existing skills

Testing

  • yarn audit:skillscoding/selector-patterns had no schema errors
  • ./tools/install --repo metamask-mobile --include coding/selector-patterns
  • Two mobile-skill-consumer product tickets (Contacts address-book count; Onboarding password-set store read). Product diffs were discarded after capture.

Important

Skill-test report from two Mobile consumer runs : skill-test-selector-patterns.local.md

Additional Context

CODEOWNERS: @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform (/domains/coding/, /domains/performance/)

Made with Cursor

Give agents a description-triggered guide for authoring and consuming
Redux selectors. MCWP-472 asked for an always-on rule; this repo ships
opt-in skills, so discovery is the description plus install/use pointers.

Co-authored-by: Cursor <cursoragent@cursor.com>
@NicolasMassart NicolasMassart self-assigned this Sep 7, 2026
@NicolasMassart NicolasMassart added the enhancement New feature or request label Sep 7, 2026
@NicolasMassart
NicolasMassart marked this pull request as ready for review September 7, 2026 17:12
@andrepimenta

Copy link
Copy Markdown
Member

Review

Checked out the branch, ran the linter and installer dry-runs, and verified every Mobile claim against a metamask-mobile checkout.

Verified clean

  • yarn audit:skills — 54 skills, 0 errors (only the repo-wide "recommended section" warnings, which this skill doesn't trigger).
  • Installer gating works: dry-run installs mms-selector-patterns for --repo metamask-mobile, correctly skipped for metamask-extension (no overlay), so the Mobile paths in the base skill.md don't leak.
  • Naming claim holds: app/selectors has 528 select* vs 48 get* exports. "Existing get* names stay" is the right call.
  • --no-coverage is correct — jest.config.js:58 sets collectCoverage: true by default.
  • Paths all check out: app/selectors/util.tscreateDeepEqualSelector, app/selectors/<feature>/index.ts, app/components/UI/<Feature>/selectors/.
  • Omitting maturity is fine — installer defaults to stable (tools/install:240) and 34 of 54 existing skills omit it. Content-only change, so no CHANGELOG entry needed.

Blockers

1. The example snippet doesn't compile. In repos/metamask-mobile.md, EMPTY_ADDRESS_BOOK_CHAIN: readonly never[] unions with Object.values(...) to readonly never[] | AddressBookEntry[], which fails at any consumer expecting a mutable array — confirmed with tsc:

error TS2345: Argument of type 'readonly never[] | Entry[]' is not assignable to parameter of type 'Entry[]'.
  The type 'readonly never[]' is 'readonly' and cannot be assigned to the mutable type 'Entry[]'.

Repo precedent avoids this deliberately: const EMPTY_TOKENS: Token[] = Object.freeze([] as Token[]) as Token[] (tokensController.ts:130). The object constant (Readonly<Record<string, never>>) matches EMPTY_TOKENS_BY_ADDRESS and is fine — only the array form needs changing.

2. The flagship example violates the skill's own rules. selectAddressBook returns an object on a plain createSelector and is an identity passthrough of a controller slice. The Factory-choice table forbids the first (object output → createDeepEqualSelector); the Reject list forbids the second ("Identity createSelector on a controller slice"). An agent that internalises the table will flag the example it was told to copy. Either change the factory or give the rule its nuance — a sub-key read plus a stable empty constant is arguably fine.

3. "Copy from app/selectors/addressBookController.ts" points at a file that breaks two Reject rules. The real file uses inline ?? {} and inline return [], and addressBookController.test.ts has one state variant, not the two the skill requires. The overlay then shows a rewritten version of that file as if it were its contents. An agent following the instruction reads the anti-pattern; one copying the block writes something that doesn't match the file, or "fixes" the real file as a drive-by. tokensController.ts genuinely has the pattern — make it the sole exemplar and label the addressBook block as illustrative.

4. The Factory table contradicts the performance skill this PR also edits. mm-selector-memoization.md:111: "createDeepEqualSelector is not free: deep-comparing a huge slice every dispatch can itself be costly. Prefer narrowing the input over deep-equalizing a giant object." The new table makes deep-equal unconditional for object/array/Map/Set output and drops narrowing entirely — the cheaper fix, and the one that matters most in the power-user scenario the perf skill is built around. Suggest: narrow the input first, reach for createDeepEqualSelector when the input slice churns and the payload is small enough to compare.

1 and 2 make agents emit bad code; 3 and 4 make them emit inconsistent code.

Non-blocking

  • Circular pointer: inside selector-patterns/repos/metamask-mobile.md — "Engine wiring that needs a first selector: install/use coding/selector-patterns from controller-integration" points at itself. Presumably meant "Engine wiring: controller-integration".
  • "Install/use" isn't actionable: no other overlay cross-references a not-yet-installed skill this way, and none of the four pointers carries the command. README documents yarn skills --include coding/selector-patterns --save — worth spelling out at least once. Placing the pointers in three base: true skills is otherwise a sound answer to the always-on gap the description notes.
  • selectTokensByChainId doesn't exist. The naming example cites it; the real symbols are selectTokensByChainIdAndAddress / selectTokensByAddress. Agents will grep for it.
  • Scope the backgroundState Reject rule to new code. 20 non-test files under app/components/app/util/app/hooks read state.engine.backgroundState today. The Requirements section is correctly scoped to "New and changed selectors"; the Reject list isn't, which invites refactor sprees.

On CONTRIBUTING's "is it a duplicate?" test

Overlap with performance is real — the Reject list is essentially the four deadly patterns from mm-selector-memoization.md. But the authoring-vs-audit split is stated explicitly, the pointers go both ways, and the net-new content (file location, naming, test shape) isn't in the perf skill. It earns its slot. Consider linking to mm-selector-memoization.md rather than restating the anti-patterns — the duplicated list is what will drift.

Use tokensController as the copy-from exemplar, narrow inputs before
createDeepEqualSelector, and put the install command on discovery pointers.

Co-authored-by: Cursor <cursoragent@cursor.com>
@NicolasMassart

Copy link
Copy Markdown
Contributor Author

Addressed the review on this follow-up commit.

Blockers

  1. Array empty constant now matches EMPTY_TOKENS (Object.freeze([] as Token[]) as Token[]).
    2–3. tokensController.ts is the sole copy-from (selectTokensByAddress, selectAllTokensFlat, selectTokensLength, stable empties). The leaf is labeled illustrative. Factory choice allows a sub-key read plus a stable empty constant on createSelector; identity wrapping of a whole churning slice does not.
  2. Factory table: narrow the input first; createDeepEqualSelector when the narrowed input still churns and the payload is small enough to compare. Workflow step 4 and Requirements match. Authoring Reject list stays short; audits still point at performance / mm-selector-memoization.md.

Nits

  • Overlay intro: Engine wiring → controller-integration.
  • yarn skills --include coding/selector-patterns --save on this overlay and the three discovery pointers.
  • Naming example: selectTokensByChainIdAndAddress.
  • backgroundState Reject scoped to new and changed UI.

Drop restated deadly patterns from Factory and Reject so the catalog
lives only in mm-selector-memoization.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
@NicolasMassart

Copy link
Copy Markdown
Contributor Author

Follow-up on CONTRIBUTING’s “is it a duplicate?” note.

Reject now keeps authoring-only bullets (UI backgroundState, inline useSelector, isEqual band-aids, flag evaluation). Identity wrappers, new refs in result functions, mutation, and huge inputs point at performance (mm-selector-memoization.md) instead of restating that catalog. Factory choice keeps the table and the sub-key + empty-constant nuance; identity-passthrough / copy-before-sort live in the memoization reference.

@NicolasMassart
NicolasMassart enabled auto-merge (squash) September 11, 2026 09:22
@NicolasMassart
NicolasMassart merged commit b83b5fd into main Sep 11, 2026
28 checks passed
@NicolasMassart
NicolasMassart deleted the feat/MCWP-472_selector-patterns branch September 11, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants