Skip to main content

ratchet

CI

A code-size budget that only goes down. Coding agents are good at adding code and bad at deciding not to. ratchet gives the repo a line budget: growth past the ceiling is refused, in plain words the agent can act on, and only a human can raise it.

ratchet refusing a duplicated-code change on pallets/click, then passing once the duplicate is removed

Real run, on pallets/click: ratchet init records today's size; an agent asked for a feature pastes a near-duplicate of existing code instead of reusing it; ratchet check refuses the growth and says exactly why; the duplicate goes, and the check passes again. Real commands and output: docs/examples.md. (The bloat step is a scripted patch standing in for the agent, honestly -- see the caption there.)

Quickstart

  1. Install: pipx install ratchet-size (or pip install ratchet-size, or pip install -e . from a clone).
  2. Record today's size: ratchet init -- writes .ratchet.json; commit it.
  3. Wire up your agent: paste this sentence to it: "Set up ratchet in /path/to/repo by following https://github.com/krishansubudhi/ratchet/blob/main/SETUP-FOR-AGENTS.md"

That's it. Everything past this point is detail you can come back to: what counts as a line, the four rules, grants, config, and per-harness integrations.

Claude Code plugin

Prefer a plugin over the manual setup above? Ratchet ships as a Claude Code plugin, with this repo doubling as its own marketplace:

/plugin marketplace add krishansubudhi/ratchet
/plugin install ratchet@ratchet

That installs a Stop hook (ratchet check --hook, falling back to uvx if ratchet isn't on PATH yet) so Claude Code can't end a turn with the repo over budget, plus a /ratchet:setup command that runs the agent setup -- install, ratchet init, detect your harness, ratchet check -- in the repo you're in. See .claude-plugin/ and hooks/hooks.json for the manifests, or claude plugin validate . to check them yourself.

What it counts

Scope: the one git repository you run it in (git rev-parse --show-toplevel). Other repos on the machine are never touched, and each repo gets its own .ratchet.json.

Which files: git-tracked files, plus untracked files that aren't gitignored, whose extension is one of the code extensions present at init (.py, .js, .ts, .go, .rs, .java, .rb, .c, .sh, and more -- see CODE_EXTS in ratchet/measure.py). Blank lines don't count.

Source vs tests: a file matching a test glob (test_*, *_test.*, *_tests.*, *.test.*, *.spec.*, or living under tests/, test/, __tests__/, spec/) is tests; everything else is source. Both groups are plain config in .ratchet.json -- edit include/exclude/extensions to carve out a different split, e.g. "exclude": ["vendor/**", "**/*.gen.*"] to stop counting vendored or generated code.

Existing repo: ratchet init measures today's totals and sets the ceilings there (plus optional --slack). A file already over max_file_lines is frozen at its current size, so nothing is refused on day one -- you start where you are. From then on a ceiling may only shrink, unless a human runs ratchet grant.

What the agent sees when refused

ratchet: REFUSED -- 2 problems (source 10,412/10,300, tests 4,980/5,000)

1. source is 10,412 lines, ceiling 10,300: 112 over
   grew vs HEAD: app/api.py +80, app/store.py +40
   fix: remove at least 112 lines of source: delete dead code, reuse what
   exists instead of adding a parallel version, simplify.
2. app/api.py is 431 lines, limit 400
   seams near the middle: line 198 `def upload`, line 230 `class Session`
   fix: split it at a seam: move a cohesive group of definitions into a new
   file, so each piece is under 400 lines.

Do not edit .ratchet.json or .ratchet-grants.jsonl to get past this.
If the growth is truly needed, stop and ask a human to run:
  ratchet grant +112 --group source --reason "<why>" --by <name>
  ratchet grant +31 --file app/api.py --reason "<why>" --by <name>

Every refusal says what grew, by how much, and which fix applies: shrink, split, or ask a human. For a harness, ratchet check --json gives the same result as data.

The four rules

Rule What it does
Budget Each group (source, tests by default) has a ceiling. A total over it is refused.
Seams A file over max_file_lines (default 400) is refused, with the top-level definitions nearest the middle offered as places to cut. A file already over the limit at init is recorded at its size: it may shrink, never grow.
Grants Only ratchet grant raises a limit, and every grant is appended to .ratchet-grants.jsonl with who and why.
Tighten ratchet tighten lowers every ceiling to the measured size (plus optional slack). Nothing ever raises it automatically.

Lines are counted without blanks. Docs, data, and fixtures don't count: only files whose extensions you list in .ratchet.json do.

How grants work

Sometimes the growth is the point: a real feature, a new integration. Then a human runs:

ratchet grant +300 --group source --reason "CSV export (issue 41)" --by alice
ratchet grant +60 --file app/api.py --reason "until the router split lands" --by alice

That raises the ceiling in .ratchet.json and appends a line to .ratchet-grants.jsonl:

{"after": 10600, "before": 10300, "by": "alice", "lines": 300, "reason": "CSV export (issue 41)", "target": "source", "ts": "2026-01-15T09:30:00Z"}

Commit both files. Here is what keeps grants honest:

  • In CI, run ratchet check --base <target branch>. It refuses any ceiling that is higher than the base's unless grants appended since the base pay for it. An agent that edits .ratchet.json by hand passes locally and fails review. The check also refuses a grants log that was rewritten rather than appended to.
  • Grants are one-line diffs in the pull request, so a reviewer sees them.
  • Tell your agent never to run ratchet grant. The Claude Code integration also puts it on the permission deny list.

Run ratchet tighten after a cleanup and commit the result. The room you freed is now the new ceiling.

Running it by hand

ratchet check exits 0 when the repo is within budget, 1 when refused, 2 on a setup error (bad config, not a git repo when --base is used, and so on). python -m ratchet works the same as the ratchet entry point, if you don't have it on PATH.

Integrations

Harness How
Claude Code Stop hook ratchet check --hook: integrations/claude-code
Cursor, Codex CLI, aider instruction file + git hook / test-cmd: integrations/other-agents.md
pre-commit framework hook, or a plain git hook: integrations/pre-commit
GitHub Actions uses: krishansubudhi/ratchet@v0: integrations/github
Anything else prompt snippet: integrations/any-agent.md

Configuration

ratchet init writes .ratchet.json. It is JSON because ratchet must write the file, and Python's standard library reads TOML but cannot write it.

{
  "version": 1,
  "max_file_lines": 400,
  "slack": 0,
  "groups": {
    "source": {"include": ["**"], "exclude": ["**/tests/**", "**/test_*"], "extensions": [".py", ".ts"]},
    "tests":  {"include": ["**/tests/**", "**/test_*"], "extensions": [".py", ".ts"]}
  },
  "ceilings": {"source": 10300, "tests": 5000},
  "file_ceilings": {"app/legacy.py": 1210}
}
  • Globs work like gitignore: **/ means any number of directories.
  • Each file belongs to the first group that matches it.
  • slack keeps some headroom above the measured size after init and tighten, if you would rather not require a delete for every add.
  • Files are listed with git ls-files, so ignored files never count. Outside git, ratchet walks the tree and skips the usual build and vendor directories.

ratchet init --max-file-lines 300 --slack 50 sets both at creation.

Requirements

Python 3.10+, git (optional, needed for --base and the "grew vs" lines). No dependencies. Tests: pip install pytest && python -m pytest -q.

License

Apache-2.0. Copyright 2026 Krishan Subudhi.

Metadata

Release files for ratchet-size 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 ratchet-size 0.1.0
File Size Uploaded
ratchet_size-0.1.0.tar.gz 25.7 kB Details

Built distribution (wheel)

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

Total release size: 47.8 kB

Release files / ratchet_size-0.1.0.tar.gz

Download URL ratchet_size-0.1.0.tar.gz
Size 25.7 kB
Tags Source
SHA-256 checksum
How to use checksums
67c906d4d5ac44804370209e65cc47028c986beff389d699060580e276ec0927
BLAKE2b-256 checksum
How to use checksums
68c4fe3590f8cfa1f074208cefec4adaf5633115e0689164c108fc37921ce4d8
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 Oct 6, 2026.

Transparency log

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

Download URL ratchet_size-0.1.0-py3-none-any.whl
Size 22.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c7c2ced40680d83d851b12f50e3c669b9eec8b6fea2ec1ea126fc8e702a947b7
BLAKE2b-256 checksum
How to use checksums
6b0a045840fc132c6978d5c135ede3afef179b989af58395e9873050493c514e
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 Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

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