Skip to content
Open
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 `platform/notifications-category-sync`: a repo-agnostic workflow for syncing a client with the backend notifications category manifest (fallback snapshot, preference keys, settings rows), with a MetaMask Mobile overlay.
- Add `performance/profiling-regression-proposal`: an evidence-only skill that proposes follow-up actions after MetaMask Mobile CI has already classified a Hermes CPU-profile regression.
- Support explicit-only workflow skills through native invocation controls, preserve repository overlays, and prune managed retired skill names during sync.
- Distribute Perps static review as a shared execution checklist with repository overlays, source-tracked client rules and per-rule evidence outcomes, without a duplicate template catalog.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
repo: metamask-mobile
parent: notifications-category-sync
---

# Notifications Category Sync — MetaMask Mobile

## Where things live

| Concern | Path |
|---------|------|
| Fallback snapshot + `resolveNotificationCategories` (mobile filter, empty list → fallback) | `app/util/notifications/categories/notification-categories-api.ts` |
| Category request, loading state, and fetch-settled fallback behavior | `app/util/notifications/hooks/useNotifications.ts`; `app/util/notifications/categories/categories-fetch-settled.ts` |
| AUS key → i18n stem (`AUS_KEY_TO_I18N_STEM`) | `app/util/notifications/categories/notification-categories-i18n.ts` |
| Flag gate (`socialAI`) and in-app inbox preference filtering | `app/util/notifications/categories/notifications-settings-types.ts` |
| Notification list filtering and category-scoped mark-as-read | `app/components/Views/Notifications/index.tsx` |
| Inbox behavior tests, including category/All mark-as-read | `app/components/Views/Notifications/index.test.tsx` |
| Section registry (`NOTIFICATION_SETTINGS_SECTIONS`, slugs, deeplink resolver) | `app/components/Views/Settings/NotificationsSettings/notificationSettingsSections.ts` |
| Settings rows + unsupported-category `Logger.error` | `app/components/Views/Settings/NotificationsSettings/index.tsx` |
| Per-section detail maps (`SETTINGS_TYPE_BY_SECTION`, layout) | `app/components/Views/Settings/NotificationsSettings/NotificationSettingsSectionContent.tsx` |
| Preference key type (`NotificationPreferenceSection`) | `app/util/notifications/hooks/useNotificationStoragePreferences.ts` (derived from `@metamask/authenticated-user-storage`; `agenticCli` is made required via Omit/Required) |
| Tabs (testID from `categoryTestID(category_id)`) | `app/components/Views/Notifications/NotificationsCategory/` |
| Startup fetch | `useFetchNotificationCategoriesEffect` in `app/util/notifications/hooks/useStartupNotificationsEffect.ts` |

## Step details

1. **Diff**
```bash
curl -s https://notification.api.cx.metamask.io/api/v4/notifications/categories \
| jq -S 'map(.visible_on |= sort | .aus_keys |= sort | .notification_types |= sort)'
```
Compare with `FALLBACK_NOTIFICATION_CATEGORIES` and update `notification-categories-api.test.ts`. The current resolver also uses the fallback when the category list is empty, not only when a request fails. If every new category has empty `aus_keys`, skip to Verify.
3. **Package**: check the key exists in the installed `@metamask/authenticated-user-storage` `NotificationPreferences` (`package.json`).
4. **Copy**: add the key to `AUS_KEY_TO_I18N_STEM`; add `app_settings.notifications_opts.<stem>_title` and `<stem>_desc` to `locales/languages/en.json` only. Other locale files are updated by the translation process, so leave them untouched. Extend `notification-categories-i18n.test.ts`.
5. **Registry**: add the kebab slug to `NotificationSettingsSectionSlug` and an entry to `NOTIFICATION_SETTINGS_SECTIONS` (`slug`, `type` = AUS key, `titleKey`, `descriptionKey`, a valid `IconName`, `showStatus`, `requiresSocialLeaderboard` only if flag-gated). Add the key to the maps in `NotificationSettingsSectionContent.tsx`; both are `Record<NotificationPreferenceSection, …>`, so `yarn lint:tsc` flags a miss. Update `notificationSettingsSections.test.ts` and the section content tests.
6. **Touchpoints**: grep `priceAlerts|price_alerts|price-alerts|agenticCli` across `app docs tests locales` (skip `tests/coverage`). Known hits:
- `tests/api-mocking/mock-responses/defaults/user-storage.ts` (AUS preferences mock)
- `docs/readme/deeplinking.md` (section slug list and the `notifications-settings` row) and `handleNotificationsSettingsUrl` tests
- `featureNotificationsGateConfig.ts` and `notifications.feature_gate.<stem>.*` locale keys, only if the feature uses the gate
- flag gate in `getNotificationsSettingsSectionConfigs` if the category is flag-gated
- new `notification_types` needing inbox rendering: `notification-states/` and `TRIGGER_TYPES` from `@metamask/notification-services-controller`
7. **Tests**: `NotificationsSettings/index.test.tsx` and `NotificationsSettings.view.test.tsx`; the unsupported log uses `Logger.error` from `app/util/Logger`. Cover inbox filtering and category-scoped mark-as-read in `app/components/Views/Notifications/index.test.tsx`. The settings test checks one log on initial render; it does not guarantee deduplication across later category updates.

## Verify

```bash
yarn jest app/util/notifications app/components/Views/Settings/NotificationsSettings app/components/Views/Notifications
yarn lint:tsc
yarn lint
```

User-facing changes need a CHANGELOG entry (see the `pr-changelog` skill).
58 changes: 58 additions & 0 deletions domains/platform/skills/notifications-category-sync/skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
name: notifications-category-sync
description: >-
Sync a client with the backend-driven notifications category manifest when the
backend adds or changes a notification category, notification type, or
preference (AUS) key. Use when asked to support a new notification category or
type, add a notifications settings row, refresh the category fallback
snapshot, or when logs report an unsupported notification category.
maturity: stable
---

# Notifications Category Sync

The notifications backend publishes a category manifest (`GET /api/v4/notifications/categories`, unauthenticated, same for every user). Clients render tabs and settings rows from it, but keep presentation (copy, icon, slug, detail screen) locally, keyed by the user-storage preference key (`aus_keys`). This skill closes the gap when the manifest gains or changes entries.

## When to use

- Backend added, removed, renamed or re-ordered a category, or changed its `aus_keys`, `notification_types` or `visible_on`
- A new preference key must get a settings row
- The local fallback snapshot no longer matches the live response
- Logs report an unsupported notification category

Out of scope: rendering a new notification type's body or push payload (notification-state mapping), and gating a feature behind notifications.

## Manifest contract

Each entry: `category_id`, `aus_keys[]`, `notification_types[]`, `visible_on[]`.

- Array order is display order.
- A client renders only entries whose `visible_on` includes its platform. Empty `visible_on` means hidden on every platform but still present in the manifest.
- `aus_keys` are the toggle pivot. Non-empty with no local section is a client bug: log the error and hide the row. Empty means display-only (inbox tab and filtering, no settings row, no error).
- `category_id` is backend-owned and may not match local naming. Resolve local sections and copy through `aus_keys`, never `category_id`.
- `notification_types` is advisory; notifications carry a server-resolved `category` (`''` means uncategorized).
- Keep the fallback snapshot aligned with the live response, including hidden entries and backend order. Clients may also use the fallback when the endpoint returns an empty category list. Check the consuming client’s resolver for its exact fallback condition.

## Workflow

1. **Diff live against the fallback.** Fetch the endpoint, normalize (sort keys and arrays), compare with the client's fallback snapshot. List added, removed and changed categories.
2. **Classify each change.**
- New entry with empty `aus_keys`: display-only. Update the snapshot and its test, then verify.
- New entry with an `aus_key` the client already supports: update the snapshot only.
- New `aus_key`: full local support (steps 3-6).
- Changed `visible_on`, `notification_types` or order: snapshot and tests only.
3. **Confirm the preference key exists** in the client's preference types. If the installed storage package lacks it, stop and report; a dependency bump is a separate decision.
4. **Add presentation for the key**: copy (title and description, in the source locale; other locales follow the repo's translation process), icon, deeplink slug, whether the row shows channel status, and any feature-flag gate.
5. **Register the section** in the local registry and every per-key map the compiler flags (analytics settings type, detail-screen layout).
6. **Mirror per-key touchpoints.** Grep an existing key in all spellings (camelCase, snake_case, kebab-case) and update every per-category list: deeplink docs and tests, API mocks, feature-gate config.
7. **Update the fallback snapshot and tests**: snapshot mirror, settings-row rendering, the unsupported-category log for non-empty `aus_keys` with no section, and display-only skipping. Assert the intended logging frequency separately if the client promises deduplication.
8. **Verify.** Re-run step 1 (no diff left), then the repo's unit tests, type check and lint.

Do not guess a multi-key category's copy or icon; ask. Do not bump dependencies silently.

## Common mistakes

- Keying local logic on `category_id` instead of `aus_keys`
- Dropping hidden entries from the fallback so it no longer mirrors the live response
- Adding a settings row for a display-only category
- Updating the section registry but not the type-keyed maps, which only fail at compile time
Loading