erpnext-code-validator
npx machina-cli add skill OpenAEC-Foundation/ERPNext_Anthropic_Claude_Development_Skill_Package/erpnext-code-validator --openclawERPNext Code Validator Agent
This agent validates ERPNext/Frappe code against established patterns, common pitfalls, and version compatibility requirements.
Purpose: Catch errors BEFORE deployment, not after
When to Use This Agent
┌─────────────────────────────────────────────────────────────────────┐
│ CODE VALIDATION TRIGGERS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ► Code has been generated and needs review │
│ "Check this Server Script before I save it" │
│ └── USE THIS AGENT │
│ │
│ ► Code is causing errors │
│ "Why isn't this working?" │
│ └── USE THIS AGENT │
│ │
│ ► Pre-deployment validation │
│ "Is this production-ready?" │
│ └── USE THIS AGENT │
│ │
│ ► Code review for best practices │
│ "Can this be improved?" │
│ └── USE THIS AGENT │
│ │
└─────────────────────────────────────────────────────────────────────┘
Validation Workflow
┌─────────────────────────────────────────────────────────────────────┐
│ CODE VALIDATOR WORKFLOW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ STEP 1: IDENTIFY CODE TYPE │
│ ══════════════════════════ │
│ • Client Script (JavaScript) │
│ • Server Script (Python sandbox) │
│ • Controller (Python full) │
│ • hooks.py configuration │
│ • Jinja template │
│ • Whitelisted method │
│ │
│ STEP 2: RUN TYPE-SPECIFIC CHECKS │
│ ═════════════════════════════════ │
│ • Apply checklist for identified code type │
│ • Check syntax patterns │
│ • Verify API usage │
│ │
│ STEP 3: CHECK UNIVERSAL RULES │
│ ══════════════════════════════ │
│ • Error handling present │
│ • User feedback appropriate │
│ • Security considerations │
│ • Performance implications │
│ │
│ STEP 4: VERIFY VERSION COMPATIBILITY │
│ ════════════════════════════════════ │
│ • v14/v15/v16 specific features │
│ • Deprecated patterns │
│ • Version-specific behaviors │
│ │
│ STEP 5: GENERATE VALIDATION REPORT │
│ ══════════════════════════════════ │
│ • Critical errors (must fix) │
│ • Warnings (should fix) │
│ • Suggestions (nice to have) │
│ • Corrected code (if errors found) │
│ │
└─────────────────────────────────────────────────────────────────────┘
→ See references/workflow.md for detailed validation steps.
Critical Checks by Code Type
Server Script Checks
┌─────────────────────────────────────────────────────────────────────┐
│ ⚠️ SERVER SCRIPT CRITICAL CHECKS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [FATAL] Import statements │
│ ═══════════════════════════ │
│ ❌ import json → Use frappe.parse_json() │
│ ❌ from frappe.utils import X → Use frappe.utils.X() │
│ ❌ import requests → IMPOSSIBLE in Server Script │
│ │
│ [FATAL] Undefined variables │
│ ════════════════════════════ │
│ ❌ self.field → Use doc.field │
│ ❌ document.field → Use doc.field │
│ │
│ [FATAL] Wrong event handling │
│ ═══════════════════════════════ │
│ ❌ try/except for validation → Just frappe.throw() │
│ │
│ [ERROR] Event name mismatch │
│ ═══════════════════════════ │
│ ❌ Event "Before Save" code in "After Save" script │
│ │
│ [WARNING] Missing validation │
│ ═══════════════════════════════ │
│ ⚠️ No null/empty checks before operations │
│ │
└─────────────────────────────────────────────────────────────────────┘
Client Script Checks
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENT SCRIPT CRITICAL CHECKS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [FATAL] Wrong API usage │
│ ═════════════════════════ │
│ ❌ frappe.db.get_value() → Server-side only! │
│ ❌ frappe.get_doc() → Server-side only! │
│ ✓ frappe.call() for server data │
│ │
│ [FATAL] Missing async handling │
│ ══════════════════════════════ │
│ ❌ let result = frappe.call() → Returns undefined │
│ ✓ frappe.call({callback: fn}) → Use callback │
│ ✓ await frappe.call({async:false}) → Or async/await │
│ │
│ [ERROR] Field refresh issues │
│ ════════════════════════════ │
│ ❌ frm.set_value() without refresh │
│ ✓ frm.set_value() then frm.refresh_field() │
│ │
│ [WARNING] Form state checks │
│ ═══════════════════════════ │
│ ⚠️ Not checking frm.doc.__islocal for new docs │
│ ⚠️ Not checking frm.doc.docstatus for submitted docs │
│ │
└─────────────────────────────────────────────────────────────────────┘
Controller Checks
┌─────────────────────────────────────────────────────────────────────┐
│ CONTROLLER CRITICAL CHECKS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [FATAL] Wrong lifecycle usage │
│ ═════════════════════════════ │
│ ❌ Modifying self.field in on_update → Changes NOT saved! │
│ ✓ Use frappe.db.set_value() in on_update │
│ │
│ [FATAL] Missing super() call │
│ ════════════════════════════ │
│ ❌ def validate(self): pass → Breaks parent validation │
│ ✓ def validate(self): super().validate() │
│ │
│ [ERROR] Transaction assumptions │
│ ═══════════════════════════════ │
│ ❌ Assuming rollback on error in on_update │
│ (only validate and before_* roll back on error) │
│ │
│ [ERROR] Circular save │
│ ══════════════════════ │
│ ❌ self.save() inside lifecycle hooks │
│ ❌ doc.save() for same document in hooks │
│ │
└─────────────────────────────────────────────────────────────────────┘
→ See references/checklists.md for complete checklists.
Validation Report Format
## Code Validation Report
### Code Type: [Server Script / Client Script / Controller / etc.]
### Target DocType: [DocType name]
### Event/Trigger: [Event name]
---
### 🔴 CRITICAL ERRORS (Must Fix)
| Line | Issue | Fix |
|------|-------|-----|
| 3 | Import statement in Server Script | Use frappe.utils.X() directly |
### 🟡 WARNINGS (Should Fix)
| Line | Issue | Recommendation |
|------|-------|----------------|
| 12 | No null check before .lower() | Add: if value: value.lower() |
### 🔵 SUGGESTIONS (Nice to Have)
| Line | Suggestion |
|------|------------|
| 8 | Consider using frappe.db.get_value for single field |
---
### Corrected Code
```python
# [Corrected version with all critical errors fixed]
Version Compatibility
| Version | Status |
|---|---|
| v14 | ✅ Compatible |
| v15 | ✅ Compatible |
| v16 | ✅ Compatible |
## Universal Validation Rules
These apply to ALL code types:
### Security Checks
| Check | Severity | Description |
|-------|----------|-------------|
| SQL Injection | CRITICAL | Raw user input in SQL queries |
| Permission bypass | CRITICAL | Missing permission checks before operations |
| XSS vulnerability | HIGH | Unescaped user input in HTML |
| Sensitive data exposure | HIGH | Logging passwords/tokens |
### Error Handling Checks
| Check | Severity | Description |
|-------|----------|-------------|
| Silent failures | HIGH | Catching exceptions without handling |
| Missing user feedback | MEDIUM | Errors not communicated to user |
| Generic error messages | LOW | "An error occurred" without details |
### Performance Checks
| Check | Severity | Description |
|-------|----------|-------------|
| Query in loop | HIGH | frappe.db.* inside for loop |
| Unbounded query | MEDIUM | SELECT without LIMIT |
| Unnecessary get_doc | LOW | get_doc when get_value suffices |
→ See [references/examples.md](references/examples.md) for validation examples.
## Version-Specific Validations
### v16 Features (Fail on v14/v15)
```python
# These ONLY work on v16+
extend_doctype_class = {} # hooks.py - v16 only
naming_rule = "UUID" # DocType - v16 only
pdf_renderer = "chrome" # Print Format - v16 only
Deprecated Patterns (Warn)
# DEPRECATED - still works but should update
frappe.bean() # Use frappe.get_doc()
frappe.msgprint(raise_exception=True) # Use frappe.throw()
job_name parameter # Use job_id (v15+)
Version-Specific Behaviors
| Behavior | v14 | v15/v16 |
|---|---|---|
| Scheduler tick | 240s | 60s |
| Background job dedup | job_name | job_id |
Quick Validation Commands
Server Script Quick Check
- ❌ Any
importstatements? → FATAL - ❌ Any
self.references? → FATAL (usedoc.) - ❌ Any
try/except? → WARNING (usually wrong) - ✅ Uses
frappe.throw()for validation errors? → GOOD - ✅ Uses
doc.fieldfor document access? → GOOD
Client Script Quick Check
- ❌ Any
frappe.db.*calls? → FATAL (server-side only) - ❌ Any
frappe.get_doc()calls? → FATAL (server-side only) - ❌
frappe.call()without callback? → FATAL (async issue) - ✅ Uses
frm.doc.fieldfor field access? → GOOD - ✅ Uses
frm.refresh_field()after changes? → GOOD
Controller Quick Check
- ❌ Modifying
self.*inon_update? → ERROR (won't save) - ❌ Missing
super().method()calls? → WARNING - ❌
self.save()in lifecycle hook? → FATAL (circular) - ✅ Imports at top of file? → GOOD (controllers allow imports)
- ✅ Error handling with try/except? → GOOD (controllers allow this)
Integration with Other Skills
This validator uses knowledge from:
| Skill | What It Provides |
|---|---|
erpnext-syntax-* | Correct syntax patterns |
erpnext-impl-* | Correct implementation patterns |
erpnext-errors-* | Error handling patterns |
erpnext-database | Query patterns and pitfalls |
erpnext-permissions | Permission check patterns |
erpnext-api-patterns | API response patterns |
Validation Depth Levels
| Level | Checks | Use When |
|---|---|---|
| Quick | Fatal errors only | Initial scan |
| Standard | + Warnings | Pre-deployment |
| Deep | + Suggestions + Optimization | Production review |
Default: Standard level for most validations.
Source
git clone https://github.com/OpenAEC-Foundation/ERPNext_Anthropic_Claude_Development_Skill_Package/blob/main/skills/source/agents/erpnext-code-validator/SKILL.mdView on GitHub Overview
An intelligent agent that validates ERPNext/Frappe code against established patterns, common pitfalls, and version compatibility requirements. It helps catch errors before deployment, improving code quality and reducing production issues.
How This Skill Works
The agent first identifies the code type (Client Script JavaScript, Server Script Python sandbox, Controller Python full, hooks.py, Jinja template, or whitelisted method). It then runs type-specific checks, followed by universal rules on error handling, security, and performance, surfacing actionable fixes.
When to Use It
- Code has been generated and needs review
- Code is causing errors or not behaving as expected
- Pre-deployment validation for production readiness
- Code review focused on best practices and maintainability
- Review config and integration code (hooks.py, controllers) for compatibility
Quick Start
- Step 1: Identify the code type (Client Script, Server Script, Controller, hooks.py, Jinja, or whitelisted method)
- Step 2: Run the appropriate type-specific checks and collect findings
- Step 3: Review universal rules and apply fixes before deployment
Best Practices
- Follow the type-specific checklists for Client Script, Server Script, or other code types
- Verify syntax patterns and correct API usage for ERPNext/Frappe
- Ensure robust error handling and clear, user-facing feedback
- Assess security implications and safe data handling
- Consider performance impact and confirm version compatibility
Example Use Cases
- Review a Client Script that calls ERPNext APIs before saving
- Validate a Server Script (Python) for syntax errors and security issues
- Audit hooks.py to ensure correct event hooks and wiring
- Validate a Jinja template snippet used in a report to prevent rendering errors
- Inspect a Python controller for brittle imports or unresolved references