Removes AI-generated code smells from branch changes or an explicit file list behind regression tests, including junk tests (prose pins, source greps, copied inventories, mock echoes), each proven by its class's rule before a cut. Use when the user asks to clean up, deslop, remove AI-slop patterns, or prune tests from recent changes.
89%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
This skill may include examples copied from the OpenCode harness. In Codex, do not call OpenCode-only tools such as call_omo_agent(...), task(...), background_output(...), or team_*(...) literally. Translate those examples to Codex native tools:
| OpenCode example | Codex tool to use |
|---|---|
call_omo_agent(subagent_type="explore", ...) | multi_agent_v1.spawn_agent({"message":"TASK: act as an explorer. ...","agent_type":"explorer","fork_context":false}) |
call_omo_agent(subagent_type="librarian", ...) | multi_agent_v1.spawn_agent({"message":"TASK: act as a librarian. ...","agent_type":"librarian","fork_context":false}) |
task(subagent_type="plan", ...) | multi_agent_v1.spawn_agent({"message":"TASK: act as a planning agent. ...","agent_type":"plan","fork_context":false}) |
task(subagent_type="oracle", ...) for final verification | By default, record a self-review in the notepad: re-read the diff, run diagnostics, and capture evidence for every acceptance criterion. Only when the user demanded strict, rigorous, or high-accuracy review, use multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false}); include fork_context: false. |
task(category="...", ...) for implementation or QA | multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","agent_type":"lazycodex-worker-medium","fork_context":false}) |
background_output(task_id="...") | multi_agent_v1.wait_agent(...) for mailbox signals |
team_*(...) | Use Codex native subagents via multi_agent_v1.spawn_agent and multi_agent_v1.wait_agent; use multi_agent_v1.send_input and multi_agent_v1.close_agent only when exposed in the active tools list |
Role-specific behavior must be described in a self-contained message. Use fork_context: false to start the child with only the initial prompt (no parent history); use fork_context: true only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's message. OMO installs these selectable agent roles into ~/.codex/agents/: explorer, librarian, plan, momus, metis, lazycodex-code-reviewer, lazycodex-qa-executor, and lazycodex-gate-reviewer - pass the matching name as agent_type so the child gets that role's model and instructions. Inspect the actual spawn schema: whenever agent_type is exposed, EVERY spawn MUST select an exact LazyCodex role, on V1 or V2. Implementation difficulty selects lazycodex-worker-low, lazycodex-worker-medium, or lazycodex-worker-high; clone QA can select lazycodex-clone-fidelity-reviewer. Never select generic worker or default. If a code block below conflicts with this section, this section wins.
Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If multi_agent_v1.* tools exist, use the table above as written. If instead a flat spawn_agent with a required task_name exists (multi_agent_v2), rewrite every multi_agent_v1.* example: multi_agent_v1.spawn_agent({...,"fork_context":false}) becomes spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"}) ("all" only when full parent history is truly required); send_input becomes send_message; do not call close_agent/resume_agent (finished agents end on their own; followup_task re-tasks one, interrupt_agent stops one); wait_agent takes only timeout_ms and returns on any child mailbox activity. Do not infer role support from V1/V2 or a model version. Legacy-schema exception: only if the actual schema lacks agent_type, omit that unsupported field and carry the complete role instructions in message, with history explicitly disabled. This cannot select a specialized TOML; an installed managed default supplies the medium worker for unnamed non-forks. The guard has no schema metadata and rejects unnamed calls, so report incompatible routing instead of retrying generically. Every deliberate full-history fork must still name its role: Codex skips role application on unnamed full-history forks, an upstream gap no LazyCodex default can repair. If a code block below conflicts with this section, this section wins. fork_context is rejected on multi_agent_v2 (fork_context is not supported in MultiAgentV2; use fork_turns instead).
When translating load_skills=[...], include the requested skill names in the spawned agent's message. If a code block below conflicts with this section, this section wins.
Omit optional keys you do not set. Never send items: [], message: "", model: "", reasoning_effort: "", or service_tier: "". Codex rejects them (Items can't be empty, reasoning_effort must not be empty).
For work likely to exceed one wait cycle, require the child to send WORKING: <task> - <current phase> before long passes and BLOCKED: <reason> only when progress stops. A multi_agent_v1.wait_agent timeout only means no new mailbox update arrived; back off between waits (double the timeout up to ~5 minutes) instead of spinning short cycles. Treat a running child as alive. Fallback only when the child is completed without the deliverable, ack-only after followup, explicitly BLOCKED:, or no longer running.
merge-base main (no arguments needed)Cleans AI-generated slop from a bounded set of changed files while strictly preserving behavior. Locks behavior with regression tests first, then runs a categorized multi-pass cleanup, then verifies with quality gates and a critical review. Reverts and direct-edits when verification fails.
The core safety invariant: behavior is locked by green tests before a single line is removed. A checklist alone is not safety; a passing regression test is.
The agent looks for these ten categories. The first three are stylistic, the next three are structural, the next two are about hidden cost, the ninth is tests, and the last is size.
Obvious comments — comments restating code, trivial docstrings, section dividers, commented-out code, vague TODOs/Notes.
# given, # when, # then, # when/then).Over-defensive code — null checks for guaranteed values, try/except around code that cannot raise, isinstance checks for statically typed params, default values for required params, backward-compat shims, redundant validation duplicated at multiple layers, broad exception catching (except Exception/except BaseException in Python, empty catch {} or catch (e) { console.error(e) } without narrowing in TypeScript/JavaScript).
main(), HTTP handler) with explicit logging + re-raise is acceptable.except Exception → catch the specific exception you expect. Empty catch {} → add instanceof narrowing or re-throw. catch (e) { log(e) } → narrow with instanceof, handle known cases, re-throw unknown.Excessive complexity — deep nesting (>3 levels), nested ternaries, complex boolean expressions (combine 4+ predicates), long parameter lists (>5 args without a struct/dataclass/object), god functions (>50 lines doing many things), overly clever one-liners that sacrifice readability, if/elif/else chains for type/enum/literal discrimination (must be match/case + assert_never), object used as a type annotation (must be Protocol, TypeVar, or explicit union).
if/else for boolean conditions and range checks (not variant discrimination).match/case with assert_never on the wildcard. object annotations → Protocol (structural), TypeVar (generic), or union (known variants).Needless abstraction — pass-through wrappers, single-use helpers, speculative indirection ("we might need this later"), interfaces with one implementer where the interface adds no testability win, factory functions that just call a constructor.
Boundary violations — wrong-layer imports (UI importing DB driver), leaky responsibilities (handler doing business logic that belongs in a service), hidden coupling (module A reads module B's private state), side effects in pure-named functions.
Dead code — unused imports, unused private functions/methods, unreachable branches, stale feature flags, debug leftovers (console.log, print(...), dbg!), removed-but-still-referenced code.
Duplication — copy-pasted branches with trivial differences, redundant helpers that do the same thing in two places, repeated literal/magic-number sequences.
Performance equivalences (behavior-preserving optimizations) — changes that are provably equivalent in semantics but cheaper in time/space:
list(...) when only iterated once → generator)join.length / len() recomputed inside loop → cacheHard rule: only apply when behavior equivalence is obvious. Do NOT change algorithms with subtle correctness implications. Do NOT micro-optimize hot paths without a benchmark. If in doubt, SKIP.
programming skill, references/testing/junk-classes.md (junk classes, retention bar, R/F/C/D marks, proof rule). Two directions:
references/testing/README.md); name its owner contract. A PROSE file (prompt, SKILL.md, rule, markdown) has no behavioral seam: cover only a machine-consumed value (parsed field, sentinel a runtime greps, a doc JSON sample through its real validator) or leave it to review.R / F / C / D. A D or C needs the proof the table names for its class BEFORE the cut: for a behavior class (cannot fail, mock echo, source grep, duplicate contract, layer replay, implementation mirror, fabricated postcondition, input transcript) a mutation of the production owner run against the candidate and its named keeper on the unmodified test tree; for a prose pin the assertion line plus the repo's prose rule; for a change certificate the revert that turns it red; for a seam keeper the knip/grep showing no production caller; for a copied inventory the generator or consumer that owns it; for a capability restatement the delivery test that owns the promise (none: F, not D); for near-duplicate cases the one table that keeps every old assertion (typecheck after). A misnamed test is re-marked by what its assertions check, never cut for its name. A mutation proves D only when the keeper goes red and the candidate stays green (cannot fail) or both go red (redundant); a candidate that is the ONLY red is R, or C into the keeper and re-run. Mutations run in Phase 4b, serially, by the orchestrator, never by a batch worker. A test-only production seam (export, flag, getter, reset hook) whose last test was deleted goes with it.awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(#|\/\/)/' <file> | wc -l.When found, do NOT just flag it. Execute a full modular refactoring:
awk measure above on each source file. For .py files you may instead run the programming skill's Python checker (uv run <programming skill dir>/scripts/python/check-no-excuse-rules.py <paths>, rule oversized-module); it scans only .py files, so it never covers another language.utils.py, helpers.py, common.py, part_1.py).__init__.py re-exports (re-exports ONLY, no logic in __init__.py).Forbidden escapes:
foo_1.py, foo_2.py) — split by what each file DOES.utils.py, helpers.py, service.py).KEEP: genuinely self-contained single-responsibility scripts (e.g., a standalone CLI checker). Opt out with the language's SIZE_OK marker in the first 10 lines (# noqa: SIZE_OK in Python, the form the Python checker reads; // allow: SIZE_OK in //-comment languages) and a comment explaining why.
A pass is complete only when all applicable gates are green. Skip gates that are genuinely N/A for the project (e.g., no security scanner configured), and report N/A explicitly — do not silently skip.
| Gate | Tool | Pass condition |
|---|---|---|
| Regression tests | project's test runner | all green |
| Lint | project's linter | zero errors (warnings OK if pre-existing) |
| Typecheck | lsp_diagnostics on changed files + project type-checker | zero new errors |
| Unit/integration tests | project's test runner | all green (pre-existing failures noted, not introduced) |
| Static/security scan | project's scanner | zero new findings, or N/A if not configured |
Create todos for all phases below. Mark in_progress one at a time.
If file paths were passed as arguments, that is the scope. Otherwise:
git diff $(git merge-base main HEAD)..HEAD --name-onlyFilter out: deleted files, binary files, generated/vendored files (node_modules/, dist/, target/, lockfiles). List the final scope.
For each in-scope source file:
git grep / project test conventions to find related test files.programming/references/testing/README.md) BEFORE editing the file, and name the behavior it guards. It asserts observable outputs at the real boundary, never the implementation shape the cleanup is about to change, and never a value derived from the code under test. A PROSE file (prompt/SKILL.md/rule/markdown) is exempt: its wording is not behavior; skip the test and rely on review, or assert only a machine-consumed value.programming skill's static detector over every in-scope test file, the ones written in step 3 included: bun run <programming skill dir>/scripts/typescript/check-test-junk.ts <test files> (exit 1 lists file:line per junk class it can see: source grep, prose pin, fixed sleep, .only/.skip, self-comparison, lower-bound cardinality). Its findings are category-9 candidates for the Tests Ledger (marked R/F/C/D by the table once its proof runs), never automatic cuts, and a test from step 3 that it flags is rewritten before the pass continues.Only a broken runner stops the pass: if the suite cannot run at all, STOP and report. Do not proceed with cleanup on unverified ground.
The largest, safest deletion is code that should not have existed. Before categorizing smells, run the deletion ladder on each changed unit:
<input type="date">, a custom query parser → URLSearchParams, a bespoke debounce → the util already imported).Only code that lands on Simplify in place proceeds to the smell categories. This turns the pass from "find smells to trim" into "first decide whether the code should exist, then trim what survives." One function replaced by a platform call is a bigger, safer win than any in-place cleanup — and it needs no per-line smell analysis.
For a diff that fixes a bug, grep the callers of every shared function it touches. Prefer one root-cause fix at the shared seam over repeated guards at each caller — a per-caller patch that leaves a sibling caller broken is a partial fix, not a cleanup.
Then produce an explicit plan before spawning the removal agents:
File: src/foo.py
Ladder: 2 units simplify-in-place; 1 unit delete (native <input> replaces custom picker)
Categories: dead code, excessive complexity, performance
Order: dead code → complexity → performance
Risk: medium (touches caching layer)
File: src/bar.py
Ladder: all simplify-in-place
Categories: obvious comments, over-defensive
Order: comments → defensive
Risk: lowIntentional shortcuts: if the plan deliberately keeps a bounded simplification (a naive scan fine under N rows, a global lock, an O(n²) path), mark it in-code with a debt: comment naming the ceiling and the upgrade trigger (in omo, prefix with // @allow so the comment-checker treats it as intentional), and list it under "Remaining Risks / Deferred" in the report. That section is the debt ledger — a simplification with a known ceiling and no marker is indistinguishable from a bug.
Order rule (safest → riskiest): comments → dead code → defensive → duplication → complexity → abstraction/boundary → performance → tests (F rows; D/C wait for Phase 4b) → oversized-modules. This minimizes blast radius of any one change.
deep-low agents in batches of 5Files are processed by deep-low category agents with the $omo:remove-ai-slops skill loaded, batched 5 at a time in parallel. The executable skill name is remove-ai-slops. The deep-low category gives the agent enough thoroughness to correctly evaluate the 10 categories and respect the KEEP rules without slipping into surface fixes; the 5-wide batch is the sweet spot — more than 5 creates result-merging noise and context contention, fewer wastes parallelism.
Batching protocol (strict):
task calls in a single message, every one with run_in_background=true.<system-reminder> notifications as each task finishes.background_output(task_id=...).Never launch all files at once when there are more than 5; never launch them serially when more than one remains in the current batch.
Per-file invocation (one of the 5 in a batch):
task(
category="deep-low",
load_skills=["remove-ai-slops"],
run_in_background=true,
description="Slop removal: {filename}",
prompt="""
Remove AI slops from: {file_path}
First run the deletion ladder from Phase 3 on this file (delete entirely / reuse existing repo code / platform-stdlib-native / simplify in place); only code that must exist proceeds to smell removal.
Then evaluate EVERY category defined in this skill's "Categories (what counts as slop)" section, applying that section's KEEP and REFACTOR rules verbatim — the Categories section you have loaded is canonical, do not work from a restated subset. For a test file, do NOT cut yet: mark every declaration R/F/C/D against `programming/references/testing/junk-classes.md` and return one ledger row per D/C/F (`| test | mark | keeper or reason | proposed proof |`), where the proposed proof names the class's proof kind and, for a mutation, the owner file, the exact find/replace, and the keeper to run. This worker applies `F` rows in the test file it owns and reports them in the ledger as applied; `D`/`C` rows are returned uncut; it never mutates any file to prove a test - the orchestrator runs the proofs in Phase 4b.
Apply changes in this order (safest → riskiest): comments → dead code → defensive → duplication → complexity → abstraction/boundary → performance → tests (`F` rows; `D`/`C` wait for Phase 4b) → oversized-modules.
Hard constraints:
- Behavior MUST be preserved. When equivalence is not obvious, SKIP.
- Do NOT change public API signatures.
- Do NOT remove type hints.
- Do NOT introduce new abstractions or dependencies.
- Diff stays minimal and scoped to slop removal.
Report changes grouped by category. For each change, give before/after, why-slop, why-safe.
For each skipped issue, give reason. For a test file, end with the ledger rows.
"""
)After the last batch finishes and no worker is running, the orchestrator takes the collected ledger rows. For each row whose proof is a mutation, run the programming skill's bun run <programming skill dir>/scripts/typescript/mutation-proof.ts --file <owner> --find <text> --replace <text> --test 'candidate=<argv>' --test 'keeper=<argv>' (add --json for the ledger; each --test value is split on spaces with no quoting, so a filter that contains a space needs a wrapper script or a file-path target instead): it applies the find/replace to the production owner, runs the candidate and the keeper on the unmodified test tree, records both verdicts, and restores the owner byte for byte (sha256-checked) before the next row. One mutation at a time, never while another worker or test run is live, and never two on the same file; a mutation touches a file outside the diff only transiently and is never committed. Rows proven per the table become the cuts; a D or C whose proof fails or was not run is an R. Non-mutation proofs (prose pin, change certificate, seam keeper, copied inventory, capability restatement, near-duplicate cases) are checked here too, from the row's evidence.
Batch failure handling: a multi_agent_v1.wait_agent timeout only means no new mailbox update arrived, not that a deep-low agent failed. For long passes, require each child to send WORKING: <file> - <current phase> and BLOCKED: <reason> only when it cannot progress. Treat a running child as alive. Mark a file for retry only when the child is completed without the deliverable, ack-only after followup, explicitly BLOCKED:, or no longer running. Do NOT block the remaining 4 in that batch; collect successful results and retry the failed file once later. If retry also fails, escalate that file under "Issues Found & Fixed" in the final report.
Run the five quality gates listed above. Then walk the critical review checklist:
Safety:
Behavior:
Quality:
If any gate fails or any checklist item flips:
git checkout the affected file (or use git diff + targeted edit to revert just the problematic hunk).edit calls — applying only the changes you can prove are safe.If you fail three times on the same file, STOP and escalate to the user with: the file, what you tried, what failed, your hypothesis. Do not keep editing.
AI SLOP REMOVAL REPORT
======================
Scope: [branch diff vs merge-base main / explicit file list]
Files: [N files]
- path/to/file1.ts
- path/to/file2.py
Behavior Lock:
- Existing coverage: [N files already covered]
- Tests added: [M gate-passing tests at path/to/test_X.py, each naming its contract]
- Baseline status: GREEN apart from the baseline-red list (recorded as bugs, excluded from proofs, not deleted): [none] OR [list]
Cleanup Plan:
- path/to/file1.ts: [ladder: 1 delete (native) + simplify-in-place] → [dead code → complexity → performance]
- path/to/file2.py: [ladder: all simplify-in-place] → [comments → defensive]
Per-File Results (each cut shows what replaces it):
path/to/file1.ts
- Ladder/delete: custom DatePicker (48 lines) → <input type="date"> (native), flatpickr import removed
- Dead code: 3 removed (lines X-Y, A-B, C) → nothing (unreachable)
- Excessive complexity: 1 simplified (nested ternary at L42 → if/else)
- Performance: 1 (line N: list scan → set lookup, O(n²)→O(n), behavior identical)
- Skipped (preserved): 2 (defensive null check at boundary; commented WHY at L88)
path/to/file2.py
- Obvious comments: 5 removed → nothing
- Over-defensive: 1 simplified (redundant isinstance on typed param)
Tests Ledger (category 9; one row per test declaration marked D/C/F, proofs run in Phase 4b):
| Test | Mark | Keeper / reason | Proof |
|---|---|---|---|
| `foo.test.ts > reads the source for "export"` | D | source grep; `foo.test.ts > exports parse()` owns the contract | mutation parse.ts `return tokens` -> `return []`: candidate green, keeper red; restored sha-equal |
| `skill.test.ts > mentions the gate` | D | prose pin on SKILL.md | assertion line + repo prose rule (no mutation applies) |
Quality Gates:
- Regression tests: PASS (12 tests, 0 failed)
- Lint: PASS
- Typecheck (lsp_diagnostics + project): PASS (0 new errors on changed files)
- Unit/integration tests: PASS (45 tests, 0 failed)
- Static/security scan: N/A (not configured)
Critical Review:
- Safety: PASS
- Behavior: PASS
- Quality: PASS
Issues Found & Fixed:
- [None] OR [Issue description → Fix applied]
Net Impact:
- LOC: -74 (removed 91, added 17)
- Dependencies: -1 (flatpickr removed; native <input type="date"> used)
- Files deleted: 1 (src/date-picker-wrapper.ts — platform-native replacement)
Remaining Risks / Deferred (this section is the debt ledger):
- [None] OR [e.g., "boundary violation in module X flagged but not refactored — needs human judgment"]
- `debt:` markers kept this pass: [None] OR [file:line — ceiling → upgrade trigger]
Final Status: CLEAN | ISSUES FIXED | REQUIRES ATTENTIONN/A and why. If a check failed and you could not fix it, say so. Never claim PASS without evidence.lsp_diagnostics, the test runner, and direct file reads until the result is grounded.code-yeongyu/lazycodex
e230762
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.