Skip to content

Commit 3c7487d

Browse files
feat(controller-integration): enhance skill descriptions for clarity
1 parent b1a9bcf commit 3c7487d

3 files changed

Lines changed: 34 additions & 16 deletions

File tree

domains/coding/skills/controller-integration/repos/metamask-extension.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,28 @@ The integration surface is `app/scripts/messenger-client-init/` (the pattern is
1515
| Stateless service | No persisted state (e.g. an API service) | Same as modular, minus state steps |
1616
| Wallet-managed | Name is one of: AccountsController, ApprovalController, ConnectivityController, KeyringController, NetworkController, RemoteFeatureFlagController, StorageService | `this.wallet.getInstance('<Name>')` — construction happens inside `@metamask/wallet` |
1717

18+
Independently of the path above, the controller class itself comes from one of two sources — the `messenger-client-init/` wiring in §3 is identical either way, only step 1 (and where types are imported from) differs:
19+
20+
| Source | When | Where the class lives |
21+
|---|---|---|
22+
| npm package | Controller is owned/shared outside the extension (most new controllers) | `@metamask/<name>-controller`, imported as a dependency |
23+
| Repo-local | Controller is extension-only | `app/scripts/controllers/<name>/` (class + a `<name>-controller.types.ts` exporting the messenger type) — e.g. `app/scripts/controllers/rewards/rewards-controller.ts` |
24+
1825
## 2. Canonical examples (read, don't guess)
1926

2027
| Controller | Copy for |
2128
|---|---|
22-
| `app/scripts/messenger-client-init/geolocation-controller-init.ts` + `messengers/geolocation-controller-messenger.ts` (+ both `.test.ts`) | Default pattern: init fn, default-state hydration, `api` methods, minimal messenger, tests |
29+
| `app/scripts/messenger-client-init/geolocation-controller-init.ts` + `messengers/geolocation-controller-messenger.ts` (+ both `.test.ts`) | Default pattern (npm-package controller): init fn, default-state hydration, `api` methods, minimal messenger, tests |
2330
| `app/scripts/messenger-client-init/transaction-pay-controller-init.ts` + `messengers/transaction-pay-controller-messenger.ts` | Separate init messenger used inside constructor callbacks, heavy cross-controller delegation |
24-
| `app/scripts/messenger-client-init/rewards-controller-init.ts` + `messengers/rewards-controller-messenger.ts` | Remote-feature-flag gating via init messenger |
31+
| `app/scripts/controllers/rewards/rewards-controller.ts` (+ `rewards-controller.types.ts` exporting `RewardsControllerMessenger`) + `app/scripts/messenger-client-init/rewards-controller-init.ts` + `messengers/rewards-controller-messenger.ts` | Repo-local controller pattern, plus remote-feature-flag gating via init messenger. The init/messenger files are indistinguishable in shape from the npm-package case — only the class import path differs |
2532
| `PerpsController` entries in `app/scripts/metamask-controller.js` + `shared/lib/environment.ts` | Build-flag gating (conditional init-map spread) |
2633
| `app/scripts/messenger-client-init/data-deletion-service-init.ts` | Stateless service |
2734

2835
## 3. Ordered steps
2936

3037
| # | File | Action |
3138
|---|---|---|
32-
| 1 | `package.json` | `yarn add @metamask/<name>-controller`, then `yarn lint:lockfile:dedupe:fix && yarn allow-scripts auto && yarn lavamoat:auto && yarn attributions:generate` (skip if repo-local) |
39+
| 1 | `package.json`, or `app/scripts/controllers/<name>/` | **npm package**: `yarn add @metamask/<name>-controller`, then `yarn lint:lockfile:dedupe:fix && yarn allow-scripts auto && yarn lavamoat:auto && yarn attributions:generate`. **Repo-local**: create the folder with the controller class and a `<name>-controller.types.ts` exporting the messenger type — mirror `app/scripts/controllers/rewards/` — no dependency commands needed |
3340
| 2 | `app/scripts/messenger-client-init/messengers/<name>-controller-messenger.ts` | Create `getXControllerMessenger` (skeleton below). Add `getXControllerInitMessenger` (namespace `'XControllerInit'`) only if the init fn itself needs actions/events beyond the controller's messenger type |
3441
| 3 | `app/scripts/messenger-client-init/messengers/<name>-controller-messenger.test.ts` | `expect(messenger).toBeInstanceOf(Messenger)`; optionally assert delegation via a `delegate` spy |
3542
| 4 | `app/scripts/messenger-client-init/messengers/index.ts` | Export the factory fns; add to `MESSENGER_FACTORIES`: `{ getMessenger, getInitMessenger: noop }` (lodash `noop`) or the real init-messenger fn |

domains/coding/skills/controller-integration/repos/metamask-mobile.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,34 @@ parent: controller-integration
1313
| Stateless service | No persisted state (e.g. an API service) | Same as modular, minus state steps |
1414
| Wallet-owned | Name is one of: AccountsController, ApprovalController, ConnectivityController, KeyringController, NetworkController, RemoteFeatureFlagController, StorageService | `this.#wallet.getInstance('<Name>')` — never an init function |
1515

16+
Independently of the path above, the controller class itself comes from one of two sources — the wiring steps in §3 are identical either way, only step 1 (and where types are imported from) differs:
17+
18+
| Source | When | Where the class lives |
19+
|---|---|---|
20+
| npm package | Controller is owned/shared outside mobile (most new controllers) | `@metamask/<name>-controller`, imported as a dependency |
21+
| Repo-local | Controller is mobile-only | `app/core/Engine/controllers/<name>-controller/` (own folder: class, `types.ts`, default state, init fn) — e.g. `card-controller/` (`CardController`), `rewards-controller/` (`RewardsController`) |
22+
1623
## 2. Canonical examples (read, don't guess)
1724

1825
| Controller | Copy for |
1926
|---|---|
20-
| `app/core/Engine/controllers/config-registry-controller-init.ts` + `app/core/Engine/messengers/config-registry-controller-messenger.ts` | Default pattern: init fn, messenger + delegate, tests, selector, fixture entry |
27+
| `app/core/Engine/controllers/config-registry-controller-init.ts` + `app/core/Engine/messengers/config-registry-controller-messenger.ts` | Default pattern (npm-package controller): init fn, messenger + delegate, tests, selector, fixture entry |
2128
| `app/core/Engine/controllers/money-account-upgrade-controller-init.ts` + `.../money-account-upgrade-controller-messenger.ts` | Init messenger, cross-controller delegation, remote-flag init-time gating |
29+
| `app/core/Engine/controllers/card-controller/` (`CardController.ts`, `types.ts`, `index.ts` as init) + `app/core/Engine/messengers/card-controller-messenger.ts` | Repo-local controller pattern: class + messenger type + default state co-located in one folder, init fn in the folder's `index.ts` |
30+
| `app/core/Engine/controllers/rewards-controller/` (`RewardsController.ts`) + `app/core/Engine/messengers/rewards-controller-messenger.ts` | Repo-local controller with remote-flag gating |
2231
| `app/features/SampleFeature/controllers/sample-petnames-controller-init.ts` | `ONLY_INCLUDE_IF(sample-feature)` fencing only — its tests/selector lag the pattern, do not copy those |
2332

2433
## 3. Ordered steps
2534

2635
| # | File | Action |
2736
|---|---|---|
28-
| 1 | `package.json` | `yarn add @metamask/<name>-controller` (skip if repo-local) |
37+
| 1 | `package.json`, or `app/core/Engine/controllers/<name>-controller/` | **npm package**: `yarn add @metamask/<name>-controller`. **Repo-local**: create the folder with the controller class (extend `BaseController`), `types.ts` (state, messenger type, `ControllerStateChangeEvent`-based Events union), and default state — mirror `card-controller/` or `rewards-controller/` |
2938
| 2 | `app/core/Engine/messengers/<name>-controller-messenger.ts` | Create `getXControllerMessenger` (skeleton below). Add `getXControllerInitMessenger` too if init-time gating is needed (own namespace `<Name>Initialization`) |
3039
| 3 | `app/core/Engine/messengers/<name>-controller-messenger.test.ts` | `expect(messenger).toBeInstanceOf(Messenger)` |
3140
| 4 | `app/core/Engine/messengers/index.ts` | Add to `MESSENGER_FACTORIES`: `{ getMessenger, getInitMessenger: noop }` (or the real init messenger fn) |
32-
| 5 | `app/core/Engine/types.ts` | Add to `GlobalActions`, `GlobalEvents`, `MessengerClients`, `EngineState`, `MessengerClientsToInitialize` (+ `RequiredControllers`/`OptionalControllers` if optional) |
33-
| 6 | `app/core/Engine/controllers/<name>-controller-init.ts` | Create `MessengerClientInitFunction` (skeleton below), return `{ controller }` only |
34-
| 7 | `app/core/Engine/controllers/<name>-controller-init.test.ts` | Use `buildMessengerClientInitRequestMock` from `app/core/Engine/utils/test-utils.ts` |
41+
| 5 | `app/core/Engine/types.ts` | Add to `GlobalActions`, `GlobalEvents`, `MessengerClients`, `EngineState`, `MessengerClientsToInitialize` (+ `RequiredControllers`/`OptionalControllers` if optional). Import the controller/messenger types from `@metamask/<name>-controller` (package) or from the local `controllers/<name>-controller/types.ts` (repo-local) |
42+
| 6 | `app/core/Engine/controllers/<name>-controller-init.ts`, or `app/core/Engine/controllers/<name>-controller/index.ts` for a repo-local controller | Create `MessengerClientInitFunction` (skeleton below), return `{ controller }` only |
43+
| 7 | `app/core/Engine/controllers/<name>-controller-init.test.ts` (co-located `index.test.ts` if repo-local) | Use `buildMessengerClientInitRequestMock` from `app/core/Engine/utils/test-utils.ts` |
3544
| 8 | `app/core/Engine/constants.ts` | Append `'<Name>:stateChange'` to `BACKGROUND_STATE_CHANGE_EVENT_NAMES`. ⚠️ Copy the exact event name from the package's `Events` type — some emit `:stateChanged` |
3645
| 9 | `app/core/Engine/Engine.ts` | (a) import init fn (b) add to `initFunctions` **after** every controller it depends on — insertion order is init order (c) add to `this.context` (d) add to **both** the destructuring and return object of `get state()` |
3746
| 10 | `app/util/test/initial-background-state.json` | Add default state (run step in §7 verify to get the exact expected value) |
@@ -104,7 +113,7 @@ export const xControllerInit: MessengerClientInitFunction<
104113
| Controller missing from `Engine.state` getter | `UPDATE_BG_STATE` writes `undefined` into Redux for that key |
105114
| Calling a non-delegated action | Runtime throw from `@metamask/messenger` at call time |
106115
| Dependency read before it's initialized | `Error: Messenger client requested before it was initialized: <Name>` — move the entry later in `initFunctions` |
107-
| Controller metadata has no `persist: true` key | Redux updates but state never survives restart (silently skipped, logged only) |
116+
| Controller metadata has no `persist: true` key | Redux updates but state never survives restart (silently skipped, logged only). For a repo-local controller this metadata is authored by you in the class's own `metadata` object, not inherited from a package |
108117

109118
## 7. Verify
110119

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
---
22
name: controller-integration
33
description: >
4-
Wire a new or existing controller into the MetaMask Mobile Engine or the
5-
MetaMask Extension background. Use this skill when: adding a controller to
6-
Engine or to the extension's messenger-client init, creating a controller
7-
init function or messenger, delegating actions/events between controllers,
8-
adding a controller to EngineState/Engine.context or to MESSENGER_FACTORIES
9-
and the MessengerClient union, or debugging a controller whose state never
10-
reaches Redux/the UI or resets on restart.
4+
Wire a new or existing controller — whether imported from a
5+
`@metamask/*-controller` npm package or defined locally in the repo — into
6+
the MetaMask Mobile Engine or the MetaMask Extension background. Use this
7+
skill when: adding a controller to Engine or to the extension's
8+
messenger-client init, creating a controller init function or messenger,
9+
delegating actions/events between controllers, adding a controller to
10+
EngineState/Engine.context or to MESSENGER_FACTORIES and the
11+
MessengerClient union, or debugging a controller whose state never reaches
12+
Redux/the UI or resets on restart.
1113
---

0 commit comments

Comments
 (0)