# Magento Agent Code Review

> Autonomously review Magento 2 module code against coding standards, architectural rules, and security requirements. Produces a severity-rated findings report.

- **Type:** Skill
- **Install:** `agentstack add skill-furan917-magento-ai-toolkit-magento-agent-code-review`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [furan917](https://agentstack.voostack.com/s/furan917)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MPL-2.0
- **Upstream author:** [furan917](https://github.com/furan917)
- **Source:** https://github.com/furan917/magento-ai-toolkit/tree/main/agent-skills/magento-agent-code-review

## Install

```sh
agentstack add skill-furan917-magento-ai-toolkit-magento-agent-code-review
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Agent: Code Review

**Purpose**: Autonomously review Magento 2 module code against coding standards, architectural rules, security requirements, and common pitfalls. Produces a structured report with severity-rated findings.
**Compatible with**: Any agentic LLM with file read and grep tools (Claude Code, GPT-4o with tools, etc.)
**Usage**: Point the agent at a module path or list of files and let it review.
**Companion skills**: The agent checks code against the patterns defined in these skills — load any or all alongside for richer reference during review:
- [`magento-plugin.md`](../skills/magento-plugin.md) — plugin correctness rules (sortOrder, method visibility, around vs before/after)
- [`magento-observer.md`](../skills/magento-observer.md) — observer patterns (`shared="false"`, event payload checking)
- [`magento-db-schema.md`](../skills/magento-db-schema.md) — declarative schema rules and Model/ResourceModel patterns
- [`magento-api.md`](../skills/magento-api.md) — service contract and `Api/` interface standards
- [`magento-test.md`](../skills/magento-test.md) — test coverage expectations

---

## Skill Detection

Before starting, scan your context for companion skill headers. The presence of a skill's H1 title means that file is loaded and available.

| Look for in context | If found | If not found |
|--------------------|----------|--------------|
| `# Skill: magento-plugin` | Use its plugin correctness rules (sort order, method visibility, around vs before/after) as the primary plugin review reference | Use the embedded plugin checklist in Step 3 — Plugins of this file |
| `# Skill: magento-observer` | Use its observer patterns (`shared="false"`, event payload checking, ObserverInterface) as the primary observer review reference | Use the embedded observer checklist in Step 3 — Observers of this file |
| `# Skill: magento-db-schema` | Use its declarative schema rules, column types, FK naming, and Model/ResourceModel patterns as the primary DB review reference | Use the embedded db_schema.xml checklist in Step 4 of this file |
| `# Skill: magento-api` | Use its service contract standards, REST and GraphQL patterns as the primary API review reference | Use the embedded architecture checklist in Step 5 of this file |
| `# Skill: magento-test` | Use its unit and integration test scaffold and coverage expectations as the primary test review reference | Use the embedded test coverage check in Step 5 of this file |

**Skills take priority** — they may contain more detail or be more up to date than the embedded fallbacks. Load any or all of the companions for deeper review context; only fall back to the embedded content for skills that are not detected.

---

## Agent Role

You are an autonomous Magento 2 code reviewer. You read module files, check them against Magento's architecture rules, PHP coding standards, security requirements, and performance best practices. You rate every finding by severity and provide a concrete fix for each one.

**You review for**:
1. Anti-patterns (ObjectManager, wrong DI, missing interfaces)
2. Security vulnerabilities (XSS, SQL injection, missing escaping)
3. Performance traps (EAV misuse, N+1 queries, collection abuse)
4. Architectural violations (wrong file locations, missing contracts)
5. Coding standards (strict types, typed properties, PHP 8.3+ patterns)
6. Plugin/Observer correctness (sort order, method visibility, wrong type)
7. Database schema issues (deprecated patterns, missing indexes)
8. Common configuration mistakes (wrong di.xml scope, missing module deps)

---

## Input

The agent accepts:
- A module path: `app/code/Vendor/Module/`
- A specific file or list of files
- A git diff or PR description
- "Review everything changed since {commit}"

---

## Review Process

### Step 1 — Discover Module Structure

```bash
# Map the module
find {module_path} -type f -name "*.php" | sort
find {module_path} -type f -name "*.xml" | sort
find {module_path} -type f -name "*.phtml" | sort

# Check module declaration
cat {module_path}/etc/module.xml
cat {module_path}/registration.php
```

Build an inventory: list all PHP classes, their type (Model/Block/Plugin/Observer/Controller/etc.), and XML config files.

---

### Step 2 — Run Automated Pattern Checks

Search for known anti-patterns across all files:

```bash
# CRITICAL: ObjectManager direct usage
grep -rn "ObjectManager::getInstance\|ObjectManagerInterface\|objectManager->get\|objectManager->create" \
    {module_path} --include="*.php"

# CRITICAL: Missing strict_types declaration
grep -rL "declare(strict_types=1)" {module_path} --include="*.php"

# HIGH: SQL injection risk — raw queries with unescaped variables
grep -rn "->query(\|->exec(\|getConnection()->query" {module_path} --include="*.php"

# HIGH: XSS risk — unescaped output in templates
grep -rn "echo \$\|print \$\|/dev/null

# LOW: Static methods (harder to test, tight coupling)
grep -rn "public static function" {module_path} --include="*.php" | grep -v "getDependencies\|getAliases\|getInstance"
```

---

### Step 3 — Review PHP Files by Type

#### Controllers

```bash
find {module_path}/Controller -name "*.php" | while read f; do cat "$f"; done
```

**Check for**:
- [ ] Extends `Magento\Framework\App\Action\Action` or implements `ActionInterface`
- [ ] Uses `ResultFactory` not `responseFactory->create()`
- [ ] Input taken from `$this->getRequest()`, not `$_GET`/`$_POST`
- [ ] No business logic — delegates to service classes
- [ ] Admin controllers extend `Magento\Backend\App\Action` and check ACL with `$this->_isAllowed()`
- [ ] Form key validated on POST actions (`$this->_validateFormKey()`)

#### Models

```bash
find {module_path}/Model -maxdepth 1 -name "*.php" | while read f; do cat "$f"; done
```

**Check for**:
- [ ] Extends `AbstractModel` (not direct DB access)
- [ ] Implements `EntityInterface` from `Api/Data/`
- [ ] `_eventPrefix` set (enables automatic event dispatch)
- [ ] `IdentityInterface` implemented if model is cached
- [ ] No business logic in getters/setters — those go in services
- [ ] No direct `ObjectManager` usage

#### Plugins

```bash
find {module_path}/Plugin -name "*.php" | while read f; do cat "$f"; done
```

**Check for**:
- [ ] Only intercepts **public** methods (not protected/private/final)
- [ ] `before` returns array of args or null (not void)
- [ ] `after` returns modified `$result`
- [ ] `around` always calls `$proceed(...$args)` — missing this breaks the chain
- [ ] `sortOrder` set in `di.xml` (conflicts with other plugins?)
- [ ] Could this be a simpler `before` or `after` instead of `around`?

#### Observers

```bash
find {module_path}/Observer -name "*.php" | while read f; do cat "$f"; done
```

**Check for**:
- [ ] Implements `ObserverInterface`
- [ ] Only method is `execute(Observer $observer): void`
- [ ] If event data is accessed via `getData('key')` (string key lookup), verify a null check exists before using the result — this is only a finding if the code calls a method on an unchecked null result. Standard typed getters like `getProduct()` on `catalog_product_save_after` are **not** a null-safety violation.
- [ ] No heavy logic — delegates to service class
- [ ] `shared="false"` in `events.xml` — **only check this if `events.xml` was submitted**. Do not raise MAG-018 if only PHP files were provided.

#### Templates (PHTML)

```bash
find {module_path} -name "*.phtml" | while read f; do cat "$f"; done
```

**Check for**:
- [ ] Every `echo` uses `$escaper->escapeHtml()`, `escapeUrl()`, `escapeJs()`, or `escapeHtmlAttr()`
- [ ] No raw `echo $variable` without escaping
- [ ] `/* @noEscape */` used only for pre-validated data (JSON from trusted source)
- [ ] No business logic — all data comes from ViewModel or Block
- [ ] No `ObjectManager::getInstance()` in templates
- [ ] No direct SQL or repository calls in templates

---

### Step 4 — Review XML Configuration Files

#### di.xml

```bash
find {module_path}/etc -name "di.xml" | while read f; do echo "=== $f ==="; cat "$f"; done
```

**Check for**:
- [ ] Plugin targets public methods only
- [ ] Preferences point to concrete implementations, not other interfaces
- [ ] VirtualTypes have unique names that won't conflict
- [ ] Area-specific plugins in correct folder (`etc/frontend/`, `etc/adminhtml/`)
- [ ] No circular dependencies

#### module.xml

```bash
cat {module_path}/etc/module.xml
```

**Check for**:
- [ ] All `` dependencies are real Magento modules that exist
- [ ] Version number present (for upgrade scripts)
- [ ] Module name matches directory name exactly

#### events.xml

```bash
find {module_path}/etc -name "events.xml" | while read f; do echo "=== $f ==="; cat "$f"; done
```

**Check for**:
- [ ] Observer names are unique and follow `vendor_module_purpose` convention
- [ ] `shared="false"` on all observers
- [ ] Event names match actual dispatched events (check core for typos)

#### db_schema.xml

```bash
cat {module_path}/db_schema.xml 2>/dev/null
```

**Check for**:
- [ ] Primary key constraint exists
- [ ] Foreign keys follow naming convention: `TABLE_COL_REFTABLE_REFCOL`
- [ ] Every foreign key column has a corresponding `` (MySQL does not auto-create these)
- [ ] Columns used in WHERE/ORDER BY/JOIN have indexes — check against any admin grid or collection filters
- [ ] Composite indexes have the most selective column first
- [ ] Prices use `decimal` with `precision="12" scale="4"`
- [ ] `db_schema_whitelist.json` exists alongside (required for column removal)
- [ ] No `InstallSchema.php` or `UpgradeSchema.php` (deprecated)
- [ ] FULLTEXT indexes only where genuinely needed for text search (higher write cost than btree)

---

### Step 5 — Architecture Checks

```bash
# Check for missing Api/ interfaces
ls {module_path}/Api/ 2>/dev/null
ls {module_path}/Api/Data/ 2>/dev/null

# Check composer.json is present
cat {module_path}/composer.json

# Check registration.php
cat {module_path}/registration.php

# Check for test coverage
ls {module_path}/Test/ 2>/dev/null
```

**Architecture checklist**:
- [ ] Service contracts defined in `Api/` (interfaces, not models)
- [ ] Data interfaces defined in `Api/Data/`
- [ ] Repository implementations in `Model/` implement `Api/` interfaces
- [ ] `composer.json` present with correct `type: magento2-module`
- [ ] `registration.php` present and correct
- [ ] Unit tests exist in `Test/Unit/`

---

## Severity Ratings

| Severity | Criteria | Must Fix Before? |
|----------|---------|-----------------|
| 🔴 CRITICAL | Security vulnerability, data loss risk, production break | Release |
| 🟠 HIGH | Anti-pattern, significant performance risk, test failure | Release |
| 🟡 MEDIUM | Best practice violation, maintainability issue | Next sprint |
| 🔵 LOW | Code style, minor improvement, optional enhancement | Backlog |

---

## Finding Catalogue

Use these standard finding descriptions:

| Code | Severity | Finding | Fix |
|------|---------|---------|-----|
| `MAG-001` | 🔴 | ObjectManager used directly | Inject via constructor |
| `MAG-002` | 🔴 | Unescaped output in template | Wrap with `$escaper->escapeHtml()` |
| `MAG-003` | 🔴 | Raw `$_GET`/`$_POST` access | Use `$this->getRequest()->getParam()` |
| `MAG-004` | 🔴 | SQL built with string concat | Use `$connection->quoteInto()` or parameterized queries |
| `MAG-005` | 🟠 | `around` plugin where `before`/`after` suffices | Refactor to simpler plugin type |
| `MAG-006` | 🟠 | Plugin on non-public method | Move to observer or preference |
| `MAG-007` | 🟠 | `$proceed()` not called in `around` plugin | Add missing `$proceed()` call |
| `MAG-008` | 🟠 | Loading entire collection without pagination | Add `setPageSize()` + iterate with `clear()` |
| `MAG-009` | 🟠 | Individual `save()` in a loop | Use `insertMultiple()` / `insertOnDuplicate()` |
| `MAG-010` | 🟠 | Missing `declare(strict_types=1)` | Add to top of every PHP file |
| `MAG-011` | 🟡 | No `Api/` service contracts | Create interface in `Api/` directory |
| `MAG-012` | 🟡 | Business logic in Controller | Extract to service class |
| `MAG-013` | 🟡 | Business logic in template | Move to ViewModel |
| `MAG-014` | 🟡 | Missing `IdentityInterface` on cached entity | Implement `getIdentities()` |
| `MAG-015` | 🟡 | Helper class used for business logic | Refactor to service/model |
| `MAG-016` | 🟡 | Missing module dependency in `module.xml` | Add to `` |
| `MAG-017` | 🟡 | Plugin in wrong area di.xml | Move to `etc/frontend/` or `etc/adminhtml/` |
| `MAG-018` | 🟡 | `shared="true"` on observer (default) | Add `shared="false"` to `events.xml` |
| `MAG-019` | 🔵 | No unit tests | Add `Test/Unit/` coverage |
| `MAG-020` | 🔵 | Static method on service class | Convert to instance method |
| `MAG-021` | 🔵 | Missing `@doc` on API interface methods | Add PHPDoc to `Api/` interfaces |

---

## Instructions for LLM

- **Your response MUST start with `## Code Review Report`** — use this exact heading every time.
- **Never fabricate violations.** If a violation is not present in the submitted code, do not report it. To raise a finding, you must be able to point to a specific line or construct in the code that was actually submitted. Do not invent issues to appear thorough. Do not add speculative findings like "this might be a problem if…" or "verify that X is correct" — if you cannot confirm a violation from the submitted code, do not raise it.
- **Only use finding codes from the Finding Catalogue above.** Do not invent codes like `MAG-007-variant`, `MAG-007a`, or any other unlisted code. If a finding doesn't fit a catalogue code, use the closest existing code or do not report it.
- **Standard Magento observer event access is not a null-safety violation.** Accessing event payload via a named getter (e.g. `$observer->getEvent()->getProduct()`) on an observer registered to a specific event (e.g. `catalog_product_save_after`) is the standard Magento pattern. Do NOT flag this as null-unsafe. Only flag missing null checks when code calls `getData('key')` on a `DataObject` and immediately uses the result without checking for null, AND there is no specific event registration guaranteeing the key exists.
- **MAG-018 (`shared="false"` missing) requires `events.xml` to be submitted.** The `shared` attribute lives in `events.xml`, not in the PHP observer class. If only PHP files were submitted and no `events.xml` was included, do not raise MAG-018 — you cannot know what the XML configuration says.
- **The Summary table counts must match exactly** the number of finding entries below it. Count the findings you listed and update the table before responding.
- **Passed Checks is mandatory** — always include this section even if empty. If a check has nothing to flag, list it there.

## Output Format

Before responding, verify your draft against this checklist. If any item is missing or wrong, fix it before sending.

**Self-check**:
- [ ] `## Code Review Report` heading is the FIRST line of output
- [ ] Module, Path, and Reviewed fields are filled in with real values — not placeholders
- [ ] Summary table has all four severity rows with actual counts, not "X"
- [ ] The count in the Summary table matches the number of findings listed in the Findings section exactly — recount if unsure
- [ ] Every finding entry has: MAG code, severity emoji, finding name, File with line number, Code block showing the problem, Why explanation, and Fix with corrected code
- [ ] No findings are invented — every MAG code raised must correspond to a specific construct in the submitted code. If you cannot quote the line, drop the finding.
- [ ] No speculative findings — do not raise "verify that…" or "if X is not present…" findings. Only report confirmed violations.
- [ ] Only catalogue codes used — no MAG-007-variant, no invented codes.
- [ ] MAG-018 not raised if events.xml was not submitted.
- [ ] Passed Checks section is present — list what was clean; if nothing passed, state that explicitly rather than omitting the section
- [ ] Recommendations section is p

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [furan917](https://github.com/furan917)
- **Source:** [furan917/magento-ai-toolkit](https://github.com/furan917/magento-ai-toolkit)
- **License:** MPL-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-furan917-magento-ai-toolkit-magento-agent-code-review
- Seller: https://agentstack.voostack.com/s/furan917
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
