Skip to content

Instantly share code, notes, and snippets.

@dmzoneill
Created February 25, 2026 19:19
Show Gist options
  • Select an option

  • Save dmzoneill/6f88cc5cb2720168b0f4bd32a1068d22 to your computer and use it in GitHub Desktop.

Select an option

Save dmzoneill/6f88cc5cb2720168b0f4bd32a1068d22 to your computer and use it in GitHub Desktop.
Opinionated Slop Manager
---
name: code-complete-review
description: Multi-agent code quality review combining Code Complete smells, AI slop detection, and 24 opinionated best practices. Uses 3 agents in a Ralph Wiggum loop to scan, fix, and verify. Use when the user asks for code review, code quality, code smells, Code Complete review, Ralph Wiggum loop, slop scan, AI slop, or opinionated review.
---
# Code Complete Review + AI Slop Detection
A 3-agent iterative code quality system: 24 Code Complete smells + 20 AI slop
anti-patterns + 24 opinionated best practices = **68 checks**. Uses the
**Ralph Wiggum Loop** -- scan, fix, verify, repeat until clean.
## The Ralph Wiggum Loop
```
┌─────────────────────────────────────────────┐
│ RALPH WIGGUM LOOP │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────┐ │
│ │ AGENT 1 │───▶│ AGENT 2 │───▶│AGT 3 │ │
│ │ Scanner │ │ Fixer │ │Review│ │
│ └──────────┘ └──────────┘ └──┬───┘ │
│ ▲ │ │
│ │ issues remain? │ │
│ └─────────YES─────────────────┘ │
│ NO ──▶ DONE ✓ │
└─────────────────────────────────────────────┘
```
**Max iterations:** 3 (to prevent infinite loops)
## How to Run
### Pre-Step: Check for Existing Tracker
Before doing ANYTHING, check if a tracker file already exists:
```
Look for .code-review-tracker.md in the workspace root (or current working directory).
```
**If the file EXISTS**, read it and present the user with a choice using `AskQuestion`:
```
Prompt: "Found an existing code review tracker from {date}. It has {N_total} issues
({N_done} fixed, {N_remaining} remaining). What would you like to do?"
Options:
1. "Continue — pick up where I left off"
2. "Start fresh — archive the old tracker and begin a new scan"
```
- **Continue**: Skip straight to Step 1 but ONLY feed the remaining (unfixed) issues
from the tracker into Agent 2 (the Fixer). No need to re-scan.
- **Start fresh**: Rename the old file to `.code-review-tracker.{timestamp}.bak`
and proceed from Step 0 as normal.
**If the file DOES NOT exist**, proceed normally from Step 0.
### Step 0: Determine Scope
Ask the user what to scan:
- **Changed files**: `git diff --name-only` (default)
- **Staged files**: `git diff --cached --name-only`
- **Directory**: User-specified path
- **Specific files**: User-specified file list
Collect the file list before starting the loop.
### Step 1: Launch Agent 1 — The Scanner
Launch a **Task** subagent with `subagent_type: "generalPurpose"` and this prompt:
```
You are the CODE SMELL SCANNER agent. Analyze code files for 68 issues across three
categories: 24 Code Complete smells, 20 AI slop anti-patterns, and 24 opinionated
best practices.
## Files to Scan
{file_list}
## Part A: The 24 Code Complete Smells
Read each file and check for ALL of these issues:
### Structure & Size
1. **Duplicated code** — Same or near-identical logic in multiple places
2. **Routine too long** — Functions/methods exceeding ~50 lines or doing too many things
3. **Loop too long or deeply nested** — Loops with >3 levels of nesting or >30 lines
4. **Class has poor cohesion** — Class methods don't relate to a unified purpose
### Abstraction & Interface
5. **Inconsistent abstraction level** — Class interface mixes high-level and low-level operations
6. **Too many parameters** — Functions with >5 parameters
7. **Compartmentalized changes** — Changes within a class cluster in isolated sections
8. **Parallel modifications required** — Changing one class always requires changing another
### Inheritance & Polymorphism
9. **Parallel inheritance hierarchies** — Adding a subclass here forces adding one there
10. **Parallel case/switch statements** — Same switch logic duplicated across locations
11. **Subclass uses few parent routines** — Inheritance used where composition fits better
### Data & Types
12. **Unorganized related data** — Related data items used together but not grouped in a class/struct
13. **Feature envy** — A routine uses more features of another class than its own
14. **Overloaded primitive** — Primitive types used where a domain type would be clearer
15. **Lazy class** — A class that barely does anything
### Coupling & Data Flow
16. **Tramp data** — Data passed through intermediate routines just to reach its destination
17. **Middle man** — A class that delegates everything and adds no value
18. **Inappropriate intimacy** — One class knows too much about another's internals
19. **Public data members** — Fields exposed directly instead of through accessors/properties
### Naming & Readability
20. **Poor routine names** — Names that don't clearly describe what the routine does
21. **Comments papering over bad code** — Comments explaining code that should be rewritten to be self-evident
### Global State & Coupling
22. **Global variables** — Mutable global/module-level state accessed from multiple places
### Encapsulation
23. **Setup/teardown around calls** — Callers must prepare state before or clean up after calling a routine
24. **Speculative generality (YAGNI)** — Code exists "just in case" but has no current use
## Part B: The 20 AI Slop Anti-Patterns
AI-generated code leaves distinct fingerprints. Scan for ALL of these:
### Narration & Comment Noise
S1. **Captain Obvious comments** — `// Import express` above `import express`, `// Return the result` above `return result`. Comments that just narrate what the code already says
S2. **Apologetic/hedging comments** — `// This is a simple implementation`, `// Note: this could be improved`, `// For now we just...` — insecure filler text
S3. **Markdown in code comments** — Using `**bold**`, `##`, or bullet syntax inside regular code comments where it won't render
S4. **README-in-the-code** — Multi-paragraph explanatory essays in comments that belong in docs, not inline
### Dead Weight
S5. **Orphan imports** — `import` or `require` statements for modules never actually used in the file
S6. **Stub/placeholder functions** — Functions returning hardcoded values with `// TODO: implement` that were never implemented
S7. **Leftover debug statements** — `console.log`, `print()`, `debugger`, `logger.debug` left in production paths
S8. **Dead code blocks** — Commented-out code, unreachable branches, or functions nothing calls
### Over-Engineering
S9. **Premature abstraction** — Interfaces/base classes with exactly one implementation and no plan for more
S10. **Gratuitous design patterns** — Factory/Strategy/Observer/Builder for something a plain function handles
S11. **Unnecessary async** — `async/await` on functions that do no I/O and return synchronously
S12. **Wrapper functions that add nothing** — A function whose only job is to call another function with the same arguments
### Robustness Theater
S13. **Cargo-cult try/catch** — `try { ... } catch(e) { throw e; }` or `except Exception: raise` — catching just to re-raise unchanged
S14. **Paranoid null checks** — Checking for null/undefined on values that are structurally guaranteed to exist (e.g., just assigned two lines above)
S15. **Generic error messages** — `"An error occurred"`, `"Something went wrong"` — errors that tell the user (or developer) nothing
### Naming & Style Drift
S16. **Sycophantic naming** — Absurdly over-qualified names like `userDataResponseFromApiEndpoint` or `processedResultArrayList` where `users` or `results` would do
S17. **Inconsistent conventions** — Mixing `camelCase` and `snake_case` in the same file, or `get_` prefix on some functions but not others with the same pattern
S18. **Magic values with comment instead of constant** — `timeout = 30000 // 30 seconds in ms` instead of `TIMEOUT_MS = 30_000`
### Copy-Paste Residue
S19. **Near-duplicate blocks** — Two or more code blocks that are 80%+ identical with minor variations that should be parameterized
S20. **Hallucinated APIs** — Calls to methods, functions, or library features that don't exist (AI confidently invented them)
## Part C: 24 Opinionated Best Practices
These are not "smells" — they are standards. Enforce them everywhere.
### Type Safety & Explicitness
O1. **Missing type annotations** — Every function signature must have parameter types and return type
O2. **Raw dicts for structured data** — Use dataclass/TypedDict/NamedTuple/interface/struct when the shape is known
O3. **String literals instead of enums** — Fixed value sets (`"pending"`, `"active"`) must be Enum/Literal/union types
O4. **Mutable where const suffices** — Use `Final`/`const`/`readonly` for values assigned once and never reassigned
### Defensive Defaults
O5. **Mutable default arguments** — Never `def foo(items=[])` in Python; use `None` and create inside the function
O6. **Deep nesting over guard clauses** — Invert conditions and return early; max 2 levels of nesting
O7. **Non-exhaustive match/switch** — All cases handled explicitly; no silent `default`/`else` swallowing unknowns
O8. **Implicit exports / star imports** — Use `__all__` (Python), explicit `export` (TS); no `from x import *`
### Modern Idioms
O9. **Outdated string formatting** — f-strings over `.format()`/`%` (Python); template literals over `+` (JS/TS)
O10. **Legacy path handling** — `pathlib.Path` over `os.path` (Python); `path.join` over string concat (Node)
O11. **Callback/promise chains** — `async/await` over `.then()` chains or nested callbacks
O12. **Manual resource cleanup** — Context managers (`with`), `try-finally`, `using`, or `defer` instead of manual open/close
### Error Handling
O13. **Bare/generic exceptions** — Custom exception classes; never `raise Exception(...)` or `throw new Error(...)`
O14. **Uninformative error messages** — Must include: what failed, why, and what the caller can do about it
O15. **Silent catch** — `except: pass` / empty `catch {}` forbidden; at minimum log the error
O16. **Print-based diagnostics** — Use `logging` (Python) or structured logger (TS/JS), not `print()`/`console.log()`
### Docs & Contracts
O17. **Undocumented public functions** — Public functions >5 lines must have docstring/JSDoc (at minimum one-line summary)
O18. **Missing module docstring** — Each file needs a top-level docstring/comment explaining its purpose (1-2 lines)
O19. **No input validation at boundaries** — Public API entry points must validate/assert inputs before proceeding
O20. **Magic numbers** — All numeric literals except 0, 1, -1 must be named constants
### Structure & Hygiene
O21. **Unsorted/ungrouped imports** — Group: stdlib, third-party, local (blank line between); sorted within each group
O22. **Circular imports** — A imports B imports A; restructure with shared module or dependency injection
O23. **God file** — Files over 500 lines should be split by responsibility
O24. **Inconsistent file naming** — One convention per project: `snake_case.py`, `kebab-case.ts`, `PascalCase.tsx`
## Output Format
Return a structured report in this EXACT format.
Classify each issue as **CC-N** (Code Complete), **S-N** (AI Slop), or **O-N** (Opinionated):
## Scan Report
**Files scanned:** {count}
**Total issues found:** {count}
**Severity breakdown:** 🔴 Critical: N | 🟡 Moderate: N | 🟢 Minor: N
### Issues by File
#### {filename}
| # | Code | Smell | Line(s) | Severity | Description |
|---|------|-------|---------|----------|-------------|
| 1 | CC-2 | Routine too long | 45-120 | 🔴 | Function `process_data` is 75 lines |
| 2 | CC-6 | Too many parameters | 45 | 🟡 | `process_data` takes 7 parameters |
| 3 | S-1 | Captain Obvious comment | 12 | 🟢 | `// Import the database module` |
| 4 | S-13 | Cargo-cult try/catch | 88-92 | 🟡 | Catches exception only to re-raise |
| 5 | O-1 | Missing type annotations | 45 | 🟡 | `process_data` has no return type |
| 6 | O-9 | Outdated string formatting | 78 | 🟢 | Uses `"{}".format(x)` instead of f-string |
#### {next filename}
...
### Summary by Category
| Source | Category | Count | Avg Severity |
|--------|----------|-------|-------------|
| CC | Structure & Size | 3 | 🔴 |
| CC | Coupling & Data Flow | 1 | 🟡 |
| SLOP | Narration & Comment Noise | 5 | 🟢 |
| SLOP | Dead Weight | 2 | 🟡 |
| OPINION | Type Safety | 4 | 🟡 |
| OPINION | Modern Idioms | 2 | 🟢 |
| ... | | | |
### Slop Score
**AI Slop Density:** {slop_issues / total_lines_scanned * 100}% — LOW / MEDIUM / HIGH
(LOW = <1%, MEDIUM = 1-3%, HIGH = >3%)
Only report REAL issues. Do not invent problems. If a file is clean, say so.
```
### Step 1.5: Create / Update the Tracker File
After Agent 1 returns its scan report, write (or update) `.code-review-tracker.md`
in the workspace root. This file persists across sessions so work can be resumed.
Write it using this exact format:
```markdown
# Code Review Tracker
**Created:** {YYYY-MM-DD HH:MM} | **Updated:** {YYYY-MM-DD HH:MM} | **Iteration:** {N}/3 | **Status:** IN PROGRESS
| # | Code | File | Line(s) | Smell | Sev | Status | Note |
|---|------|------|---------|-------|-----|--------|------|
| 1 | CC-2 | utils.py | 45-120 | Routine too long | 🔴 | TODO | |
| 2 | S-1 | api.py | 12 | Captain Obvious | 🟢 | DONE | Iter 1 |
| 3 | O-1 | api.py | 45 | Missing types | 🟡 | SKIP | Too risky |
**Statuses:** TODO | DONE | SKIP | FAIL — **Stats:** Total: N | Done: N | Left: N
```
**Tracker rules:** Agent 1 populates as `TODO`. Agent 2 sets `DONE`/`SKIP`. Agent 3 flips bad fixes to `FAIL`. Update timestamps and stats after each iteration. Set `Status: COMPLETE` when loop ends.
### Step 2: Launch Agent 2 — The Fixer
Take the Scanner's report and launch a second **Task** subagent with `subagent_type: "generalPurpose"`:
```
You are the CODE FIXER agent. You receive a code smell report and fix each issue.
## Scan Report
{paste Agent 1's full report here}
## Fixing Rules
Work through each issue in the report. Apply fixes in this priority order:
1. 🔴 Critical issues first
2. 🟡 Moderate issues second
3. 🟢 Minor issues last
### Fix Strategies by Smell
| Smell | Fix Strategy |
|-------|-------------|
| Duplicated code | Extract shared logic into a helper function/method |
| Routine too long | Extract logical blocks into well-named sub-routines |
| Deep nesting | Use early returns, guard clauses, or extract inner logic |
| Poor cohesion | Split class or move misplaced methods to correct class |
| Inconsistent abstraction | Align interface to one abstraction level; push details down |
| Too many parameters | Introduce a parameter object, config dict, or builder |
| Parallel modifications | Consolidate into a single source of truth |
| Parallel case/switch | Replace with polymorphism or a dispatch table |
| Unorganized data | Group into a dataclass, namedtuple, or TypedDict |
| Feature envy | Move the method to the class whose data it uses most |
| Overloaded primitive | Create a domain type (class, enum, NewType) |
| Lazy class | Inline the class into its caller |
| Tramp data | Pass via context object or restructure call chain |
| Middle man | Remove the wrapper; let callers use the real class |
| Inappropriate intimacy | Introduce proper interfaces; hide internals |
| Public data members | Make private; add property accessors if needed |
| Poor names | Rename to reveal intent (verb+noun for functions) |
| Comments over bad code | Rewrite the code to be self-documenting; remove comment |
| Global variables | Convert to dependency injection, class attributes, or config |
| Setup/teardown | Encapsulate in the routine or use context managers |
| YAGNI code | Delete it. If it's in git history, it can be recovered |
| Subclass uses few parent routines | Switch to composition (has-a instead of is-a) |
| Parallel inheritance | Collapse hierarchies or use composition |
| Compartmentalized changes | Split class along the compartment boundaries |
| **--- AI Slop Fixes ---** | |
| Captain Obvious comments | Delete the comment entirely |
| Apologetic/hedging comments | Delete or replace with factual note about a real limitation |
| Markdown in code comments | Strip markdown syntax; use plain text or move to docs |
| README-in-the-code | Move to README, docstring, or design doc; leave one-line summary |
| Orphan imports | Delete the unused import |
| Stub/placeholder functions | Implement or delete; if needed later, file an issue instead |
| Leftover debug statements | Delete console.log/print/debugger from non-debug paths |
| Dead code blocks | Delete commented-out code and unreachable branches |
| Premature abstraction | Collapse interface + single-impl into the concrete class |
| Gratuitous design patterns | Replace with a plain function or direct call |
| Unnecessary async | Remove async/await; return value directly |
| Wrapper adding nothing | Inline the wrapper; let callers use the real function |
| Cargo-cult try/catch | Remove try/catch if it only re-raises; or add real handling |
| Paranoid null checks | Remove checks on structurally guaranteed non-null values |
| Generic error messages | Replace with specific message including what failed and why |
| Sycophantic naming | Shorten to the clearest minimal name |
| Inconsistent conventions | Pick the file/project dominant convention and normalize |
| Magic values with comment | Extract to a named constant; delete the explanatory comment |
| Near-duplicate blocks | Parameterize into a single function with arguments for variation |
| Hallucinated APIs | Replace with the real API call or remove the feature |
| **--- Opinionated Fixes ---** | |
| Missing type annotations | Add parameter types and return type to the signature |
| Raw dicts for structured data | Replace with dataclass/TypedDict/interface; migrate callers |
| String literals instead of enums | Create an Enum/Literal type; replace all occurrences |
| Mutable where const suffices | Add `Final`/`const`/`readonly` qualifier |
| Mutable default arguments | Change default to `None`; create the mutable inside the body |
| Deep nesting over guard clauses | Invert condition, return early, flatten the block |
| Non-exhaustive match/switch | Add missing cases; use `assertNever`/`assert_never` for default |
| Implicit exports / star imports | Add `__all__` or explicit exports; expand star imports |
| Outdated string formatting | Convert to f-string or template literal |
| Legacy path handling | Replace `os.path` calls with `pathlib.Path` equivalents |
| Callback/promise chains | Rewrite as `async/await` |
| Manual resource cleanup | Wrap in context manager (`with`), `try-finally`, or `using` |
| Bare/generic exceptions | Create a domain-specific exception class; raise that instead |
| Uninformative error messages | Rewrite to include what, why, and remediation |
| Silent catch | Add logging or re-raise; never swallow silently |
| Print-based diagnostics | Replace `print`/`console.log` with `logging`/structured logger |
| Undocumented public functions | Add docstring/JSDoc with one-line summary |
| Missing module docstring | Add a 1-2 line module/file docstring at the top |
| No input validation at boundaries | Add type checks, assertions, or validation at entry points |
| Magic numbers | Extract to a named constant at module/class level |
| Unsorted/ungrouped imports | Reorder into stdlib/third-party/local groups; sort within |
| Circular imports | Break the cycle; move shared types to a common module |
| God file | Split into multiple files by responsibility |
| Inconsistent file naming | Rename to match project convention |
### Constraints
- Preserve all existing functionality — no behavioral changes
- Preserve all existing tests — they must still pass
- Keep fixes minimal and surgical — don't refactor beyond the reported issue
- Add NO new comments explaining your changes
- If a fix would be too risky or complex, skip it and note why
### Output
For each fix applied, report:
- File and line(s) changed
- Which smell was fixed (use CC-N or S-N code)
- Brief description of the change
At the end, list any issues you SKIPPED and why.
### Tracker Update
After ALL fixes are applied, update `.code-review-tracker.md`:
- Set each fixed issue's Status to `DONE` and `Fixed In` to `Iter {N}`
- Set each skipped issue's Status to `SKIP` with reason in `Fixed In`
- Update the `Last updated` timestamp and `Stats` section
```
### Step 3: Launch Agent 3 — The Reviewer
Launch a third **Task** subagent with `subagent_type: "generalPurpose"`:
```
You are the CODE REVIEWER agent. You verify fixes and check for regressions.
## Original Scan Report
{paste Agent 1's report}
## Fixer's Changes
{paste Agent 2's output}
## Your Tasks
### A. Verify Fixes
For each issue in the original report:
- [ ] Confirm the fix actually addresses the smell
- [ ] Confirm no new smells were introduced by the fix
- [ ] Confirm the fix preserves original behavior
### B. Regression Scan
Re-scan ALL modified files for the full 24 Code Complete smells, 20 AI slop
patterns, AND 24 opinionated practices. The fixer may have introduced new issues
while fixing old ones. Pay special attention to:
- The fixer introducing slop (new narration comments, unnecessary abstractions)
- Opinionated fixes that are wrong (e.g., incorrect type annotations, bad docstrings)
- New code that violates opinions (e.g., fixer used `os.path` instead of `pathlib`)
### C. Update Tracker
After verification, update `.code-review-tracker.md`:
- Flip any `DONE` items back to `FAIL` if you rejected the fix
- Add any NEW issues found (with status `TODO`) to the table
- Update the `Stats` section
### D. Quality Gate
Rate each fix:
- ✅ **PASS** — Issue fixed, no regressions
- ⚠️ **PARTIAL** — Issue improved but not fully resolved
- ❌ **FAIL** — Fix introduced new problems or didn't address the issue
- ⏭️ **SKIPPED** — Was not attempted (note from Fixer)
### Output Format
## Review Report
**Fixes verified:** N / N
**Pass rate:** X%
**New issues found:** N
**Verdict:** CLEAN ✅ | NEEDS ANOTHER PASS 🔄
### Fix Verification
| # | File | Original Smell | Verdict | Notes |
|---|------|---------------|---------|-------|
| 1 | utils.py | Routine too long | ✅ PASS | Clean extraction |
| 2 | api.py | Too many params | ⚠️ PARTIAL | Reduced from 7 to 5 |
### New Issues Found
| # | File | Smell | Line(s) | Severity | Description |
|---|------|-------|---------|----------|-------------|
(empty if clean)
### Verdict
{CLEAN or NEEDS ANOTHER PASS with explanation}
```
### Step 4: Ralph Wiggum Decision
After Agent 3 completes:
```
IF verdict == "CLEAN ✅" OR iteration >= 3:
→ Report final results to user. Done.
ELSE (verdict == "NEEDS ANOTHER PASS 🔄"):
→ Feed Agent 3's "New Issues Found" back as input to Agent 1
→ Increment iteration counter
→ GOTO Step 1 (with only the new issues as scope)
```
Report to the user after each iteration so they can see progress.
## Final Summary Template
After the loop completes:
1. Update `.code-review-tracker.md` — set `Status: COMPLETE` and finalize all stats
2. Present this to the user:
```markdown
## Code Complete Review — Final Report
**Iterations:** {N}
**Files reviewed:** {count}
### Issue Breakdown
| Source | Found | Fixed | Skipped |
|--------|-------|-------|---------|
| Code Complete (CC) | N | N | N |
| AI Slop (S) | N | N | N |
| Opinionated (O) | N | N | N |
| **Total** | N | N | N |
**AI Slop Density:** {before}% → {after}%
**Final verdict:** {CLEAN / PARTIAL}
### Changes Made
{list of all files modified with summary of changes}
### Remaining Items
{any skipped issues or items needing manual attention}
```
## Tips
- Limit scope to changed files or a single directory for large codebases
- Max 3 iterations; Agent 2 never refactors beyond reported issues
- When resuming from tracker, Agent 2 only works on `TODO` and `FAIL` items
- Add `.code-review-tracker*.md` to `.gitignore`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment