metamask-extension Agent Skills & AI Rules

GitHub

Globe_with_meridians: :electric_plug: The MetaMask browser extension enables browsing Ethereum blockchain enabled websites

13,193 stars TypeScript TypeScript 1 file Β· ~35,512 tokens #brave#chrome#dapp#dapp-developers#edge#ethereum#extension#firefox
RAW Rules
4 Discovered Rules β€’ ~13,052 Estimated Tokens
Quick:
Loading skill content...

AI Agent Protocol & Instructions

AGENTS.md root-instruction ~12,152 tokens
Raw
# AGENTS.md

Instructions for AI coding agents working on MetaMask Browser Extension.

---

## Agent Instructions Summary

**Project Type:** Browser extension (Chrome/Firefox)
**Languages:** TypeScript (required for new code), JavaScript (legacy)
**UI Framework:** React with functional components + hooks
**State Management:** Redux + BaseController architecture
**Testing:** Jest (unit), Playwright (E2E)
**Build System:** Webpack (with LavaMoat for production)
**Security:** LavaMoat policies required for all dependency changes

### Critical Rules for Agents

1. **ALWAYS use TypeScript** for new files (never JavaScript)
2. **ALWAYS run `yarn lint:changed:fix`** before committing
3. **ALWAYS update LavaMoat policies** after dependency changes: `yarn lavamoat:auto`
4. **ALWAYS colocate tests** with source files (`.test.ts`/`.test.tsx`)
5. **ALWAYS use yarn.cmd** if you're running in PowerShell
6. **ALWAYS use `oxfmt` for code formatting**; Prettier is only for JSON formatting and changelog validation
7. **NEVER use class components** (use functional components with hooks)
8. **NEVER modify git config** or run destructive git operations
9. **NEVER commit** unless explicitly requested by user
10. **NEVER stage changes** unless explicitly requested by user
11. **WHEN asked to commit, use Conventional Commits** format for commit messages
12. **WHEN asked to open a PR, use a Conventional Commits title** unless user specifies otherwise
13. **WHEN asked to open a PR, open it as DRAFT** unless user specifies otherwise
14. **WHEN using `.github/pull-request-template.md`, comment out non-applicable sections including the section title**
15. **WHEN using `.github/pull-request-template.md`, the manual testing section
    should contain instructions for how to _manually_ test the changes. It must not
    list steps for automated testing.**
    - Good Instructions:
      - Run extension
      - Go to homepage, asset list
      - Use Mainnet or Linea as the selected/enabled network.
      - Verify mUSD is visible even with 0 balance.
      - Enable "Hide zero-balance tokens".
      - Verify mUSD is still visible.
      - Ensure token sort is by descending balance and low-value assets are collapsed.
      - Verify mUSD is not hidden inside the low-value collapsed section.
    - Bad Instructions:
      - `yarn lint:changed:fix`
      - `yarn test:unit shared/lib/deep-links/metrics.test.ts shared/lib/deep-links/utils.test.ts ui/pages/onboarding-flow/creation-successful/creation-successful.test.tsx`

16. **BEFORE modifying any `.github/workflows/` file**, read `.github/AGENTS.md` for CI-specific rules (consolidation patterns, required job wiring, merge queue considerations)
17. **DEEPLINK INTERSTITIAL SECURITY β€” EXTREMELY HIGH RISK:** Before implementing any change that can cause fewer deep links to show the security interstitial, agents **MUST stop and obtain explicit, documented consent from `@MetaMask/extension-security-team`**. Without documented Security approval, do not make the changeβ€”even when it appears necessary to complete another feature, migration, refactor, or test fix. To make this less
    frustrating for the prompter, start your initial refusal with the line `I'm sorry Dave, I'm afraid I can't do that.\n`.

### Comprehensive Guidelines Location

> **Agent skills:** `yarn install` prepares the public [MetaMask/skills](https://github.com/MetaMask/skills) cache; `yarn skills` syncs generated files into `.cursor/`, `.claude/`, and `.agents/` (see README -> "AI Agent Skills"). If a referenced skill is missing locally, run `yarn skills`.

Read these files for detailed coding standards:

- Controller patterns: `.cursor/rules/mms-controller-guidelines/RULE.md`
- Testing (unit + E2E create/maintain): `.cursor/rules/mms-extension-testing/RULE.md` β€” primary entrypoint; see `knowledge/extension-testing-layers.md`
- E2E testing standards (repo index): `./test/e2e/AGENTS.md`
- E2E create/maintain (nested refs): `.agents/skills/mms-extension-testing/SKILL.md` β†’ `references/e2e.md`
- CI workflows: `.github/AGENTS.md`
- Front-end performance:
  - `.cursor/rules/mms-perf-rendering/RULE.md` (rendering performance - start here)
  - `.cursor/rules/mms-perf-hooks-effects/RULE.md` (hooks & effects)
  - `.cursor/rules/mms-perf-react-compiler/RULE.md` (React Compiler & anti-patterns)
  - `.cursor/rules/mms-perf-state-management/RULE.md` (Redux & state management)
- PR workflow: `.cursor/rules/mms-pr-guidelines/RULE.md`
- Code style: `.cursor/rules/mms-coding-guidelines/RULE.md`
- Official guidelines: `.github/guidelines/CODING_GUIDELINES.md`

---

## Quick Setup

### Prerequisites

- **Node.js+** (use `nvm use` to auto-select development version specified in `.nvmrc`)
- **Yarn** (managed by Corepack, included with Node.js)
- **Infura API Key** (free at https://infura.io)

### First-Time Setup

```bash
# 1. Enable Corepack (manages Yarn)
corepack enable

# 2. Install dependencies
yarn install

# 3. Copy and configure environment
cp .metamaskrc.dist .metamaskrc

# 4. Edit .metamaskrc and add your Infura API key
# INFURA_PROJECT_ID=your_key_here

# 5. Start development build (Chrome/Chromium with MV3)
yarn start

# 6. Load extension in browser
# Chrome: See docs/add-to-chrome.md
# Firefox: See docs/add-to-firefox.md
```

### Optional Configuration

In `.metamaskrc`, you can also configure:

- `PASSWORD` - Auto-fill development wallet password
- `SEGMENT_WRITE_KEY` - For MetaMetrics debugging
- `SENTRY_DSN` - For error tracking debugging

### Common Setup Issues

| Issue                            | Solution                                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `command not found: yarn`        | Run `corepack enable`                                                                                   |
| Build fails with policy errors   | Run `yarn lavamoat:auto`                                                                                |
| Invalid Infura key error         | Check `INFURA_PROJECT_ID` in `.metamaskrc`                                                              |
| Anvil won't start                | Ensure port 8545 is available and `yarn foundryup` has installed the binary                             |
| Git hooks not working in VS Code | Follow [Husky troubleshooting](https://typicode.github.io/husky/troubleshooting.html#command-not-found) |

---

## Common Commands

### Building

```bash
# Development Builds (with file watching and hot reload)
yarn start                  # Chrome MV3 (default)
yarn start:mv2             # Firefox MV2
yarn start:flask           # Flask build (beta features)
yarn start:with-state      # Start with preloaded wallet state

# Production Builds
yarn dist                  # Chrome MV3
yarn dist:mv2              # Firefox MV2

# Test Builds (for E2E testing)
yarn build:test            # Build with LavaMoat enabled
yarn start:test            # Build with LavaMoat disabled (faster iteration)
yarn build:test:flask      # Flask test build
yarn build:test:mv2        # Firefox MV2 test build

# Download pre-built test builds (fastest)
yarn download-builds --build-type test
```

**Build System Notes:**

- `yarn start` uses Webpack (faster, development)
- `yarn dist` uses Webpack + LavaMoat (production)
- `yarn start` skips LavaMoat by default for speed; use `yarn start:lavamoat` to enable it
- Test builds are required for E2E tests (not dev builds)

### Testing

```bash
# Unit Tests
yarn test                  # Lint + unit tests
yarn test:unit             # Unit tests only
yarn test:unit:watch       # Watch mode
yarn test:unit:coverage    # With coverage report

# E2E Tests
yarn test:e2e:chrome       # Run all E2E tests (Chrome)
yarn test:e2e:firefox      # Run all E2E tests (Firefox)

# Single E2E test with options
yarn test:e2e:single test/e2e/tests/account-menu/account-details.spec.js \
  --browser=chrome \
  --leave-running \
  --debug

# Integration Tests
yarn test:integration
yarn test:integration:coverage

# Playwright Tests
yarn test:e2e:benchmark    # Performance benchmarks
```

**Testing Notes:**

- Unit tests should be colocated with source files (`.test.ts`/`.test.tsx`)
- Always create a test build before running E2E tests
- Use `--leave-running` to debug failed E2E tests
- See `.cursor/rules/mms-extension-testing/RULE.md` for testing standards

### Linting & Formatting

```bash
# Run all linters
yarn lint                  # JSON formatting + oxfmt + ESLint + TypeScript + Styles + Images

# Individual linters
yarn lint:json             # Prettier JSON formatting check
yarn lint:format           # oxfmt code formatting check
yarn lint:eslint           # ESLint only
yarn lint:tsc              # TypeScript type checking
yarn lint:styles           # Stylelint for SCSS

# Auto-fix
yarn lint:fix              # Fix all auto-fixable issues
yarn lint:json:fix         # Fix JSON formatting with Prettier
yarn lint:format:fix       # Fix code formatting with oxfmt
yarn lint:eslint:fix       # Fix ESLint issues

# Lint only changed files (faster)
yarn lint:changed
yarn lint:changed:fix
```

**Formatter Notes:**

- Use `yarn lint:changed:fix` for normal agent work; it applies the repo's formatter choices to changed files.
- Use `yarn lint:format:fix` or `oxfmt -c oxfmt.config.mts` for JavaScript, TypeScript, JSX, TSX, and other code formatting.
- Use `yarn lint:json:fix` for JSON files such as `package.json`; this is the main remaining Prettier formatting path.
- Do not run Prettier directly on code files.

### Development Tools

```bash
# Test Dapps
yarn dapp                  # Start test dapp on :8080
yarn dapp-multichain       # Multichain test dapp
yarn dapp-solana           # Solana test dapp
yarn dapp-chain            # Dapp with local Anvil

# DevTools
yarn devtools:react        # React DevTools
yarn devtools:redux        # Redux DevTools
yarn start:dev             # Start with both DevTools

# Local Blockchain
yarn anvil                 # Start Anvil (Foundry) on port 8545

# Storybook
yarn storybook             # Component documentation/development
yarn storybook:build       # Build static storybook

# Git Hooks
yarn githooks:install      # Install pre-commit hooks
```

### Dependency Management

```bash
# When adding/updating/removing dependencies:

# 1. Install/update package
yarn add package-name
yarn upgrade package-name

# 2. Deduplicate lockfile
yarn lint:lockfile:dedupe:fix

# 3. Update allow-scripts (determines which install scripts can run)
yarn allow-scripts auto

# 4. Update LavaMoat policies
yarn lavamoat:auto         # Regenerates the webpack LavaMoat policies

# Or use MetaMask bot (for team members with repo branch):
# Comment on PR: @metamaskbot update-policies
```

**Important:** Always update LavaMoat policies when dependencies change.

`attribution.txt` is generated by the release process. **Do not generate it or commit and push changes to it in PRs**, including dependency updates.

---

## Common Agent Workflows

### Workflow: Adding a New Feature

```bash
# 1. Start development build
yarn start

# 2. Create new files (MUST be TypeScript)
# - Component: ui/components/feature-name/feature-name.tsx
# - Test: ui/components/feature-name/feature-name.test.tsx
# - Types: ui/components/feature-name/feature-name.types.ts

# 3. Make changes

# 4. Run lint and tests on changed files
yarn lint:changed:fix
yarn test:unit path/to/feature-name.test.tsx

# 5. If test needs E2E, build test build
yarn build:test
yarn test:e2e:single test/e2e/tests/new-test.spec.js --browser=chrome
```

### Workflow: Modifying Existing Code

```bash
# 1. Identify file type and read relevant guidelines
# - Controller? Read .cursor/rules/mms-controller-guidelines/RULE.md
# - React component? Read .cursor/rules/mms-coding-guidelines/RULE.md
# - Test? Read .cursor/rules/mms-extension-testing/RULE.md

# 2. Make changes following guidelines

# 3. Run linter on changed files
yarn lint:changed:fix

# 4. Run existing tests
yarn test:unit path/to/modified-file.test.ts

# 5. Update tests if behavior changed

# 6. Check for circular dependencies
yarn circular-deps:check
```

### Workflow: Adding/Updating Dependencies

```bash
# 1. Add or update package
yarn add package-name
# OR
yarn upgrade package-name

# 2. REQUIRED: Deduplicate lockfile
yarn lint:lockfile:dedupe:fix

# 3. REQUIRED: Update allow-scripts
yarn allow-scripts auto

# 4. REQUIRED: Update LavaMoat policies (this may take several minutes)
yarn lavamoat:auto

# 5. Test the build
yarn build:test

# 6. Commit all changes including:
#    - package.json
#    - yarn.lock
#    - lavamoat/webpack/*/policy.json
```

### Workflow: Fixing a Bug

```bash
# 1. Create a failing test that reproduces the bug
# Add test to existing .test.ts file or create new one

# 2. Run the test to confirm it fails
yarn test:unit path/to/test-file.test.ts

# 3. Fix the bug in source code

# 4. Run test again to confirm fix
yarn test:unit path/to/test-file.test.ts

# 5. Run all related tests
yarn test:unit

# 6. Lint changes
yarn lint:changed:fix

# 7. If bug is in E2E scenario
yarn build:test
yarn test:e2e:single path/to/test.spec.js --browser=chrome
```

### Workflow: Creating a Controller

```bash
# 1. MUST read controller guidelines first
# Read .cursor/rules/mms-controller-guidelines/RULE.md

# 2. Create controller file (TypeScript only)
# Location: app/scripts/controllers/your-controller/your-controller.ts

# 3. Controller MUST:
#    - Extend BaseController from @metamask/base-controller
#    - Define state type
#    - Define metadata for all state properties
#    - Export getDefaultYourControllerState() function
#    - Use messenger for inter-controller communication
#    - Use selectors for derived state (not getter methods)

# 4. Create test file
# Location: app/scripts/controllers/your-controller/your-controller.test.ts

# 5. Create types file
# Location: app/scripts/controllers/your-controller/types.ts

# 6. Run tests
yarn test:unit app/scripts/controllers/your-controller/your-controller.test.ts

# 7. Lint
yarn lint:changed:fix
```

### Controller Development Patterns

When creating a controller, follow these critical patterns from `.cursor/rules/mms-controller-guidelines/RULE.md`:

#### State Metadata Requirements

**Every state property MUST have metadata with these properties:**

| Property                                | Type    | Purpose                   | Example Value              |
| --------------------------------------- | ------- | ------------------------- | -------------------------- |
| `anonymous` OR `includeInDebugSnapshot` | boolean | Safe for Sentry? (no PII) | `anonymous: true`          |
| `includeInStateLogs`                    | boolean | Include in state logs?    | `false` for sensitive data |
| `persist`                               | boolean | Save to storage?          | `true` for user data       |
| `usedInUi`                              | boolean | Used by UI?               | `true` if rendered         |

**Example:**

```typescript
const tokensControllerMetadata = {
  tokens: {
    anonymous: true, // No PII, safe for Sentry
    includeInStateLogs: true, // Safe to include in logs
    persist: true, // Should be saved
    usedInUi: true, // Rendered in UI
  },
  apiKey: {
    anonymous: false, // Sensitive
    includeInStateLogs: false, // Must exclude from logs
    persist: true, // But should be saved
    usedInUi: false, // Backend only
  },
};
```

#### Default State Function Pattern

**ALWAYS export function, NEVER export object:**

```typescript
βœ… CORRECT: Returns new object each time
export function getDefaultTokensControllerState(): TokensControllerState {
  return {
    tokens: [],
    lastUpdated: 0,
  };
}

❌ WRONG: Shared object reference (mutation risk)
export const defaultTokensControllerState = {
  tokens: [],
  lastUpdated: 0,
};
```

#### Constructor Single Options Bag

**ALWAYS use single options object, NO positional arguments:**

```typescript
βœ… CORRECT:
constructor({
  messenger,
  state = {},
  apiKey,        // All options in one bag
  isEnabled,
}: TokensControllerOptions) {
  super({
    name: 'TokensController',
    metadata: tokensControllerMetadata,
    messenger,
    state: { ...getDefaultTokensControllerState(), ...state },
  });
}

❌ WRONG:
constructor(
  options: ControllerOptions,
  apiKey: string,     // Separate positional arg - BAD
  isEnabled: boolean, // Separate positional arg - BAD
) { }
```

#### Action Methods (Not Setters)

**Model high-level user actions, not property changes:**

```typescript
❌ WRONG: Generic setters
setTokenData(data: any) { }
updateField(field: string, value: any) { }

βœ… CORRECT: Action-based methods
addToken(token: Token) {
  if (!token.address) {
    throw new Error('Token address required');
  }

  this.update((state) => {
    state.tokens.push(token);
    state.lastUpdated = Date.now();
  });
}

removeToken(address: string) {
  this.update((state) => {
    state.tokens = state.tokens.filter(t => t.address !== address);
    state.lastUpdated = Date.now();
  });
}
```

#### Keep State Minimal - Use Selectors

**NEVER store derived values in state:**

```typescript
❌ WRONG: Derived values in state
type State = {
  tokens: Token[];
  tokenCount: number;  // DON'T STORE - derive it!
  hasTokens: boolean;  // DON'T STORE - derive it!
};

βœ… CORRECT: Minimal state + selectors
type State = {
  tokens: Token[];  // Only essential data
};

// Export selectors for derived values
export const tokensControllerSelectors = {
  selectTokens: (state: State) => state.tokens,
  selectTokenCount: (state: State) => state.tokens.length,
  selectHasTokens: (state: State) => state.tokens.length > 0,
};
```

#### Cleanup with destroy()

**Implement if controller has background tasks:**

```typescript
class TokensController extends BaseController</*...*/> {
  #pollInterval: NodeJS.Timeout | null = null;

  constructor(options: Options) {
    super(/* ... */);
    if (options.enablePolling) {
      this.#startPolling();
    }
  }

  destroy() {
    // Clean up resources
    if (this.#pollInterval) {
      clearInterval(this.#pollInterval);
      this.#pollInterval = null;
    }

    // Call super to clean up messenger
    super.destroy();
  }
}
```

**See `.cursor/rules/mms-controller-guidelines/RULE.md` for complete patterns with detailed examples.**

---

### Decision: Which Test Build to Use?

```
IF you need to run E2E tests:
  IF you're iterating/debugging:
    β†’ Use `yarn start:test` (faster, LavaMoat disabled)
  IF you're doing final verification:
    β†’ Use `yarn build:test` (slower, LavaMoat enabled, matches production)

IF you're developing with feature flags:
  β†’ Use `FEATURE_FLAG=1 yarn build:test`
  β†’ Then run E2E: `yarn test:e2e:single path/to/test.spec.js`

IF you're working on Firefox compatibility:
  β†’ Use `yarn build:test:mv2`
  β†’ Then test: `yarn test:e2e:firefox`
```

### Decision: Where to Put New Code?

```
IF creating a controller:
  β†’ app/scripts/controllers/controller-name/

IF creating a UI component:
  β†’ ui/components/component-name/ (for reusable components)
  β†’ ui/pages/page-name/ (for page-level components)

IF creating a utility function:
  β†’ shared/lib/ (if used by both background and UI)
  β†’ app/scripts/lib/ (if only used by background)
  β†’ ui/helpers/ (if only used by UI)

IF creating constants:
  β†’ shared/constants/

IF creating TypeScript types:
  β†’ shared/types/ (for shared types)
  β†’ types/ (for project-wide types)
  β†’ [component-dir]/types.ts (for component-specific types)

IF creating a state migration:
  β†’ Run: yarn generate:migration
  β†’ Edits: app/scripts/migrations/[number].ts
```

### Decision: Which Browser Target?

```
IF user specifies Chrome, Edge, or Brave:
  β†’ Use MV3 (Manifest V3)
  β†’ Commands: yarn start, yarn dist, yarn build:test

IF user specifies Firefox:
  β†’ Use MV2 (Manifest V2)
  β†’ Commands: yarn start:mv2, yarn dist:mv2, yarn build:test:mv2
  β†’ Set ENABLE_MV3=false

IF user doesn't specify:
  β†’ Default to Chrome MV3
  β†’ Use: yarn start
```

---

## Project Structure

### High-Level Directory Layout

```
metamask-extension/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ scripts/           # Background scripts & controllers (860 TS, 234 JS)
β”‚   β”‚   β”œβ”€β”€ controllers/   # Business logic controllers
β”‚   β”‚   β”œβ”€β”€ lib/           # Utility libraries
β”‚   β”‚   └── migrations/    # State migration scripts
β”‚   β”œβ”€β”€ manifest/          # Browser extension manifests (MV2/MV3)
β”‚   β”œβ”€β”€ images/            # Icons and images
β”‚   └── *.html             # Extension HTML pages
β”œβ”€β”€ ui/                    # React UI code (1,412 TSX, 1,292 JS)
β”‚   β”œβ”€β”€ components/        # Reusable React components
β”‚   β”œβ”€β”€ pages/             # Page-level components
β”‚   β”œβ”€β”€ ducks/             # Redux slices (state management)
β”‚   β”œβ”€β”€ hooks/             # Custom React hooks
β”‚   β”œβ”€β”€ selectors/         # Redux selectors
β”‚   └── store/             # Redux store configuration
β”œβ”€β”€ shared/                # Code shared between background and UI
β”‚   β”œβ”€β”€ constants/         # Shared constants (47 TS files)
β”‚   β”œβ”€β”€ lib/               # Shared utilities (122 TS files)
β”‚   β”œβ”€β”€ modules/           # Shared modules (45 TS files)
β”‚   └── types/             # TypeScript type definitions
β”œβ”€β”€ test/                  # Test files (586 TS, 79 JS)
β”‚   β”œβ”€β”€ e2e/               # End-to-end tests
β”‚   β”œβ”€β”€ integration/       # Integration tests
β”‚   └── *.test.*           # Unit tests (colocated with source)
β”œβ”€β”€ development/           # Build system and dev tools
β”‚   β”œβ”€β”€ build/             # Build scripts
β”‚   └── webpack/           # Webpack configuration
β”œβ”€β”€ docs/                  # Documentation (54 files)
└── .cursor/rules/         # AI agent coding guidelines
```

### Finding Specific Code

| What You Need                | Where to Look                                   |
| ---------------------------- | ----------------------------------------------- |
| Controllers (business logic) | `app/scripts/controllers/`                      |
| React Components             | `ui/components/` or `ui/pages/`                 |
| Redux State Management       | `ui/ducks/` (slices) and `ui/selectors/`        |
| Background Scripts           | `app/scripts/`                                  |
| Constants                    | `shared/constants/`                             |
| Utility Functions            | `shared/lib/` or `ui/helpers/`                  |
| Type Definitions             | `shared/types/` or `types/`                     |
| State Migrations             | `app/scripts/migrations/`                       |
| Build Configuration          | `development/build/` and `development/webpack/` |
| Extension Manifests          | `app/manifest/v2/` or `app/manifest/v3/`        |

### Architecture Patterns

**Controllers** (Background Scripts):

- Inherit from `BaseController` (from `@metamask/base-controller`)
- Manage wallet state and business logic
- Communicate via Messenger pattern (pub/sub)
- Use selectors for derived state (not getter methods)
- See `.cursor/rules/mms-controller-guidelines/RULE.md` for detailed patterns

**React Components** (UI):

- Functional components with hooks (no class components)
- Props destructured in function parameters
- Redux for global state, local state for UI-only data
- Performance optimizations: useMemo, useCallback, React.memo
- Unique IDs as keys (not array index for dynamic lists)
- Organized in component folders with tests, styles, and types
- See `.cursor/rules/mms-coding-guidelines/RULE.md` and `.cursor/rules/mms-perf-rendering/RULE.md`

**Testing**:

- Unit tests colocated with source files (`.test.ts`)
- Jest for unit tests, Playwright for E2E
- Test files organized with `describe` blocks by method/function
- See `.cursor/rules/mms-extension-testing/RULE.md` for testing patterns

### File Modification Patterns

When you modify certain files, you typically need to update related files:

**When modifying a Controller:**

```
app/scripts/controllers/foo/foo-controller.ts β†’ ALSO UPDATE:
β”œβ”€β”€ app/scripts/controllers/foo/foo-controller.test.ts (tests)
β”œβ”€β”€ app/scripts/controllers/foo/types.ts (if types changed)
└── app/scripts/metamask-controller.ts (if adding/removing controller)
```

**When modifying a React Component:**

```
ui/components/foo/foo.tsx β†’ ALSO UPDATE:
β”œβ”€β”€ ui/components/foo/foo.test.tsx (tests)
β”œβ”€β”€ ui/components/foo/foo.types.ts (if props changed)
β”œβ”€β”€ ui/components/foo/foo.stories.tsx (if props changed)
└── ui/components/foo/index.ts (if exports changed)
```

**When modifying Redux State (ducks):**

```
ui/ducks/foo/foo.ts β†’ ALSO UPDATE:
β”œβ”€β”€ ui/ducks/foo/foo.test.ts (tests)
β”œβ”€β”€ ui/selectors/foo.ts (selectors that depend on this state)
└── ui/components/*/foo-component.tsx (components using this state)
```

**When adding/removing dependencies:**

```
package.json β†’ MUST UPDATE:
β”œβ”€β”€ yarn.lock (run yarn install)
└── lavamoat/webpack/*/policy.json (run yarn lavamoat:auto)
```

**When modifying state shape:**

```
app/scripts/controllers/foo/foo-controller.ts β†’ MUST CREATE:
└── app/scripts/migrations/[next-number].ts (migration for state change)
```

---

## Working with Feature Flags

### What are Feature Flags?

Feature flags allow you to enable/disable features during development. They're defined in `.metamaskrc` and control which features are built into the extension.

### Available Feature Flags

Check `.metamaskrc.dist` for the current list of feature flags. Common ones:

- `MULTICHAIN` - Multi-chain support
- `BLOCKAID_PUBLIC_KEY` - Security features
- Various experimental features

### Using Feature Flags

**Method 1: Configure in `.metamaskrc`**

```bash
# Edit .metamaskrc
MULTICHAIN=1
OTHER_FEATURE=1

# Build with flags
yarn build:test
```

**Method 2: Pass as environment variable**

```bash
# Enable for single build
MULTICHAIN=1 yarn build:test
MULTICHAIN=1 yarn start:test

# Run E2E tests with feature enabled
MULTICHAIN=1 yarn build:test
yarn test:e2e:single test/e2e/tests/some-test.spec.js
```

### Remote Feature Flags

Override remote feature flags using `.manifest-overrides.json`:

```json
{
  "_flags": {
    "remoteFeatureFlags": {
      "testBooleanFlag": false
    }
  }
}
```

Set in `.metamaskrc`:

```
MANIFEST_OVERRIDES=.manifest-overrides.json
```

---

## LavaMoat Security System

### What is LavaMoat?

LavaMoat is a supply chain security tool that restricts what dependencies can do (file access, network access, etc.). It's enabled in production builds to protect users.

### When to Update LavaMoat Policies

Update policies whenever you:

- βœ… Add a new dependency
- βœ… Update an existing dependency
- βœ… Remove a dependency
- βœ… Change how code accesses Node.js APIs
- βœ… See "LavaMoat policy violation" errors

### How to Update Policies

**Automated (Recommended):**

```bash
# Regenerate the webpack LavaMoat policies
yarn lavamoat:auto

# Or use MetaMask bot (team members only):
# Comment on PR: @metamaskbot update-policies
```

**Manual:**

```bash
# Compile the webpack build tooling
yarn webpack:tsc

# Regenerate the webpack build tooling policy
yarn webpack:lavamoat:policy:build

# Regenerate the Firefox MV2 application policies
yarn webpack:lavamoat:policy:mv2

# Regenerate the Chrome MV3 application policies
yarn webpack:lavamoat:policy:mv3

# If policies still fail after regeneration:
rm -rf node_modules/ && yarn
# Then compile the webpack build tooling and rerun the affected policy command above.
```

### Common Policy Issues

- **Policy fails on macOS/Windows:** Platform-specific optional dependencies. Regenerate on the target platform.
- **Dynamic imports fail:** LavaMoat's static analysis may miss dynamic code. May need manual policy updates.
- **Can't build at all:** Use `yarn start` (LavaMoat off by default) for development, but fix before merging.

### Development Without LavaMoat

For faster iteration during development (LavaMoat is off by default):

```bash
yarn start       # Development build
yarn start:test  # Test build
```

**⚠️ Warning:** Always test with LavaMoat enabled before merging!

---

## Browser Compatibility

### Manifest V2 vs Manifest V3

| Feature           | MV2 (Firefox)         | MV3 (Chrome/Chromium) |
| ----------------- | --------------------- | --------------------- |
| **Build Flag**    | `ENABLE_MV3=false`    | Default               |
| **Start Command** | `yarn start:mv2`      | `yarn start`          |
| **Dist Command**  | `yarn dist:mv2`       | `yarn dist`           |
| **Background**    | Background page       | Service worker        |
| **Permissions**   | Broader access        | More restrictive      |
| **APIs**          | `browser.*` namespace | `chrome.*` namespace  |

### Building for Different Browsers

```bash
# Chrome / Edge / Brave (MV3)
yarn start                    # Development
yarn dist                     # Production

# Firefox (MV2)
yarn start:mv2                # Development
yarn dist:mv2                 # Production

# Test builds
yarn build:test               # Chrome MV3
yarn build:test:mv2           # Firefox MV2
```

### Browser-Specific Considerations

**Firefox:**

- Must use MV2 (Manifest V2)
- Use `webextension-polyfill` for cross-browser compatibility
- Test with `yarn test:e2e:firefox`

**Chrome/Chromium:**

- Uses MV3 (Manifest V3) by default
- Service worker limitations (no DOM access in background)
- Test with `yarn test:e2e:chrome`

**Both:**

- Code should use `browser.*` namespace (polyfilled for Chrome)
- Conditional logic for browser differences in `app/scripts/lib/util.js`

---

## Testing Strategy

### Unit Tests

**Location:** Colocated with source files (`.test.ts` or `.test.tsx`)

**Running:**

```bash
yarn test:unit              # All unit tests
yarn test:unit:watch        # Watch mode
yarn test:unit:coverage     # With coverage
```

**Key Principles:**

- Use Jest (not Mocha or Tape)
- Test through public interfaces (not private methods)
- Keep critical test data inline
- Use `describe` blocks to organize by method/function
- Never use "should" in test names (use present tense)

**Example:**

```typescript
describe('TokensController', () => {
  describe('addToken', () => {
    it('adds the token to state', () => {
      // Arrange, Act, Assert
    });

    it('throws error when token address is missing', () => {
      // Test error case
    });
  });
});
```

**Detailed Guidelines:** See `.cursor/rules/mms-extension-testing/RULE.md`

### E2E Tests

**Location:** `test/e2e/tests/`

**Running:**

```bash
# Must build test build first!
yarn build:test              # or yarn start:test

# Run E2E tests
yarn test:e2e:chrome         # All Chrome tests
yarn test:e2e:firefox        # All Firefox tests

# Single test with debug
yarn test:e2e:single test/e2e/tests/TEST_NAME.spec.js \
  --browser=chrome \
  --debug \
  --leave-running
```

**Options:**

- `--browser` - chrome, firefox, or all
- `--debug` - Verbose logging
- `--leave-running` - Keep browser open on failure
- `--retries` - Number of retries on failure
- `--update-snapshot` - Update snapshots

**E2E Best Practices:**

Find them in [](./test/e2e/AGENTS.md)

### Visual Verification (MetaMask CLI / Playwright)

When the user explicitly asks for visual verification of UI behavior (e.g., "verify this works", "confirm visually", "take screenshots", "click through onboarding/unlock/send flow"), you **MUST** use the MetaMask visual testing skill and `mm` cli tools instead of only reasoning about code.

**Load the skill:** `/metamask-visual-testing`

**Workflow:**

0. Build if needed (`yarn build:test`), then `mm launch` (this auto-starts the daemon)
1. **Query prior knowledge:** Run `mm knowledge-search "<flow>"` and `mm knowledge-sessions` to reuse previously discovered flows and avoid wasting tokens rediscovering known sequences.
2. Always call `mm describe-screen` before acting to discover targets
3. Use `mm click`/`mm type`/`mm wait-for` to drive the flow
4. Provide evidence via `mm screenshot` and/or final `mm describe-screen` output
5. Always end with `mm cleanup` (even on failure)

**If CLI is unavailable or denied:** Say so explicitly and explain what's missing. Do not claim you verified without actual tool output as evidence.

**Skill location:** `.claude/skills/mms-visual-testing/SKILL.md`

**MM CLI architecture docs:** `test/e2e/playwright/llm-workflow/README.md`

### Integration Tests

**Location:** `test/integration/`

**Running:**

```bash
yarn test:integration
yarn test:integration:coverage
```

**Coverage Goals:**

- Unit tests: > 80% coverage
- Critical paths: > 90% coverage
- E2E tests: Cover main user workflows

---

## State Migrations

### What are Migrations?

When MetaMask updates, the stored state format might change. Migrations transform old state to new format automatically.

### Creating a Migration

```bash
# Generate migration template
yarn generate:migration

# Creates: app/scripts/migrations/XXX.ts (next number)
```

### Migration Guidelines

1. **Always create migrations for state changes**
2. **Test migrations thoroughly** (old state β†’ new state)
3. **Handle missing data gracefully** (some users may have old/corrupted state)
4. **Never mutate input state** (return new state object)
5. **Include version number** in migration metadata

**Example Migration:**

```typescript
import { cloneDeep } from 'lodash';

const version = 123;

export default {
  version,
  async migrate(originalVersionedData: any) {
    const versionedData = cloneDeep(originalVersionedData);
    versionedData.meta.version = version;
    transformData(versionedData.data);
    return versionedData;
  },
};

function transformData(state: any): void {
  // Transform state.data
  if (state.PreferencesController) {
    state.PreferencesController.newProperty = 'defaultValue';
  }
}
```

---

## Pull Request Workflow

### Before Creating a PR

- [ ] All tests pass: `yarn test`
- [ ] Linting passes: `yarn lint`
- [ ] No console.logs or debug code
- [ ] Changes are covered by tests
- [ ] LavaMoat policies updated (if dependencies changed)

### Creating a PR

**Reference:** Follow the [PR template](https://github.com/MetaMask/metamask-extension/blob/main/.github/pull-request-template.md) when creating pull requests.

### Default Agent Commit/Push/PR Flow (When Requested)

Execute only the steps that correspond to what the user explicitly requested. Do not perform additional steps (e.g., do not push or open a PR if the user only asked to commit).

#### When asked to **commit**

1. Run `yarn lint:changed:fix` before creating the commit.
2. Stage only files relevant to the requested change.
3. Create a commit using Conventional Commits format: `<type>(optional-scope): <summary>`.

#### When asked to **push**

Complete all steps for **commit** above, then:

4. Push the current branch to `origin`.

#### When asked to **open a PR**

Complete all steps for **push** above, then:

5. Open a **draft** PR with:
   - A Conventional Commits PR title (normally matching the commit summary).
   - A PR body based on `.github/pull-request-template.md`.
   - Any non-applicable template section commented out as a full block, including the section heading, for example:

```markdown
<!--
## **Screenshots/Recordings**
### **Before**
### **After**
-->
```

6. Do not mark the PR as "Ready for review" unless explicitly requested.

**PR Title Format:**

- Clear and descriptive
- Will be used in squash commit message
- Example: "feat(networks): add token validation for custom networks"

**Description Section:**

- **Context:** What's the background?
- **Problem:** What needs to be fixed/added?
- **Solution:** How do your changes address it?
- Answer: "What is the reason for the change?" and "What is the improvement/solution?"

**Changelog Entry:**

- If End-User-Facing: Write a short user-facing description in past tense
  - Example: `CHANGELOG entry: Added a new tab for users to see their NFTs`
  - Example: `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
- If not End-User-Facing: Write `CHANGELOG entry: null` or label with `no-changelog`

**Related Issues:**

- List all related issues using `Fixes: #issue-number` format
- Link to related PRs if applicable

**Manual Testing Steps:**

- Provide numbered steps to test the changes
- Include specific pages/features to test
- Example:
  1. Go to this page...
  2. Click this button...
  3. Verify this behavior...

**Screenshots/Recordings:**

- **Before:** Screenshots/videos showing the previous state (for UI changes)
- **After:** Screenshots/videos showing the new state (for UI changes)
- Required for all UI changes

**Pre-merge Author Checklist:**

- [ ] Followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md)
- [ ] Completed the PR template to the best of ability
- [ ] Included tests if applicable
- [ ] Documented code using [JSDoc](https://jsdoc.app/) format if applicable
- [ ] Applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md))

**Additional PR Comments:**

- Call out non-obvious changes
- Explain complex logic inline
- Link to related issues/PRs

### During Review

- Respond to all feedback
- Link to commits that address feedback (e.g., "Fixed in abc1234")
- **Avoid rebasing after receiving comments** (makes review harder)
- Push new commits instead of amending
- If the Conventional Commit type in the PR's title is `chore`, please evaluate if `chore` is truly the best choice. We also have two custom types: `bump` (for package updates) and `release` (for tasks on a release branch and tasks that are all about getting a release ready).

### Before Merging

- [ ] All conversations resolved
- [ ] Required approvals received
- [ ] CI checks passing
- [ ] Review the squash commit message (auto-generated from PR)
- [ ] **Don't modify the commit title format** (must be: `Title (#number)`)

**Detailed Guidelines:** See `.cursor/rules/mms-pr-guidelines/RULE.md`

---

## Code Style & Standards

### General Principles

1. **TypeScript for all new code** (no new JavaScript files)
2. **Functional components with hooks** (no class components)
3. **Destructure props** in function parameters
4. **Small, focused functions** (single responsibility)
5. **Early returns** to reduce nesting
6. **DRY principle** (extract repeated code)

### Naming Conventions

```typescript
// Components: PascalCase
export const TokenListItem = () => {};

// Functions: camelCase
const handleInputChange = () => {};

// Custom hooks: use prefix
const useTokenBalance = () => {};

// Higher-order components: with prefix
const withAuth = (Component) => {};

// Controllers: PascalCase with Controller suffix
class TokensController extends BaseController {}
```

### Component Structure

```
component-name/
β”œβ”€β”€ component-name.tsx          # Main component
β”œβ”€β”€ component-name.types.ts     # TypeScript types
β”œβ”€β”€ component-name.test.tsx     # Unit tests
β”œβ”€β”€ component-name.stories.tsx  # Storybook stories
β”œβ”€β”€ component-name.scss         # Styles
β”œβ”€β”€ __snapshots__/              # Jest snapshots
β”œβ”€β”€ README.md                   # Component documentation
└── index.ts                    # Public exports
```

### React Best Practices

```typescript
// βœ… CORRECT: Functional component with destructured props and performance optimizations
interface TokenListProps {
  tokens: Token[];
  onSelect: (token: Token) => void;
}

export const TokenList = ({ tokens, onSelect }: TokenListProps) => {
  // Use hooks
  const [selected, setSelected] = useState<Token | null>(null);

  // Memoize expensive computations (sorting large arrays)
  const sortedTokens = useMemo(() =>
    [...tokens].sort((a, b) => a.symbol.localeCompare(b.symbol)),
    [tokens]  // Only re-sort when tokens array changes
  );

  // Memoize callbacks passed to children to prevent unnecessary re-renders
  const handleClick = useCallback((token: Token) => {
    setSelected(token);
    onSelect(token);
  }, [onSelect]);

  return (
    <div>
      {sortedTokens.map(token => (
        <TokenItem
          key={token.address}  // Use unique ID, not array index
          token={token}
          onClick={handleClick}  // Stable reference prevents child re-renders
        />
      ))}
    </div>
  );
};
```

**Performance Anti-Patterns to Avoid:**

```typescript
// ❌ WRONG: Using index as key for dynamic lists
{tokens.map((token, index) => (
  <TokenItem
    key={index}  // Don't use index as key for dynamic lists
    token={token}
  />
))}

// ❌ WRONG: No memoization for expensive operations
const sortedTokens = tokens.sort((a, b) => a.value - b.value);  // Runs on every render

// ❌ WRONG: Using useEffect for derived state
const [displayName, setDisplayName] = useState('');
useEffect(() => {
  setDisplayName(`${token.symbol} (${token.name})`);  // Should calculate during render
}, [token]);
```

**Detailed Guidelines:**

- General coding: `.cursor/rules/mms-coding-guidelines/RULE.md`
- Performance optimization:
  - `.cursor/rules/mms-perf-rendering/RULE.md` (rendering performance)
  - `.cursor/rules/mms-perf-hooks-effects/RULE.md` (hooks & effects)
  - `.cursor/rules/mms-perf-react-compiler/RULE.md` (React Compiler & anti-patterns)
  - `.cursor/rules/mms-perf-state-management/RULE.md` (Redux & state management)

---

## React Performance Optimization

### Critical Performance Rules

When writing React components, follow these performance best practices:

#### 1. Always Use Unique IDs as Keys

```typescript
// ❌ WRONG: Using index as key for dynamic list
{tokens.map((token, index) => (
  <TokenItem key={index} token={token} />  // BAD!
))}

// βœ… CORRECT: Use unique identifier
{tokens.map((token) => (
  <TokenItem key={token.address} token={token} />
))}
```

#### 2. Memoize Expensive Calculations

```typescript
// ❌ WRONG: Sorts on every render
const TokenList = ({ tokens }) => {
  const sortedTokens = tokens.sort((a, b) => b.balance - a.balance);  // BAD!
  return <div>{sortedTokens.map(...)}</div>;
};

// βœ… CORRECT: Memoize with useMemo
const TokenList = ({ tokens }) => {
  const sortedTokens = useMemo(() =>
    [...tokens].sort((a, b) => b.balance - a.balance),
    [tokens]
  );
  return <div>{sortedTokens.map(...)}</div>;
};
```

#### 3. Don't Use useEffect for Derived State

```typescript
// ❌ WRONG: Using effect for derived state
const TokenDisplay = ({ token }) => {
  const [displayName, setDisplayName] = useState('');

  useEffect(() => {
    setDisplayName(`${token.symbol} (${token.name})`);  // BAD!
  }, [token]);

  return <div>{displayName}</div>;
};

// βœ… CORRECT: Calculate during render
const TokenDisplay = ({ token }) => {
  const displayName = `${token.symbol} (${token.name})`;
  return <div>{displayName}</div>;
};
```

### Performance Checklist for Components

Before marking a component complete:

```
βœ“ List keys use unique IDs (token.address, tx.hash), not array index
βœ“ Expensive operations wrapped in useMemo (sorting, filtering)
βœ“ Callbacks passed to children wrapped in useCallback
βœ“ Static objects/styles defined as constants outside component
βœ“ No useEffect where render-time calculation would work
βœ“ Large lists (100+ items) consider virtualization (react-window)
```

### When to Optimize

- **DO optimize:** Frequently rendered components (list items, modals)
- **DO optimize:** Components with expensive calculations (sorting 100+ items)
- **DO optimize:** Deep component trees that re-render often
- **DON'T optimize:** Simple components that render quickly
- **DON'T optimize:** Components that rarely re-render

**Rule of thumb:** Profile first with React DevTools, then optimize what matters.

**See:**

- `.cursor/rules/mms-perf-rendering/RULE.md` - Rendering performance (keys, memoization, virtualization)
- `.cursor/rules/mms-perf-hooks-effects/RULE.md` - Hooks & effects optimization
- `.cursor/rules/mms-perf-react-compiler/RULE.md` - React Compiler considerations & anti-patterns
- `.cursor/rules/mms-perf-state-management/RULE.md` - Redux & state management optimization

---

## Error Handling for Agents

### When You Encounter a Build Error

```
1. Read the error message carefully
2. Check if it's a known issue in tables below
3. Apply the solution from the table
4. If not in table, check if it's a:
   - LavaMoat policy error β†’ Run `yarn lavamoat:auto`
   - TypeScript error β†’ Run `yarn lint:tsc`
   - Dependency error β†’ Run `yarn install`
5. If still failing, try nuclear option:
   rm -rf node_modules/ dist/ build/
   yarn install
   yarn lavamoat:auto
```

### When Tests Fail

```
1. IF test was passing before your changes:
   β†’ Your changes broke something
   β†’ Revert changes and understand what the test expects
   β†’ Fix code to match expected behavior

2. IF test expects old behavior but you're changing behavior:
   β†’ Update the test to match new expected behavior
   β†’ Document why behavior changed in test/PR description

3. IF E2E test fails:
   β†’ Check if you built test build: `yarn build:test`
   β†’ Check if test build is stale: delete dist/ and rebuild
   β†’ Run with --debug flag for more info
   β†’ Run with --leave-running to inspect browser state

4. IF snapshot test fails:
   β†’ Review the snapshot diff carefully
   β†’ IF change is intentional: `yarn test:unit -u`
   β†’ IF change is not intentional: fix your code
```

### When LavaMoat Policies Fail

```
1. ALWAYS run after dependency changes: `yarn lavamoat:auto`
2. IF auto-generation fails:
   β†’ Try: rm -rf node_modules/ && yarn && yarn lavamoat:auto
3. IF still fails:
   β†’ Check if on correct platform (macOS vs Linux)
   β†’ Platform-specific dependencies need regeneration on that platform
4. IF blocked during development:
   β†’ Temporarily use: yarn start (LavaMoat off by default)
   β†’ MUST fix before merging
```

### When You Get Circular Dependency Errors

```
1. Run: yarn circular-deps:check
2. Fix the circular dependency by:
   β†’ Moving shared code to a common location
   β†’ Using dependency injection
   β†’ Breaking circular imports
3. After fixing: yarn circular-deps:update
4. Commit the updated development/circular-deps.jsonc
```

---

## Troubleshooting

### Build Issues

| Problem                      | Solution                                                     |
| ---------------------------- | ------------------------------------------------------------ |
| `Module not found` errors    | Run `yarn install` again                                     |
| `Out of memory` during build | Increase Node heap: `NODE_OPTIONS=--max-old-space-size=4096` |
| LavaMoat policy errors       | Run `yarn lavamoat:auto`                                     |
| Webpack cache issues         | Run `yarn webpack:clearcache`                                |
| Stale build artifacts        | Delete `dist/` and `build/` directories                      |

### Test Issues

| Problem                 | Solution                                          |
| ----------------------- | ------------------------------------------------- |
| E2E tests fail to start | Build test build first: `yarn build:test`         |
| Tests hang indefinitely | Check if port 8545 (Anvil) is available           |
| Snapshot tests fail     | Update snapshots: `yarn test:unit -u`             |
| Browser not launching   | Check if browser is installed and in PATH         |
| Random E2E failures     | Use `--retries` flag or check for race conditions |

### Development Issues

| Problem                | Solution                                               |
| ---------------------- | ------------------------------------------------------ |
| Extension won't load   | Check browser console for errors                       |
| Hot reload not working | Restart `yarn start`                                   |
| Changes not appearing  | Hard refresh extension (chrome://extensions)           |
| State corrupted        | Clear extension data in browser                        |
| Port already in use    | Kill process on port: `lsof -ti:PORT \| xargs kill -9` |

### Dependency Issues

| Problem                  | Solution                                        |
| ------------------------ | ----------------------------------------------- |
| Yarn version mismatch    | Run `corepack enable`                           |
| Package install fails    | Clear cache: `yarn cache clean && yarn install` |
| Peer dependency warnings | Check if packages are compatible                |
| Allow-scripts fails      | Run `yarn allow-scripts auto`                   |

---

## Agent Pre-Completion Checklist

Before completing your task, verify you've done ALL of the following:

### Code Quality Checks

```bash
# 1. Run linter and auto-fix
yarn lint:changed:fix

# 2. Run TypeScript type checking
yarn lint:tsc

# 3. Check for circular dependencies
yarn circular-deps:check

# 4. Verify no console.log or debug code remains
# grep -r "console.log" in modified files
```

### Testing Checks

```bash
# 1. Run unit tests for modified files
yarn test:unit path/to/modified-file.test.ts

# 2. If you modified a controller, run controller tests
yarn test:unit app/scripts/controllers/

# 3. If you modified UI components, run component tests
yarn test:unit ui/components/

# 4. If behavior changed, ensure tests are updated
# Tests must reflect new expected behavior
```

### Build Checks

```bash
# 1. Verify dev build works
yarn start
# (Let it build, check for errors, then Ctrl+C)

# 2. If E2E-related, verify test build works
yarn build:test
# (Check for build errors)
```

### Dependency Checks (ONLY if you modified dependencies)

```bash
# 1. Deduplicate lockfile
yarn lint:lockfile:dedupe:fix

# 2. Update allow-scripts
yarn allow-scripts auto

# 3. Update LavaMoat policies
yarn lavamoat:auto

# 4. Verify all policy files a