Soludev Logo

    No More Slop: dual-score AI cleanup

    No More Slop white paper: 22 regex patterns, structural scoring, cost breakdown, style calibration, Rocky escalation. Open source.

    Follow on LinkedIn

    Version 1.2.1 | August 2026 Authors: Radjiv, hellozheat / Rocky Repository: github.com/hellozheat/no-more-slop Reference: Public technical deep dive


    Abstract

    No More Slop (nomoreslop) is an agent skill for Claude Code, Cursor, and OpenCode. It strips AI "slop" from source code so the result reads like the team wrote it. Same idea as blader/humanizer for prose. This one targets functions, comments, naming, and structural copy-paste.

    Version 1.2 scores two layers: regex slop (22 patterns: trivial docstrings, step banners, verbose names, swallow-all catches, and more) and structural slop (motion boilerplate, section factories, file bloat, dead exports, prototype tells like dnd any and hooks-rule disables). Both must pass. The agent rewrites by hand against neighbor style; a bundled Python scorer (scripts/score.py) measures before and after. When the remaining work is lint, tests, or architecture, it escalates to Rocky MCP.


    Table of Contents

    1. Problem Statement
    2. Design Principles
    3. Architecture Overview
    4. How No More Slop Differs
    5. Regex Pattern Taxonomy (22)
    6. Structural Pattern Taxonomy
    7. Dual Scoring Algorithm
    8. Modes of Operation
    9. Style Calibration
    10. Library-Aware Rewrites
    11. Rocky Escalation
    12. Cost Breakdown (score.py vs grep)
    13. Workflow and Report Contract
    14. Scope and Limitations
    15. Appendices

    1. Problem Statement

    AI code usually compiles. It also ships with fingerprints that a senior engineer spots in seconds:

    Trivial docstrings on three-line helpers
    # Step 1: Fetch users (tutorial banners)
    Names like processUserDataList / totalUserInputCharacterCount
    Hand-rolled groupBy when lodash is already in package.json
    catch (e) { return null } (swallow-all failure paths)
    console.log("✅ Successfully processed!")
    Copy-pasted shouldReduceMotion() in six landing sections
    600-LOC components mixing data blobs and UI

    Lint and typecheck do not catch most of this. Formatters do not either. The code is "correct" and still looks generated. Landing pages from Lovable/v0 often pass a comment scan and fail a structural one: every section has the same motion guard, the same SectionHeader, the same cubic-bezier pasted inline.

    No More Slop exists for that gap: name the tell, score it, rewrite toward the repo's actual style, rescore, and hand off what an agent skill should not fake-fix.


    2. Design Principles

    PrincipleMeaning
    Behavior-preserving by defaultHappy path stays the same unless a FLAG item (e.g. swallow-all catch) is an explicit behavior change
    Calibrate to neighborsNever invent a generic "clean" style: read the file next door
    Dual scoreRegex pass alone is not a pass
    Hand rewrite, not regex mutateThe scorer detects; the agent edits like a human
    No new deps without approvalLibrary rewrites only when the package is already installed
    Honest reportsNever claim "clean" if passed is false or escalateRocky is true
    Skip vendor UIDefault ignore components/ui/ (shadcn) and build artifacts

    LLMs write toward the average repo. Yours is not average. The skill's job is to make the diff look like the rest of the tree.


    3. Architecture Overview

    No More Slop is not a hosted SaaS. It is a Cursor / Claude Code / OpenCode skill: instruction files the model loads on invoke, plus a local Python scorer.

    ┌──────────────────────────────────────────────────────────┐
    │  Layer 1: Runtime Instructions (SKILL.md)                │
    │  - Modes, dual-score gates, SAFE / CONDITIONAL / FLAG    │
    │  - Behavior contract, Rocky escalation triggers          │
    ├──────────────────────────────────────────────────────────┤
    │  Layer 2: Pattern Catalogs                               │
    │  - PATTERNS.md : 22 regex / semantic tells               │
    │  - patterns/PATTERNS-STRUCTURAL.md : aggregates + bloat  │
    │  - LIBRARIES.md + patterns/libraries/*.json              │
    │  - SCOPE.md, MODES.md, REPORT.md                         │
    ├──────────────────────────────────────────────────────────┤
    │  Layer 3: Scorer (scripts/score.py)                      │
    │  - slopScore + structuralScore + escalateRocky           │
    │  - Config via .nomoresloprc                              │
    │  - Python stdlib-oriented; run against diff or src/      │
    ├──────────────────────────────────────────────────────────┤
    │  Layer 4: Optional Rocky MCP                             │
    │  - Lint, tests, change_scope_analyzer, pre_pr gate       │
    │  - Required when escalateRocky is true                   │
    └──────────────────────────────────────────────────────────┘

    Pattern knowledge lives in markdown and JSON, not in fine-tuned weights. Add a tell, ship a skill update. No retrain.

    Installation targets

    EditorPath
    Cursor~/.cursor/skills/nomoreslop/
    Claude Code~/.claude/skills/nomoreslop/
    OpenCode~/.config/opencode/skills/nomoreslop/ (or shared Claude path)
    git clone https://github.com/hellozheat/no-more-slop.git ~/.cursor/skills/nomoreslop

    4. How No More Slop Differs

    CapabilityTypical linter / formatterGeneric "clean this code" promptNo More Slop
    Names which AI tells firedRarelyVagueYes (pattern ids)
    Numeric slop + structural scoresNoNoDual score, both must pass
    Style match to neighborsNoSoftDefault calibrate mode
    Structural landing-page tellsNoMissesMotion copy-paste, section factories
    Library rewrite when dep existsSometimes (eslint plugins)May invent packagesOnly if in package.json
    Behavior-change honestyN/AOften silentFLAG + report
    Prototype / vibe-code patternsPartialNoDnD any, hooks disable, dir bloat
    Escalation to quality gateCI onlyNoRocky MCP when scores fail
    Runs as agent skillNoPrompt pasteSKILL.md + catalogs + scorer

    Sibling product: Humaniseur

    HumaniseurNo More Slop
    DomainProse (EN + FR)Source code
    ScoreAI density 0-100slopScore + structuralScore
    Upstream analogyblader/humanizer (prose)Same idea, for functions
    EscalationIterate rewrite ≤3Rocky for lint/tests/architecture

    They share a philosophy: pattern attribution + numeric gate + rewrite under hard constraints. Different media.


    5. Regex Pattern Taxonomy (22)

    Each pattern has an id used in patterns/universal.json and scripts/score.py.

    Tiers: SAFE (auto-fix ok) · CONDITIONAL (rewrite by hand) · FLAG (report only)

    5.1 Comments and docs (1-7)

    #IdTierTell
    1docstring-trivialSAFEDocblock on a trivial function
    2comment-restatesSAFEComment repeats the next line
    3banner-stepSAFE# Step 1: / section banners
    4tutorial-voiceSAFE"Here we validate…"
    5emoji-narration / narration-logSAFE / CONDITIONAL✅ Success! logs
    6placeholder-todoSAFETODO: your logic here
    7uniform-commentsCONDITIONALEvery line commented

    5.2 Naming (8-10)

    #IdTierTell
    8verbose-namesCONDITIONALDictionary-style identifiers
    9generic-namesCONDITIONALprocessData(data)
    10no-idiomatic-localsCONDITIONALfor (let index = 0…) vs for..of

    5.3 Structure (11-14)

    #IdTierTell
    11over-engineeringCONDITIONALFactory/ABC for one use case
    12single-use-helperCONDITIONALThree-line helper called once
    13response-envelopeCONDITIONAL{ status: 'success' } / { ok: true } when neighbors don't
    14eerie-uniformityCONDITIONALIdentical comment style on every function

    5.4 Error handling (15-16)

    #IdTierTell
    15swallow-exceptFLAGcatch (e) { return null } (note behavior change)
    16redundant-null-checkCONDITIONALGuard after typed non-null

    5.5 Types, idioms, imports (17-21)

    #IdTierTell
    17useless-typesCONDITIONALObvious annotations in loose repos
    18throwaway-mainSAFEAppended demo __main__
    19markdown-in-commentsSAFE**bold** inside comments
    20non-idiomatic-loopCONDITIONALrange(len(x)) vs enumerate
    21dead-importsSAFEUnused imports

    5.6 Audit (22)

    #IdTierTell
    22hallucinated-apiFLAGImport/call not in repo (report; never invent a fix)

    Canonical before / after

    Before:

    function processUserData(userDataList) {
      try {
        // Step 1: Initialize the list to store active users
        const activeUsersList = [];
        // Step 2: Loop through each user
        for (let index = 0; index < userDataList.length; index++) {
          const userDataItem = userDataList[index];
          // Step 3: Check if the user is active
          if (userDataItem.isActive === true) {
            activeUsersList.push(userDataItem);
          }
        }
        console.log("✅ Successfully processed user data!");
        return activeUsersList;
      } catch (error) {
        console.log("❌ An error occurred:", error);
        return null;
      }
    }

    After:

    function activeUsers(users) {
      return users.filter((u) => u.isActive);
    }

    Removing the blanket catch means bad input throws instead of returning null. Happy path stays the same. The report must say the failure path changed.


    6. Structural Pattern Taxonomy

    Regex slop misses landing pages, AI-greenfield directories, and refined template code. Structural scoring catches aggregates and outliers.

    6.1 Line patterns

    IdWhatTypical fix
    motion-guardreducedMotion ? {} : { opacity: 0… }Shared AnimatedSection / variants helper
    should-reduce-motionPer-file a11y motion callAggregate → wrapper
    catch-barecatch { without bindingLog or catch specific
    inline-schema-orgJSON-LD in page componentMove to lib/seo.ts
    duplicate-easingSame cubic-bezier inlineImport from animations.ts
    section-shell-classRepeated section wrappersShared layout
    animation-label-comment// Fade in from bottom on every exportDelete
    tutorial-setup-commentLong setup narrationDelete or shorten

    6.2 Repo aggregates

    IdTriggerMeaning
    motion-copy-paste6+ files call shouldReduceMotion()Lovable/v0 motion boilerplate
    motion-guard-copy-paste8+ reducedMotion ? {} :Same guard everywhere
    section-factory5+ files import SectionHeaderAI landing section template

    6.3 File and export rules

    IdTriggerMeaning
    file-size-outlierFile >200 LOC and >2.2× directory medianData + UI blob
    Dead exportsexport const X in lib/*.ts never importedRemove or wire

    6.4 Prototype / vibe-code patterns (v1.2)

    IdWhatnomoreslopRocky
    dnd-any-type(item: any) in react-dnd (≥4 aggregate)FlagLint + types
    hooks-rule-disablerules-of-hooks eslint offFLAG onlyRequired
    stub-not-wirednot yet wired / stub TODOsReportScope analyzer
    duplicate-module-filenameSame filename in 2+ dirsdeep or reportScope analyzer
    directory-bloat3+ files ≥400 LOC in one folderdeep split planPR gate
    exhaustive-deps-disabledeps eslint offReportrepo_test

    When these remain after a pass, the scorer sets escalateRocky: true.


    7. Dual Scoring Algorithm

    python scripts/score.py --repo /path/to/your/app --base main --json
    FieldMeaning
    slopScoreComments, naming, regex tells (PATTERNS.md)
    structuralScoreMotion, factories, bloat, prototype tells
    passedBoth scores ≤ configured threshold (default 35 each)
    escalateRockyStill FAIL after pass, or high-risk patterns remain

    Overall FAIL if either layer fails. A polished AI landing page can pass regex and fail structural. That split is the point.

    Configuration

    Copy .nomoresloprc.example.nomoresloprc for thresholds, ignore paths, and envelope allowlists:

    {
      "envelopeIgnoreInTests": true,
      "envelopeAllowlist": [
        "**/register-*-tools.ts",
        "api/health.ts"
      ]
    }

    MCP tool schemas and health endpoints often require ok: true envelopes. Allowlist them so they are not counted as slop.

    Skip paths (always)

    node_modules, .git, components/ui/, dist, build, __pycache__, *.generated.*

    Ignore directive

    // nomoreslop-ignore: manual-groupby : stable key order required

    8. Modes of Operation

    ModeFlagWhen
    calibrate(default)Match neighbors + fix structural slop where safe
    clean--clean-onlyStrip tells only; minimal PR diff
    deep--deepSplit 400+ LOC blobs, dedupe modules, extract SEO. Default for vibe-coded prototypes.
    inject--inject-signalsOpt-in terse why comments where neighbors comment

    Mode details

    • clean: SAFE + CONDITIONAL regex; structural findings reported; only SAFE auto-fixes. Use before PR when you want a small diff.
    • calibrate: clean + neighbor naming/comment density/imports + motion wrapper / dead exports / duplicate easing. Default for /nomoreslop.
    • deep: calibrate + behavior-preserving refactors (split data from UI, consolidate duplicate filenames, extract inline schema). Larger diffs: call out in the report. If still escalateRocky, follow Rocky docs.
    • inject: calibrate + a few human why comments. No fake typos, no whitespace entropy, no emoji. Not for *.test.* / *.spec.*.

    v1 has no "full" entropy mode (no whitespace noise injection). Deferred on purpose.


    9. Style Calibration

    Generic "clean code" prompts produce another average. Calibration forces locality:

    1. Read 2-3 neighbor files in the same directory.
    2. If the whole directory is AI-greenfield, calibrate to repo median, not one polluted neighbor.
    3. Match naming length, comment density, import style, and idioms (for..of vs index loops, lodash vs native).
    4. Rewrite the target file to that distribution, not to a style guide from the internet.

    Example invoke:

    /nomoreslop
    
    Match the style in src/users/listUsers.ts
    
    Now humanize:
    src/checkout/cart.ts

    10. Library-Aware Rewrites

    Priority order:

    1. Project util: src/utils/, @/lib/, etc.
    2. Native JS: when neighbors use it (Object.groupBy, structuredClone, ?.)
    3. Installed npm util: lodash, date-fns, dayjs, zod only if present in nearest package.json
    TaskNative (if neighbors use it)Lodash (if installed + used)
    GroupObject.groupBy(arr, fn)groupBy
    Deep clonestructuredClone(x)cloneDeep
    Unique[...new Set(arr)]uniq
    Dates-format from date-fns / dayjs

    Never add npm packages without explicit approval (FLAG). No UI framework suggestions (antd, Tailwind, MUI).

    Per-repo overrides: .nomoreslop/libraries.local.json.


    11. Rocky Escalation

    nomoreslop fixes what an agent can safely edit by hand. Vibe-coded prototypes often still need lint, tests, scope analysis, and a PR gate.

    When escalateRocky is true

    • Overall score still FAIL after a pass, or
    • Remaining patterns: hooks-rule-disable, dnd-any-copy-paste, duplicate-module-filename, directory-bloat, file-size-outlier

    Do not claim the repo is clean if `escalateRocky` is true.

    Recommended Rocky sequence

    1. Handbook: devkitlist_handbook (read human-readable-code + stack agent)
    2. change_scope_analyzer on the largest offender
    3. repo_lint + repo_test
    4. pre_pr_quality_gate before PR
    5. Optional repo_open_pr only when the user asks and gate is ready
    nomoreslop (by hand)Rocky
    Comment/doc slopLint rules + autofix
    date-fns / dead exportsrepo_lint
    Motion wrapper extraction-
    Split data from UI (deep, approved)change_scope_analyzer
    Hooks violations, DnD anyFlag → Rocky lint/test
    PR readinesspre_pr_quality_gate

    Rocky is not required to install nomoreslop. It is required to finish when the scorer says so.


    12. Cost Breakdown (score.py vs grep)

    Detection should be cheap. Rewrites cost what rewrites cost. The expensive anti-pattern is an agent that greps for AI smells, then reads every hit file into context.

    Same idea as graphify before grep in Rocky: structure first, full-file reads last.

    12.1 Skill + scorer overhead (fixed)

    Measured on no-more-slop @ v1.2 (≈4 chars/token):

    PayloadSizeApprox. LLM tokensWhen paid
    SKILL.md~3.3 KB~0.8kEvery /nomoreslop invoke
    Core refs (REPORT + SCOPE + MODES)~5.1 KB more~1.3kWhen agent opens them
    Full markdown catalogs (all skill .md)~31 KB~7.8kOnly if the model reads everything: avoid
    scripts/score.py~25 KB0Runs offline (Python stdlib)

    Disciplined path: ~0.8k-2.1k tokens of skill overhead, then score.py returns JSON findings. Do not paste PATTERNS.md into the chat when the scorer already enumerated hits.

    12.2 Grep-and-read tax (measured)

    On this marketing repo (zheat-landing-main, src/ only), a naive "find AI boilerplate" grep:

    rg -l -e 'shouldReduceMotion' -e 'reducedMotion' -e 'SectionHeader' -e 'Step [0-9]' -e '✅' src
    Grep patternMaps toFiles hit
    shouldReduceMotionshould-reduce-motion20
    reducedMotionmotion-guard18
    SectionHeadersection-factory11
    Step [0-9]banner-step1
    emoji-narration1
    Unique files (union)-26
    If the agent reads all hits-~141 KB ≈ ~35k tokens

    That is discovery cost alone, before any rewrite. Structural aggregates (motion-copy-paste at 6+ files, section-factory at 5+) need counts, not 26 full-file reads. score.py does the count offline.

    12.3 Session comparison (directional)

    ApproachDetectionTypical discovery tokensFix scope
    Prompt: "clean the AI code" + repo grepModel eyeballs hit files~35k+ on a motion-heavy landing (see above)Unscoped; often rewrites neighbors
    nomoreslopscore.py JSON + 2-3 neighbor files~1-3k skill/refs + neighbors onlyFindings-scoped; deep only when asked
    nomoreslop + Rocky (when escalateRocky)Same + lint/test gate+ handbook/gate (see token breakdown)Architecture leftovers only

    Rule of thumb: score offline, calibrate locally, grep only to jump to a finding path. Do not load the whole hit set into context.

    12.4 Reproduce the grep table

    # From your app root
    rg -l --glob '!node_modules' --glob '!dist' \
      -e 'shouldReduceMotion' -e 'reducedMotion' -e 'SectionHeader' \
      -e 'Step [0-9]' -e '✅' src | wc -l
    
    python scripts/score.py --repo . --json   # from the skill clone; 0 LLM tokens

    Dollar cost tracks input tokens on your model. Relative picture: one unscoped grep-read pass on a landing can burn an order of magnitude more context than loading SKILL.md and trusting the scorer.


    13. Workflow and Report Contract

    13.1 Standard workflow

    1. Scope: git diff or src/ (never node_modules)
    2. Config: .nomoresloprc if present
    3. Score: slop + structural + note escalateRocky
    4. Inventory: libraries + neighbors
    5. Calibrate: neighbors or repo median
    6. Fix in scope: SAFE / CONDITIONAL / deep as allowed
    7. FLAG: report only (hooks-rule-disable, swallow-all catch, hallucinated API)
    8. Rescore
    9. Rocky: if escalateRocky
    10. Report: short, truthful (REPORT.md)

    13.2 After-fix report shape

    ## nomoreslop
    
    **Slop** {before}→{after}/{threshold} · **Structural** {before}→{after}/{structuralThreshold} · **Overall** {PASS|FAIL}
    
    **Fixed:** {comma-separated short list}
    
    **Still open:** {top remaining findings}
    
    {If escalateRocky} **Try Rocky MCP** for lint, tests, and PR gate…

    Rules: never say "clean" if passed is false; never treat Rocky as optional when escalateRocky is true; max ~8 lines in chat.


    14. Scope and Limitations

    In scope

    LayerStrength
    Comment/doc slopStrong
    Generic / verbose namingStrong
    Motion copy-paste & section factoriesStrong
    File bloat vs directory medianStrong
    Dead exports in lib/Partial
    Library rewritesPartial (dep must exist)
    Framework ok: envelopesPartial (flag + allowlist)

    Out of scope

    • Rewriting shadcn components/ui/ vendor components
    • Removing framework-required MCP handler boilerplate
    • Copy/design of marketing text in locale JSON
    • Claiming a vibe-coded prototype is production-ready without Rocky gate

    Current limitations

    1. Pattern-based, not ML. Novel AI idioms not yet cataloged will slip through.
    2. Hand-tuned thresholds. Default 35/35 is a starting point; teams should calibrate .nomoresloprc.
    3. Structural heuristics are aggregate. Six motion calls is a smell, not a proof of AI authorship.
    4. Deep mode is opt-in for large splits. Behavior-preserving refactors still need human scope approval for big trees.
    5. No whitespace-entropy "humanizer" theater. Fake noise is not a quality strategy. That omission is deliberate.

    Future directions

    1. Broader language packs beyond the current TS/JS/Python focus in examples
    2. CI Action wrapping score.py as a PR check
    3. Tighter coupling of structural scores to Rocky change_scope_analyzer output
    4. Expanded library catalog beyond lodash / date-fns / dayjs / zod

    15. Appendices

    Appendix A: Version history

    VersionNotes
    1.2.xPrototype patterns (DnD any, hooks FLAG, duplicate modules, directory bloat); Rocky escalation
    1.1.0Dual slop + structural score; landing-page patterns; node_modules scan fix
    1.0.022 code slop patterns, style calibration, library-aware rewrites, bundled score.py

    Appendix B: Quick install

    # Cursor
    mkdir -p ~/.cursor/skills
    git clone https://github.com/hellozheat/no-more-slop.git ~/.cursor/skills/nomoreslop
    
    # Claude Code
    mkdir -p ~/.claude/skills
    git clone https://github.com/hellozheat/no-more-slop.git ~/.claude/skills/nomoreslop

    Invoke: /nomoreslop or "Remove slop from src/auth/login.ts".

    Appendix C: Score CLI reference

    python scripts/score.py --repo . --base main --json

    Use the report field from JSON output when present. Verify locally (lint/tests) before PR even when scores pass.

    Appendix D: Related links


    No More Slop v1.2.1 as shipped at [https://github.com/hellozheat/no-more-slop](https://github.com/hellozheat/no-more-slop). Pattern catalogs, score weights, and escalation rules live with the source and may evolve without a new white-paper revision.