magento-agent-code-review

star 27

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

furan917 By furan917 schedule Updated 2/22/2026

name: magento-agent-code-review description: "Autonomously review Magento 2 module code against coding standards, architectural rules, and security requirements. Produces a severity-rated findings report." license: MIT metadata: author: mage-os

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:


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

# 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:

# 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 \$\|<?= \$" {module_path} --include="*.phtml" | grep -v "escaper\|escapeHtml\|escapeUrl\|escapeJs\|@noEscape"

# HIGH: Direct $_GET/$_POST access (bypass Magento request layer)
grep -rn "\$_GET\|\$_POST\|\$_REQUEST\|\$_SERVER\|\$_COOKIE" {module_path} --include="*.php"

# MEDIUM: Missing return types and typed properties
grep -rn "public function " {module_path} --include="*.php" | grep -v ": " | head -20

# MEDIUM: Around plugins (performance concern)
grep -rn "function around" {module_path} --include="*.php"

# MEDIUM: Deprecated ObjectManager in templates
grep -rn "ObjectManager\|Mage::" {module_path} --include="*.phtml"

# LOW: Helper class usage (should use Services/ViewModels instead)
find {module_path}/Helper -name "*.php" 2>/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

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

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

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

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.xmlonly check this if events.xml was submitted. Do not raise MAG-018 if only PHP files were provided.

Templates (PHTML)

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

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

cat {module_path}/etc/module.xml

Check for:

  • All <sequence> dependencies are real Magento modules that exist
  • Version number present (for upgrade scripts)
  • Module name matches directory name exactly

events.xml

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

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 <index> (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

# 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 <sequence>
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 present with at least one prioritised action
## Code Review Report
**Module**: Vendor_Module
**Path**: app/code/Vendor/Module/
**Reviewed**: [timestamp]

---

### Summary
| Severity | Count |
|----------|-------|
| 🔴 CRITICAL | X |
| 🟠 HIGH     | X |
| 🟡 MEDIUM   | X |
| 🔵 LOW      | X |

---

### Findings

#### [MAG-001] 🔴 CRITICAL — ObjectManager Direct Usage
**File**: `Model/Service.php:42`
**Code**:
\`\`\`php
$product = ObjectManager::getInstance()->create(Product::class);
\`\`\`
**Why**: Bypasses DI, untestable, causes unpredictable behaviour in plugins/proxies.
**Fix**:
\`\`\`php
// Inject ProductFactory via constructor
public function __construct(
    private readonly ProductFactory $productFactory
) {}
// Then use:
$product = $this->productFactory->create();
\`\`\`

---

#### [MAG-002] 🔴 CRITICAL — Unescaped Output in Template
**File**: `view/frontend/templates/custom.phtml:15`
...

---

### Passed Checks ✅
- strict_types declared in all files
- No $_GET/$_POST direct access
- Api/ interfaces present
- db_schema.xml used (no deprecated InstallSchema)
- Observer uses shared="false"

---

### Recommendations
1. [Highest priority action]
2. [Second priority]
3. [Third priority]
Install via CLI
npx skills add https://github.com/furan917/magento-ai-toolkit --skill magento-agent-code-review
Repository Details
star Stars 27
call_split Forks 2
navigation Branch main
article Path SKILL.md
More from Creator