Rung
Know what has actually been verified, and against which code. Rung is a command-line tool for codebases where coding agents write a lot of the code. It runs your tests, security scanners, runtime checks, TLA+ model checks and Lean proofs, and records each result in a ledger tied to the exact bytes it checked. A result expires the moment those bytes change, and a pull request is blocked only on what it introduces.
What a pass means depends on the check: a test suite proves what its tests assert, TLC proves an invariant over every state of a model, a Lean proof holds for the model it is about. Rung makes each of those explicit and keeps them current; it cannot make a weak test strong.
$ rung check --changed
PASS tests (command): `pytest -q` exited 0
FAIL secrets (scan): 1 new finding(s) at or above `warning`
NEW error slack-bot-token slack.py:1 [7a9fe0a79cbca79f]
FAIL deps (deps): 1 missing, 0 younger than 30 days
MISSING pypi:requests-toolbelt-turbo-pro: not on the registry (hallucinated or private?)
PASS runtime (evidence): 3 artifact(s) recorded
FAIL lease-protocol (tla): invariant ResultFromLeaseHolder is violated
counterexample (5 states):
2. Claim owner = w1, valid = TRUE
3. Expire valid = FALSE
4. Claim owner = w2, valid = TRUE
5. Complete result = w1, done = TRUE
Why
Agents write more code than people can read, and "the build passed" says little about whether a change does what was meant. Rung's answer is three habits.
- Evidence over assertion. A verdict is something a machine checked: a test run, a scanner, a recording of the real app, TLC, the Lean kernel. Rung never takes a model's word.
- Verdicts expire. Each verdict stores content hashes of every file it covered. Change one byte and it is stale. Nobody has to remember to re-run anything.
- Gate on what changed. On a branch, only problems the branch introduces block. Old
findings are compared with the merge base, or accepted once with
rung baseline.
Quick start
uv tool install rung-cli # or: pipx install rung-cli (the command is `rung`)
cd your-repo
rung init # survey the repo, wire up installed scanners, choose a model
rung check # run everything and record verdicts
rung agents --hooks # agents cannot finish while their changes are unverified
Or let your coding agent do the setup. Rung ships a skill that installs the CLI, records your tests and scans, adds the CI gate and installs the agent rules, while leaving the decisions that are yours (accepting existing findings, approving laws) to you:
# Claude Code (2.1.275+), inside a session:
/plugin install rung --marketplace CalQuity/rung
/rung:setup
# Codex and other agents that read .agents/skills:
mkdir -p ~/.agents/skills/rung-setup && curl -fsSL \
https://raw.githubusercontent.com/CalQuity/rung/main/skills/setup/SKILL.md \
| sed 's/^name: setup$/name: rung-setup/' > ~/.agents/skills/rung-setup/SKILL.md
Then let the model write a verification script for your app, which Rung registers as a check only once it has passed against the real app (a failed script is left for you to finish):
rung draft runtime --intent "create, list and delete a todo through the HTTP API"
What it checks
| Concern | Check | What blocks |
|---|---|---|
| Tests | command |
The suite fails |
| Bug fixes | rung prove |
The new test passes without the fix, so it proves nothing |
| Shortcuts to green | shortcuts |
A change adds @ts-ignore, # type: ignore, as any, a lint disable, a skipped, focused or deleted test, a lowered coverage threshold or a relaxed type-checker setting |
| Type checking and lint | command |
The project's own tsc, pyright, mypy, ruff, ESLint, go vet or Clippy fails (rung init records the ones it finds configured) |
| Laws on real code | rung draft property |
A Hypothesis or fast-check property fails, or the properties catch fewer than half the deliberate bugs Rung plants |
| Real behaviour (UI, API, CLI) | evidence |
The drive script fails or records no evidence; require = [".webm"] demands a video. Without a script, an agent's browser or computer-use recording, attested with rung attest, holds until the code changes |
| Secrets | scan (gitleaks) |
A new secret appears |
| Vulnerable dependencies | scan (osv-scanner) |
A new known vulnerability appears |
| Invented or brand-new packages | deps |
A new package is missing from PyPI/npm, or is under 30 days old |
| CI workflows | scan (zizmor) |
A new insecure pattern appears in a workflow |
| Code patterns | scan (semgrep, opengrep, trivy, any SARIF tool) |
A new finding at or above fail_on |
| Concurrency and protocols | tla |
TLC finds an interleaving that breaks an invariant |
| Core logic | lean + drt |
A proof fails or cheats, or the code disagrees with the proven model |
See docs/RECIPES.md for APIs, web apps, libraries, job queues and monorepos.
Laws
LAWS.md lists what must never break, one sentence each, and the checks that enforce it:
## refund-bounded
A refund never exceeds the original charge, even under concurrent retries.
- enforced by: refund-protocol, refund-model, tests
- status: approved
rung laws shows whether each law currently holds: approved, enforced, and every enforcing
verdict fresh. Agents may propose laws; only people approve them.
A verification kit for your running app
rung draft runtime --intent "apply a coupon at checkout and see the total update" gives your
project a verification kit, written by the model and registered as a check only once it has worked
against the real app:
.rung/verify/drive.py, a script that starts the app on a free port, drives a feature the way a user does (Playwright for web UIs, HTTP or CLI calls otherwise) and asserts on what a user would see. It never calls internal functions or test-only endpoints..rung/verify/FEATURES.md, a feature map: for each user-facing feature, how a user reaches it, how the script drives it, what observable state proves it works, and the gotchas (seed data, logins, flaky spots).- A project verification skill (
.claude/skills/verify-<project>/) that tells the project's coding agents to drive the app after any user-facing change, extend the script and feature map when they add a feature, and treat a failure as a product bug rather than weaken the script. - Recordings. Every run writes its evidence to a fresh directory: a video of the browser session
(
--require .webmdemands one), screenshots, request and response logs, a transcript and a structuredresult.json. Their hashes go into the verdict, which stays valid only while the code it covers is unchanged.
Where a script cannot reach a feature (a login flow, a native app), an agent can drive it with its
own browser or computer-use tool and record it with rung attest <check> <files> --by <tool>. Such
evidence is recorded with who captured it and is never re-run, so it is weaker than a script.
Property tests that are tested themselves
rung draft property pricing.py --intent "a total is never negative and equals the discounted sum"
has the model write Hypothesis (Python) or fast-check (TypeScript) properties for the laws, then:
- runs them on the real code. A property that fails is a finding for you, never sent back to the model to make it pass. So is any failure Rung cannot pin on the test's own code;
- breaks the code on purpose, one token at a time (
<to<=,+to-,andtoor), and runs them again. Only a property failure counts as catching a bug; one that breaks the build or times out is not counted. Properties that catch fewer than half go back to the model with the bugs they missed. If no bug can be planted at all, the result says INCONCLUSIVE.
All of this runs in a disposable copy of the project, without credential-looking environment
variables, so your files are never mutated. That limits accidents; it is not a security sandbox,
so read a drafted test before trusting it. The accepted test is written at the end (never over an
existing file); a failed draft is kept in .rung/cache/drafts/. The result is a command check
covering the project, and proposed laws in LAWS.md.
Formal methods where they pay
TLA+ and Lean are not for every file. Rung recommends them where the survey finds code that needs them, and the model drafts the first version.
rung draft tla queue.pywrites a TLA+ model whose actions copy the code's real guards, then runs TLC. A counterexample stops the draft and goes to you. The model never gets to edit the spec until the checker is quiet.rung draft lean pricing.pywrites a small Lean model of the intended behaviour, proves its laws, and sets up a differential test that holds the real code to it on random inputs. Lean checks pass only when#print axiomsshows nosorry, nonative_decideand no invented axioms.
examples/lease-queue shows both catching a real bug, plus a
three-step case only the differential test finds.
In CI
on: pull_request
permissions:
contents: read
pull-requests: write # only for the report comment; without it the gate still works
jobs:
rung:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false
- uses: CalQuity/rung@v0.1.3
The action runs the checks a pull request touches against its merge base. It keeps a single
report comment up to date and fails only on what the pull request introduced. The pull request's
own rung.toml, LAWS.md, waivers, baselines and scanner configs decide what it is checked
against, so the action lists any edits to them in the report and as annotations (add other policy
files, such as a semgrep rules file, with policy-paths); set
policy-changes: fail to fail the gate on them until a reviewer accepts. Without the action, use
rung check --changed and rung report.
To run the same check before every push, add Rung to .pre-commit-config.yaml:
- repo: https://github.com/CalQuity/rung
rev: v0.1.3
hooks:
- id: rung-check
With coding agents
rung agents writes a Claude Code skill and an AGENTS.md section, which Codex, Cursor and
others read. With --hooks it also installs a Claude Code Stop hook. An agent whose edits
touch a check that is not freshly passing is told which rung check to run, and cannot finish
until it has. This is a best-effort local guard: it lets the agent stop on a second attempt (so it
can never trap one), allows the stop if Rung itself cannot run, and looks at everything changed since the branch
left its base, committed or not.
CI with rung check --changed is the enforcement boundary.
Drafting uses a model you already have. rung init reuses your claude or codex login, or
any OpenAI-compatible endpoint, including local models. Rung never stores credentials.
Commands
| Command | Does |
|---|---|
rung init |
Survey the repo, add relevant checks, choose a model |
rung plan |
Recommend checks, cheapest and most useful first |
rung check [ids] [--changed] [--base REF] |
Run checks and record verdicts |
rung status [--ci] |
Fresh, stale, failing; --ci exits 1 unless all fresh |
rung report [--format md|json] |
Checks, laws, new findings and fix proofs |
rung prove 'pytest tests/test_x.py' |
Show a new test fails without the fix and passes with it |
rung laws [--approve ID] |
Which laws hold |
rung add <kind> <id> ... |
Declare a command, evidence, scan or deps check |
rung attest <check> <files> --by <tool> |
Record evidence an agent or person captured (Claude in Chrome, Codex, computer use) |
rung baseline <check> |
Accept a scan's existing findings |
rung waive <check> <fingerprint> --reason ... |
Accept one finding until an expiry date |
rung draft tla|lean|property|runtime |
Model-drafted spec, proof, property tests or drive script; a checker decides |
rung agents [--hooks] |
Agent skill, AGENTS.md rules, enforcement hook |
rung doctor |
Which tools and model CLIs are available |
rung ledger [--prune] |
Check the verdict files for corruption |
Every command takes -C DIR and --json. Exit codes: 0 ok, 1 a check or gate failed,
2 configuration or usage error, 3 a required tool is missing, 4 git error.
Configuration lives in rung.toml; see docs/CONFIG.md.
How Rung stays honest
- A checker never reports
passunless its tool confirmed the result. Missing tools and malformed output are errors, and so is a TLA+ spec that reaches only one state. - A file that changes while a check runs voids the verdict.
- The ways an agent can get to green without fixing anything (silencing the type checker or linter,
skipping or deleting tests, lowering coverage thresholds) block in the
shortcutscheck. - Gitleaks always runs with
--redact, and secret-looking values (tokens, keys, credentials in URLs) are masked before command output is stored in the ledger. That masking is best effort: commands should not print secrets. - Waivers need a reason, an approver and an expiry date. Expired waivers stop applying.
- Each verdict file carries a hash of its own contents, so a hand-edited, truncated or conflicted
file reads as "re-run this check". That is an integrity aid, not a signature: whoever can write
the file can rehash it, so CI re-runs what a change touches (
rung check --changed) and changes to.rung/andLAWS.mddeserve review like any other change (CODEOWNERS helps). - Approving a law or adding a waiver is a file edit, not an authenticated signature.
- Rung collects no telemetry.
Rung's own repository is checked by Rung: see rung.toml and LAWS.md.
Requirements
-
Python 3.11+ and git.
-
The tools for the checks you enable. Java 11+ and
tla2tools.jarfor TLA+; Lean via elan for Lean; gitleaks, osv-scanner, zizmor and so on for scans.rung doctorlists what is missing and how to install it. -
A coding agent or model, only for
rung draft. Checks never use a model. To have Rung draft specs, proofs and drive scripts, install and log in to one of:- Claude Code:
npm install -g @anthropic-ai/claude-code, thenclaude auth login - Codex CLI:
npm install -g @openai/codex, thencodex login - any OpenAI-compatible endpoint (Ollama, vLLM, a hosted API): set
base_url,modeland the key variable under[llm]inrung.toml
rung initlists the ones it finds, asks which to use and offers to log you in;rung init --provider claude-cliskips the question. Rung reuses the tool's own login and never stores credentials. Pick the model withmodel = "..."under[llm]; empty means the tool's default. - Claude Code:
-
For agent enforcement (
rung agents --hooks):rungmust be on thePATHClaude Code sees, whichuv tool install rung-cliprovides.
Credits
Rung builds on other people's ideas and says so.
- Verification skills, the feature map, and the rule that a generated verification skill must run before it counts come from Lauren Tan's pstack. Rung enforces that rule with the ledger; no pstack code is used.
- Verification-guided development, a Lean model differentially tested against production code, is how AWS built Cedar.
- Laws enforced by proof come from Bend 2's
LAWS.bend. - Diff-scoped reporting and agent hooks were informed by React Doctor.
- Counterexample-driven repair of TLA+ specs follows TraceFix and related work.
- The TLA+ and Lean guides in
src/rung/guides/are distilled from the TLA+ VS Code extension's knowledge base and tlaplus/AgentSkills (MIT), opum-ai/proof-skills (MIT), leanprover/skills (Apache-2.0) and alok/vericoding-skill (MIT).
Rung is not affiliated with or endorsed by any of these projects.
License
Metadata
Release files for rung-cli 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| rung_cli-0.1.3.tar.gz | 221.0 kB | With provenance | Details for rung_cli-0.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| rung_cli-0.1.3-py3-none-any.whl | Python 3 | none | any | Details for rung_cli-0.1.3-py3-none-any.whl |
Total release size: 355.9 kB
Release files / rung_cli-0.1.3.tar.gz
| Download URL | rung_cli-0.1.3.tar.gz |
|---|---|
| Size | 221.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
96bde7892426b96985300586daf5c69257348f6ee26b54548249224304c073f8
|
|
BLAKE2b-256 checksum How to use checksums |
a5b10270c02e54625b34a82125fd0e752f83af4778e60f24259d98d50c4626c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 1, 2026.
Transparency logRelease files / rung_cli-0.1.3-py3-none-any.whl
| Download URL | rung_cli-0.1.3-py3-none-any.whl |
|---|---|
| Size | 134.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cb458df42bc85e0cea783c2871049e5ee2a8c341e048a3e3325c174a7405b505
|
|
BLAKE2b-256 checksum How to use checksums |
4fa3ee03a9225b80e2a301b4b13ce0acd55518666cca4e4b4c791cd3c7de0003
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 1, 2026.
Transparency logSecurity for rung-cli 0.1.3
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for rung_cli-0.1.3.tar.gz
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 Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.