Skip to main content

cutoff

Does the model know your library, or the version from 2024?

CI Zero dependencies For library maintainers MIT


You shipped 3.0. You renamed timeout_s to timeout, wrote the migration guide, and bumped the major version. You did everything right.

Then the issues start:

TypeError: connect() got an unexpected keyword argument 'timeout_s' "I followed the example Claude gave me…"

The model learned your API from two years of blog posts, Stack Overflow answers and old READMEs. It writes your old API, confidently, for every one of your users. That's a new kind of bug report, and until now there was no test for it.

cutoff is that test. It asks models to write small programs with your library, runs them against your real current version, and shows exactly which models reach for removed or deprecated APIs. Then it drafts the fix and proves the fix works.

A real run: httpx 0.28

httpx 0.28 (November 2024) removed the proxies= and app= arguments and deprecated verify=<string>. I asked three Claude models for small httpx programs, 5 samples per probe, and ran every one against the real httpx 0.28.1:

Stale httpx calls: Claude Haiku 10 of 15, Claude Sonnet 0 of 15, Claude Opus 0 of 15

$ cutoff run --model claude:haiku --model claude:sonnet --model claude:opus -k 5
Stale-API rate (removed or deprecated calls), by model:
  claude:haiku            67%  (10/15, 95% CI 42–85%)
  claude:opus              0%  (0/15, 95% CI 0–20%)
  claude:sonnet            0%  (0/15, 95% CI 0–20%)

What stale code looks like:
  [claude:haiku] proxy
      client = httpx.Client(proxies="http://localhost:8080")
      → TypeError: Client.__init__() got an unexpected keyword argument 'proxies'
  [claude:haiku] custom-ca
      client = httpx.Client(verify=certifi.where())
      → DeprecationWarning: `verify=<str>` is deprecated. Use `verify=ssl.create_default_context(cafile=...)` …

The bigger models write current httpx. The small, fast one writes the 2023 API: every single time for proxies, and every time for custom CA bundles.

The fix loop

cutoff fix picks the lines of your own changelog that mention the APIs models got wrong (the newest release that mentions each one, plus any line saying what to use instead), reruns only the failing probes with that context, and shows before → after:

With the changelog snippet, stale proxies= calls fell from 10 of 10 to 0 of 10, and stale verify= calls from 10 of 10 to 4 of 10

$ cutoff fix --model claude:haiku -k 10
Before → after, on the probes that went stale:
  custom-ca            claude:haiku       stale 10/10 → 4/10
  proxy                claude:haiku       stale 10/10 → 0/10

No LLM writes the snippet, so it can't invent API advice. For proxies=, two sentences from httpx's changelog fixed it completely. For verify=, they helped but didn't finish the job, and cutoff shows you that instead of hiding it. The config, all 45 raw samples and the fix output are in examples/httpx/.

Set it up in two minutes

uv tool install git+https://github.com/sandeepsirodia/cutoff     # Python 3.11+
cutoff init                                                       # drafts cutoff.toml from your CHANGELOG

init reads your changelog's Removed / Deprecated / Changed entries and drafts one probe per renamed or removed API, each marked # drafted, review me. You fill in what each probe should do and how to check it:

[library]
name = "mylib"
python_path = ["src"]                  # or: install = "pip install mylib==3.0"
removed = ["timeout_s", "old_helper"]
deprecated = ["legacy_mode"]

[[probe]]
id = "connect-timeout"
task = "Connect to host 'db' with a 5 second timeout and store the connection in `result`."
check = """
import solution
assert solution.result.timeout == 5
"""

Keep it green: CI mode

cutoff run --model claude:haiku -k 10 --update-baseline   # once
cutoff run --model claude:haiku -k 10 --ci                # every release: exit 1 if models got more stale

How it judges code, and why you can trust the verdict

Verdict Meaning
removed-api Failed with AttributeError / ImportError / TypeError naming an API from your removed list
deprecated-api Triggered a DeprecationWarning (runs with warnings as errors)
wrong Ran, but your check's assertion failed
crash Anything else, including timeouts and attempted network access
pass Your check passed against your real current version
  • Real execution, not pattern matching. A stale API is only flagged if running it against your library actually fails.
  • Sandboxed enough. Each program runs in a temp directory, with network access blocked and a timeout that kills the whole process group.
  • Honest numbers. Stale rates come with 95% Wilson intervals, because 1/10 and 10/100 are not the same claim.
  • Any model. claude / claude:<model> built in, or name=cmd:<any shell command> for anything else (the prompt arrives in $CUTOFF_PROMPT).

Honest limits

  • Warnings: DeprecationWarning, PendingDeprecationWarning and FutureWarning are all errors in the generated program. A library that signals deprecation some other way (a log line, a custom exception) needs that listed in removed.
  • Python libraries only for now. JS/TS needs its own isolation, so it gets its own version rather than a half-working one here.
  • The network block covers Python sockets, not subprocesses a program might spawn. It's a guard against accidents, not a security sandbox. Don't point cutoff at models you don't trust with code execution on your machine.
  • A removed-api verdict needs the symbol in your removed list; unlisted breakages show up as crash or wrong.
  • The httpx numbers are one library, three probes and small samples (5 or 10 per cell); read them as a demonstration of the method, not a model ranking.

Prior art, and what's new here

  • Research: LLMs Meet Library Evolution (ICSE 2025) measured deprecated-API use by LLMs across popular Python libraries. cutoff turns that kind of study into a tool one maintainer can run in CI.
  • APIScanner flags deprecated API use in your editor. It's static, and aimed at users rather than maintainers.
  • Docs-in-context services (llms.txt, MCP doc servers) deliver current docs to models. cutoff measures whether models get your API wrong, and whether a snippet fixes it.
Development
python -m unittest discover -s tests -v

Tests map 1:1 to SPEC.md. They use a tiny fixture library (fixturelib 2.0, with a renamed argument, a removed function and a deprecated flag) and fake models that return canned programs, so there are no API calls and the runs are deterministic.

MIT © Sandeep Sirodia · Maintain a library models keep getting wrong? Try it, and open an issue with what you find. A ⭐ helps other maintainers find it.

Release files for cutoff 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cutoff 0.1.0
File Size Uploaded
cutoff-0.1.0.tar.gz 17.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cutoff 0.1.0
File Interpreter ABI Platform
cutoff-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.7 kB

Release files / cutoff-0.1.0.tar.gz

Download URL cutoff-0.1.0.tar.gz
Size 17.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7f06877dc2c253f22fe6628ddf1c7e31563d632ba0392c630dc31cfcda73a4b3
BLAKE2b-256 checksum
How to use checksums
410cdb729ddfca546eadfccf90d60ee961b540f33a4faadb239a28f053150120
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / cutoff-0.1.0-py3-none-any.whl

Download URL cutoff-0.1.0-py3-none-any.whl
Size 14.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1ba49b27306e14b2a69fb6d5678fd52a15bde87b23d8fa165475f754a8cee18d
BLAKE2b-256 checksum
How to use checksums
775177ec76e437877cf76a70811e986667e80969276a6a14314b89079b5d3c26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page