Skip to main content

Quality gates for AI-assisted codebases โ€” catch the slop LLMs leave behind.

Project description

๐Ÿชฃ Slop-Mop

PyPI version Primary code scanning gate Python 3.10+ License

Quality gates that catch code that technically passes. Agents optimize for the metric, not the intent โ€” tests that assert nothing, complexity pushed just under the threshold, duplicated logic with renamed variables. Slop-mop treats the agent as an adversarial code author and checks for the slop that other gates wave through. When it finds some, it tells the agent exactly what to fix and how.

Slop-Mop

AI agents are captaining a lot of ships these days. As a group, they are great naval tacticians in battle, but horrible at maintaining their ships. They close tickets, ship features, pass tests โ€” and leave behind duplicated code, untested paths, creeping complexity, and security gaps. Nobody intends to create this mess. It's a natural byproduct of accomplishing tasks, and without something to catch it, it accumulates until the codebase becomes unnavigable.

The useful thing is that every AI agent makes the same kinds of mistakes. They're overconfident (code compiles, must be correct), deceptive (tests pass, must be tested), lazy (it works, no need to clean up), and myopic (this file is fine, never mind what it duplicates). These failure modes are predictable, which means they're automatable.

Slop-mop runs a set of quality gates organized around these four failure modes. Each gate targets a specific pattern โ€” bogus tests, dead code, duplicated strings, complexity creep, missing coverage โ€” and when one fails, it tells the agent exactly what's wrong and how to fix it. Two levels:

  • Swab (sm swab) โ€” routine maintenance, every commit. Quick checks that keep things from getting worse.
  • Scour (sm scour) โ€” deep inspection before opening a PR, or coming into port. Catches what routine swabbing misses and gives you a chance to clear the barnacles from the hull

The mop finds the slop. The agent cleans it up. The ship stays seaworthy.

Fast CI Failure Triage

When a PR fails the primary code-scanning gate, use the reusable machine-first triage script instead of manually digging through logs:

activate && python scripts/ci_scan_triage.py --pr 84 --show-low-coverage

Or triage a specific run immediately:

activate && python scripts/ci_scan_triage.py --run-id 22840517416 --show-low-coverage

What it does:

  • Downloads the slopmop-results artifact from GitHub Actions
  • Extracts actionable failed/error/warned gates
  • Writes machine-readable output to .slopmop/last_ci_triage.json

Quick Start

# Install (once per machine)
pipx install slopmop[all]     # recommended โ€” all tools bundled, isolated
# or: pipx install slopmop    # minimal โ€” just the framework, add tools later
# or: pip install slopmop[all]

# Set up the project
sm init                       # auto-detects languages, writes .sb_config.json

# Run quality gates
sm swab                       # fix what it finds, commit when green
sm scour                      # thorough check before opening a PR
sm buff                       # post-PR loop: CI triage + next-step guidance

Tip: repeat sm swab runs are accelerated by selective per-gate caching. See Selective Gate Caching below for details and --no-cache behavior.

sm init auto-detects Python, JavaScript/TypeScript, Go, Rust, and C/C++ and writes a .sb_config.json with applicable gates enabled. For Go, Rust, and C/C++ projects it scaffolds custom gates (e.g. go test, cargo clippy, make) since built-in gates focus on Python and JS.

Installation Options

Command What You Get
pipx install slopmop Framework only โ€” sm init shows what's missing
pipx install slopmop[lint] + black, isort, autoflake, flake8
pipx install slopmop[typing] + mypy, pyright
pipx install slopmop[security] + bandit, semgrep, detect-secrets, pip-audit
pipx install slopmop[analysis] + vulture, radon
pipx install slopmop[testing] + pytest, pytest-cov, diff-cover
pipx install slopmop[all] Everything above

The Loop

Development with slop-mop follows a single repeated cycle:

sm swab โ†’ see what fails โ†’ fix it โ†’ repeat โ†’ commit

When a gate fails, the output tells the agent exactly what to do next:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๐Ÿค– AI AGENT ITERATION GUIDANCE                           โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Level: swab                                              โ”‚
โ”‚ Failed Gate: overconfidence:coverage-gaps.py             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ NEXT STEPS:                                              โ”‚
โ”‚                                                          โ”‚
โ”‚ 1. Fix the issue described above                         โ”‚
โ”‚ 2. Re-check: sm swab -g overconfidence:coverage-gaps.py  โ”‚
โ”‚ 3. Resume:   sm swab                                     โ”‚
โ”‚                                                          โ”‚
โ”‚ Keep iterating until all the slop is mopped.             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

This is purpose-built for AI agents. The iteration is mechanical, and the agent never has to wonder what to do next. The same trait that creates slop โ€” relentless task accomplishment โ€” is what makes agents excellent at cleaning it up when given precise instructions. Slop-mop turns the agent's biggest liability into its best feature: point the mop at the mess, and the agent won't stop until it's clean.

Practical lifecycle loop:

while coding       -> sm swab
before PR          -> sm scour
after PR opens     -> sm buff

sm buff is post-submit protection. It reads CI scan results for the PR branch, surfaces unresolved machine signals, and directs the next local fix/recheck loop.

The Prescription Contract

Every gate failure must be prescriptive, not just descriptive. A gate that says "coverage too low" is describing a symptom. A gate that says "add tests in tests/test_foo.py covering lines 45-67 of foo.py" is prescribing a remedy.

This is a joint optimization. Prescriptive output is simultaneously:

  • More maintainable โ€” a human reading the failure knows exactly what to do. No digging through tool docs, no re-running with -v, no interpreting a wall of output.
  • More token-efficient โ€” an agent reading the failure can act on the first turn. No exploratory read-search-read cycle burning context to figure out what the gate already knew.

The two goals aren't in tension. When a gate already ran pytest, it has the assertion error. When it already ran coverage, it has the missing line numbers. When it already ran bandit, it has the rule ID that maps to a documented fix. Surfacing that data is the fix for both problems at once.

Gates are sorted into two roles to make the contract enforceable:

Role What it wraps Prescription standard
๐Ÿ”ง Foundation Standard tooling (pytest, mypy, black, eslint, bandit) Relay the tool's own diagnostic. Never say "run the tool yourself".
๐Ÿ”ฌ Diagnostic Novel analysis (AST-based bogus-test detection, gate-dodging diffs, debugger-artifact scans) State what to change, where, and by how much. "Move foo() to bar.py โ€” clears by 223 lines."

A gate earns its place by emitting something an agent can cargo-cult. If fixing it requires independent judgment beyond what the gate can factually determine, the finding stays descriptive and fix_strategy stays None โ€” no guessing.

Use sm status for a report card of all gates at once.


Why These Categories?

Gates aren't organized by language โ€” they're organized by the failure mode they catch. These are the four ways LLMs reliably degrade a codebase:

๐Ÿ”ด Overconfidence

"It compiles, therefore it's correct and will work perfectly in production"

The LLM generates code that looks right, passes a syntax check, and silently breaks at runtime. These gates verify that the code actually works.

Gate What It Does
overconfidence:coverage-gaps.dart ๐Ÿ“Š Dart/Flutter coverage analysis from flutter test --coverage
overconfidence:coverage-gaps.js ๐Ÿ“Š JavaScript coverage analysis
overconfidence:coverage-gaps.py ๐Ÿ“Š Whole-repo coverage (80% default threshold)
overconfidence:missing-annotations.py ๐Ÿ” mypy strict โ€” types must check out
overconfidence:type-blindness.js ๐Ÿ—๏ธ TypeScript type checking (tsc)
overconfidence:type-blindness.py ๐Ÿ”ฌ pyright strict โ€” second opinion on types
overconfidence:untested-code.js ๐Ÿงช Jest test execution
overconfidence:untested-code.py ๐Ÿงช Runs pytest โ€” code must actually pass its tests

๐ŸŸก Deceptiveness

"These tests are in the way of closing the ticket - how can I get around them?"

The LLM writes tests that assert nothing, mock everything, or cover the happy path and call it done. Coverage numbers look great. The code is still broken.

Gate What It Does
deceptiveness:bogus-tests.dart ๐Ÿงช Detects empty or non-assertive Dart/Flutter tests
deceptiveness:bogus-tests.js ๐ŸŽญ Bogus test detection for JS/TS
deceptiveness:bogus-tests.py ๐ŸงŸ AST analysis for tests that assert nothing
deceptiveness:debugger-artifacts ๐Ÿž Catches leftover breakpoint()/debugger;/dbg!()/runtime.Breakpoint() across Python, JS, Rust, Go, C
deceptiveness:gate-dodging ๐Ÿšจ Detects loosened quality thresholds
deceptiveness:hand-wavy-tests.js ๐Ÿ” ESLint expect-expect assertion enforcement

๐ŸŸ  Laziness

"When I ran mypy, it returned errors unrelated to my code changes..."

The LLM solves the immediate problem and moves on. Formatting is inconsistent, dead code accumulates, complexity creeps upward, and nobody notices until the codebase is incomprehensible.

Gate What It Does
laziness:broken-templates.py ๐Ÿ“„ Jinja2 template validation
laziness:complexity-creep.py ๐ŸŒ€ Cyclomatic complexity (max rank C)
laziness:dead-code.py ๐Ÿ’€ Dead code detection via vulture (โ‰ฅ80% confidence)
laziness:generated-artifacts.dart ๐Ÿงฑ Detects committed Flutter build/tool artifacts
laziness:silenced-gates ๐Ÿ”‡ Detects disabled gates when language tooling exists
laziness:sloppy-formatting.js ๐ŸŽจ ESLint + Prettier (supports auto-fix ๐Ÿ”ง)
laziness:sloppy-formatting.py ๐ŸŽจ autoflake, black, isort, flake8 (supports auto-fix ๐Ÿ”ง)
laziness:sloppy-frontend.js โšก Quick ESLint frontend check

๐Ÿ”ต Myopia

"This file is fine in isolation โ€” I don't need to see what it duplicates three directories away"

The LLM has a 200k-token context window and still manages tunnel vision. It duplicates code across files, ignores security implications, and lets functions grow unbounded because it can't see the pattern.

Gate What It Does
myopia:code-sprawl ๐Ÿ“ File and function length limits
myopia:dependency-risk.py ๐Ÿ”’ Full security audit (code + pip-audit)
myopia:ignored-feedback ๐Ÿ’ฌ Checks for unresolved PR review threads
myopia:just-this-once.py ๐Ÿ“ˆ Coverage on changed lines only (diff-cover)
myopia:source-duplication ๐Ÿ“‹ Code clone detection (jscpd)
myopia:string-duplication.py ๐Ÿ”ค Duplicate string literal detection
myopia:vulnerability-blindness.py ๐Ÿ” bandit + semgrep + detect-secrets

Levels

Every gate has an intrinsic level โ€” the point in the workflow where it belongs:

Level Command Gates When to Use
Swab sm swab Most gates across all categories Before every commit
Scour sm scour Everything in swab + scour-only gates Before opening or updating a PR

Scour is a strict superset of swab โ€” it runs everything swab does, plus gates that need more time or PR context. Scour-only gates include dependency-risk.py (full security audit), just-this-once.py (diff-coverage), myopia:ignored-feedback, and any custom gates marked "level": "scour".

Individual gates can be run directly with -g:

sm swab -g overconfidence:coverage-gaps.py     # re-check just coverage
sm swab -g laziness:complexity-creep.py        # re-check just complexity

Time Budget

Use --swabbing-time to set a time budget in seconds. Gates with historical runtime data are scheduled with a dual-lane strategy (one fast lane + heavy lanes) and packed against projected remaining budget. Gates without timing history always run (to establish a baseline). Once a gate starts running, it runs to completion.

sm swab --swabbing-time 30    # only run gates that fit in ~30 seconds

sm init sets a default of 20 seconds. Change it any time:

sm config --swabbing-time 45  # raise the budget
sm config --swabbing-time 0   # disable the limit entirely

Time budgets only apply to swab. Scour runs always execute every gate.

Selective Gate Caching

sm swab uses fingerprint-based result caching to avoid re-running unchanged work. The optimization is selective per gate:

  • Gates can declare their own input scope (for example, only Python files in selected directories).
  • If files outside that scope change, only affected gates re-run; unaffected gates are served from cache.
  • Gates that do not declare a scope still use a safe project-wide fingerprint.

This keeps repeat runs fast while preserving correctness. You will see cache usage in summary output, for example ๐Ÿ“ฆ 3/16 from cache.

sm swab              # normal mode: selective cache hits enabled
sm swab --no-cache   # force a full cold run (debug/troubleshooting)

Cache data is stored at .slopmop/cache.json in the project.


Getting Started

Most projects won't pass all gates on day one. That's expected.

1. Initialize

sm init                       # auto-detects everything, writes .sb_config.json

2. See What Fails

sm swab                       # run swab-level gates, see what fails
sm status                     # full report card

3. Disable What's Not Ready Yet

sm config --disable laziness:complexity-creep.py     # too many complex functions right now
sm config --disable overconfidence:coverage-gaps.py  # coverage is at 30%, not 80%
sm swab                                        # get the rest green first

4. Fix Everything That's Left

Iterate: run sm swab, fix a failure, run again. The iteration guidance tells the agent exactly what to do after each failure.

5. Install Hooks

sm commit-hooks install           # pre-commit hook runs sm swab
sm commit-hooks status            # verify hooks are installed

Now every git commit runs slop-mop. Failed gates block the commit.

6. Re-enable Gates Over Time

sm config --enable laziness:complexity-creep.py      # refactored enough, turn it on
sm config --enable overconfidence:coverage-gaps.py   # coverage is at 75%, set threshold to 70

With hooks in place, every commit runs through slop-mop. Gates that aren't ready yet stay disabled until the codebase catches up.


Configuration

sm config --show              # show all gates and their status
sm config --enable <gate>     # enable a disabled gate
sm config --disable <gate>    # disable a gate
sm config --json <file>       # bulk update from JSON

Include / Exclude Directories

sm config --exclude-dir myopia:generated       # skip generated code
sm config --include-dir overconfidence:src      # only check src/
  • include_dirs: whitelist โ€” only these dirs are scanned
  • exclude_dirs: blacklist โ€” always skipped, takes precedence

.sb_config.json

Edit directly for per-gate configuration. Gates are organized by flaw category:

{
  "version": "1.0",
  "swabbing_time": 20,
  "overconfidence": {
    "enabled": true,
    "gates": {
      "coverage-gaps.py": { "enabled": true, "threshold": 80 },
      "untested-code.py": { "enabled": true, "test_dirs": ["tests"], "timeout": 300 }
    }
  },
  "laziness": {
    "enabled": true,
    "gates": {
      "dead-code.py": { "enabled": true, "min_confidence": 80, "exclude_patterns": ["**/vendor/**"] }
    }
  }
}

Custom Gates

Custom gates let you plug repo-specific checks into the slop-mop pipeline as shell commands โ€” no Python required. They serve two purposes:

  1. Repo-specific checks โ€” things that only make sense in your project (migration validation, config linting, proprietary build steps) but benefit from slop-mop's reporting, time-budgeting, and LLM-readable output.
  2. Gate prototyping โ€” when you think a check might belong in slop-mop permanently, run it as a custom gate first. If it proves useful across projects, that's a natural signal to promote it to a built-in gate via a feature request or PR.

Custom gates are an escape hatch and a proving ground, not a replacement for make or just. The value is integration with the slop-mop framework โ€” taxonomy, fail-fast, time budget, structured JSON output โ€” not task execution.

{
  "custom_gates": [
    {
      "name": "cargo-clippy",
      "description": "Run clippy lints",
      "category": "laziness",
      "command": "cargo clippy -- -D warnings 2>&1",
      "level": "swab",
      "timeout": 300
    },
    {
      "name": "go-test",
      "description": "Run Go tests",
      "category": "overconfidence",
      "command": "go test ./...",
      "level": "swab",
      "timeout": 300
    }
  ]
}

Custom gates run alongside built-in gates and respect the same enable/disable, timeout, and time-budget mechanics. Exit code 0 means pass, anything else is a failure. sm init auto-scaffolds appropriate custom gates when it detects Go, Rust, or C/C++ projects.

Why Wrapper Gates?

Some built-in gates wrap well-known tools โ€” coverage-gaps.py runs pytest --cov, sloppy-formatting.py runs black --check. Why not just run those tools directly?

They establish a floor. Without them, an AI agent can commit code with no tests, no type checking, and no formatting โ€” and the "interesting" gates like complexity analysis have nothing to anchor to. The wrappers ensure the absolute minimum is in place for sane development.

They provide behavioral conditioning. When an LLM sees slop-mop consistently enforce formatting and test coverage across runs, it starts pre-emptively formatting and testing. The wrappers aren't just gates โ€” they're training signal that encourages models to be good citizens.

They unify the interface. sm swab gives you formatting + type-checking + test coverage + complexity analysis + dead code + duplicate detection + vulnerability scanning in one command, with zero per-tool configuration. The wrapper gates make that possible.


CI Integration

Dead-Simple: Turn On Code Scanning

  1. Create .github/workflows/slopmop-code-scanning.yml in your repo.
  2. Paste this workflow.
  3. In branch protection/rulesets, require Primary Code Scanning Gate (blocking).

Note: On private repos, GitHub Code Scanning may require GitHub Advanced Security. Public repos work out of the box.

name: slop-mop primary code scanning gate

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

permissions:
  contents: read
  security-events: write
  actions: read

jobs:
  scan:
    name: Primary Code Scanning Gate (blocking)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install slop-mop
        run: |
          python -m venv .venv
          source .venv/bin/activate
          pip install --upgrade pip
          pip install slopmop[all]

      - name: Run primary gate (scour -> SARIF)
        id: scour
        continue-on-error: true
        run: |
          source .venv/bin/activate
          sm scour --sarif --output-file slopmop.sarif --no-json

      - name: Publish SARIF to Code Scanning
        uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: slopmop.sarif
          category: slopmop

      - name: Enforce primary gate verdict
        if: steps.scour.outcome == 'failure'
        run: |
          echo "::error::slop-mop primary code scanning gate failed"
          exit 1

Warnings stay warnings. Failures block merge.

Optional: Final Dogfood Sanity After Scan Passes

Use this only if you want a second, downstream sanity run after the primary gate is already green on a PR.

name: slop-mop downstream dogfood sanity

on:
  workflow_run:
    workflows: ["slop-mop primary code scanning gate"]
    types: [completed]

jobs:
  dogfood:
    name: Final Dogfood Sanity Check (blocking)
    if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.workflow_run.head_sha }}
          fetch-depth: 0
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: |
          python -m venv .venv
          source .venv/bin/activate
          pip install --upgrade pip
          pip install slopmop[all]
      - name: Run final dogfood scour
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          source .venv/bin/activate
          sm scour

Recommended policy: one required blocking gate (code scanning), optional dogfood as a second safety net.

Check CI Status Locally

sm ci               # current PR
sm ci 42             # specific PR
sm ci --watch        # poll until CI completes

Architecture

Slop-mop installs as a normal package and is configured per-project via .sb_config.json. The sm command goes on PATH once and works in any repo.

Tool resolution order โ€” sm uses the project's tools when available:

  1. <project_root>/venv/bin/<tool> or .venv/bin/<tool> โ€” project-local venv (highest priority)
  2. $VIRTUAL_ENV/bin/<tool> โ€” currently activated venv
  3. System PATH โ€” sm's own bundled tools (via pipx)

This means if the project has its own pytest (with plugins like pytest-django), sm uses it. Otherwise, sm falls back to its own.

Submodule alternative: For strict version pinning, add slop-mop as a git submodule and invoke python -m slopmop.sm directly. Supported but not recommended for most projects.


Development

# Working on slop-mop itself
pip install -e ".[dev]"
sm scour                   # dogfooding โ€” sm validates its own code
pytest

See CONTRIBUTING.md for the process of adding new gates.


Further Reading

๐Ÿ“– A Hand for Daenerys: Why Tyrion Is Missing from Your Vibe-Coding Council โ€” the article that started this project.


License

Slop-Mop Attribution License v1.0 โ€” free to use, modify, and redistribute with attribution.

P.S. Other than this line in the readme and a few scattered lines here and there, nothing in this project was written by a human. It is, for better or worse, the result of living under the slop-mop regime.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slopmop-0.7.0.tar.gz (242.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

slopmop-0.7.0-py3-none-any.whl (282.2 kB view details)

Uploaded Python 3

File details

Details for the file slopmop-0.7.0.tar.gz.

File metadata

  • Download URL: slopmop-0.7.0.tar.gz
  • Upload date:
  • Size: 242.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for slopmop-0.7.0.tar.gz
Algorithm Hash digest
SHA256 c6ea31b668fc7cfc7541979c78a7b71d641cf332cade99a70db013e4aa31a9bd
MD5 ba098ba92548e2bef6b42f912126eb9d
BLAKE2b-256 c9e3f76163064a7a256ae7a1eb6ea62b0efe956761cc4645fd59bf992c122ce5

See more details on using hashes here.

Provenance

The following attestation bundles were made for slopmop-0.7.0.tar.gz:

Publisher: release.yml on ScienceIsNeato/slop-mop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file slopmop-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: slopmop-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 282.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for slopmop-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0e8f497a40f2a60087ca567615ea8d915a624f88c905ce16c2ec127f776d4540
MD5 00bd324866c0c15e5d6adf83393c923c
BLAKE2b-256 066c711bbba55227b590a99edc62b3a5f26809ceb29bb67a4588e6cf35d6ed6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for slopmop-0.7.0-py3-none-any.whl:

Publisher: release.yml on ScienceIsNeato/slop-mop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page