The Four Layers
AGENTS.md — Prevention
Tells the AI what to generate and what to avoid. Architecture rules and code rules. First line of defense, but only works if the AI reads and follows it.
ESLint Plugins — Detection
eslint-plugin-unicorn: prefer-add-event-listener flags onclick assignments. eslint-plugin-no-unsanitized: flags innerHTML. These scan code regardless of who wrote it.
Husky + lint-staged — Enforcement
Runs ESLint at commit time. Already in ready-build. Even if warnings are ignored in the editor, bad code cannot enter the repo.
Copilot Code Review — AI Reviewer
gh pr edit --add-reviewer @copilot. Analyzes full repo context on every PR. Catches architectural mismatches, security issues, and subtle patterns that static analysis misses.
What Each Layer Catches
Wrong Architecture
AGENTS.md catches this. Tells the AI "this is an SPA" so it does not generate multi-page navigation. Copilot review can also catch architectural mismatches in PRs.
innerHTML Usage
AGENTS.md says "never innerHTML." eslint-plugin-no-unsanitized flags it automatically. Husky blocks the commit if ESLint fails.
onclick vs addEventListener
AGENTS.md says "use addEventListener." eslint-plugin-unicorn flags onclick assignments. Husky blocks the commit.
Patterns AI Invents
AI may generate patterns that are technically valid but architecturally wrong. Copilot review catches these because it sees the full repo context, not just one file.
AGENTS.md for Event-Driven Code
Add these rules to your AGENTS.md to prevent the most common AI mistakes with event code:
# AGENTS.md — Event-Driven Code Rules
## Architecture
- This is a single-page app (SPA).
- Never generate code that creates new HTML files
or uses window.location for navigation.
- All "pages" are view functions that show/hide
content within index.html.
## Code rules
- Use addEventListener, not onclick property assignments.
- Never use innerHTML — use createElement and textContent.
- All event listeners use named callbacks or arrow functions
passed to addEventListener. ESLint Plugin Setup
# Install the ESLint plugins
npm install --save-dev eslint-plugin-unicorn
npm install --save-dev eslint-plugin-no-unsanitized // In your ESLint config:
// eslint-plugin-unicorn
// Rule: prefer-add-event-listener
// Flags: btn.onclick = () => { ... }
// Suggests: btn.addEventListener('click', () => { ... })
// eslint-plugin-no-unsanitized (Mozilla)
// Rule: no-unsanitized/property
// Flags: element.innerHTML = value
// Flags: element.outerHTML = value Husky + lint-staged
# Husky + lint-staged (already in ready-build)
# Runs ESLint on every commit.
# If ESLint fails, the commit is blocked.
# To verify it's working:
git commit -m "test" --dry-run
# If lint-staged is configured, it will run ESLint
# on staged files before allowing the commit. Copilot Code Review
# Add Copilot as a reviewer on a pull request:
gh pr edit --add-reviewer @copilot
# Copilot reviews the full repo context, not just the diff.
# It can catch architectural mismatches, security issues,
# and patterns that static analysis misses. The Key Insight:
AGENTS.md tells the AI what to do. ESLint checks after the code is written. Husky blocks bad code at commit time. Copilot reviews the full picture on every PR. No single layer catches everything — but together, they catch what I miss. ðŸŸ
See It in a Real Project ðŸŸ
The Robot ID Card App AGENTS.md has these exact rules in action. The app itself uses addEventListener everywhere, createElement instead of innerHTML, and classList instead of inline styles. Explore the full source code to see how prevention works in practice.