Zerum
Zerum — deterministic code governance for Python · Credo for Python
v0.5.0 ships ~75 native checks (ZR001–ZR510), explainable findings (curated copy for tier-1 rules; AST-precise fallbacks elsewhere), quiet default / full strict profiles, optional Ruff orchestration, human / json / sarif output, and --remediation-prompt — a deterministic markdown brief for LLM/editor agents (no model call in Zerum). Install via PyPI, Homebrew, crates.io, or GitHub Releases.
Zerum is not a Ruff replacement. It focuses on maintainability, consistency, architecture boundaries, and deterministic AI-slop patterns.
Install
Pick one. After install you get a zerum command on your PATH.
Standalone binary
Download a release archive for your OS/arch from
GitHub Releases, extract, and put zerum on your PATH.
Homebrew (tap formula; after the formula is published):
brew install latentmeta/tap/zerum
crates.io (requires Rust 1.70+):
cargo install zerum --locked
Python (recommended for most Python projects)
No Rust toolchain required — prebuilt wheels:
pip install zerum
Isolated tool installs:
pipx install zerum
# or
uv tool install zerum
Verify:
zerum --version
zerum --help
Quick start
cd your-python-project
# Optional: write a starter zerum.toml (default profile)
zerum init
# Run checks on the project
zerum check .
# Write a remediation prompt for an LLM / editor agent
zerum check . --remediation-prompt
# Browse the catalog and learn a rule
zerum list-checks
zerum explain ZR001
Exit codes for zerum check:
| Code | Meaning |
|---|---|
| 0 | No issues |
| 1 | Issues found |
| 2 | Operational error (bad path, CLI error, etc.) |
Using Zerum
Check a project
zerum check .
zerum check path/to/package
zerum check src/
Human-readable output is the default. Each finding includes rule id, location, explanation, and remediation.
Remediation prompt (v0.5.0)
Zerum can save a deterministic markdown prompt from its findings — grouped by check type, ordered by severity (critical → info), with shared remediation text and source snippets. Zerum does not call an LLM; you paste the file into Cursor, ChatGPT, or another agent.
zerum check . --remediation-prompt
# → zerum-remediation-prompt.md
zerum check . --remediation-prompt fixes.md
zerum check . --profile strict --remediation-prompt fixes.md
Typical workflow:
zerum check . --remediation-prompt fixes.md- Open
fixes.mdin your editor / agent and ask it to apply the remediations - Re-run
zerum check .until clean
The prompt includes:
- Goal and constraints (minimal edits, preserve APIs)
- Summary counts and a type index ordered by severity
- Findings grouped by check id, shared explanation/remediation once per type
- Per-occurrence location, message, and source context
- Instructions to re-run Zerum after edits
Profiles: default vs strict
With no zerum.toml (or [profile] name = "default"), Zerum uses the built-in default profile: noisy pattern heuristics are off so greenfield modules stay quieter.
Enable the full catalog:
zerum check . --profile strict
Or persist it:
zerum init --strict
# writes a strict starter config
[profile]
name = "strict"
Compare:
zerum check . # default — quieter
zerum check . --profile strict # all ~75 rules
Configuration (zerum.toml)
zerum init # default template
zerum init --strict # strict template
Common knobs:
[profile]
name = "default"
[checks.ZR001]
enabled = true
max_lines = 50
[checks.ZR401]
severity = "high"
# Architecture layers (ZR207)
[[checks.ZR207.rules]]
from = "app.domain"
forbidden = "app.infrastructure"
# Always run Ruff when you check (requires ruff on PATH)
# external_checkers = ["ruff"]
Custom profiles can inherit:
[profiles.team]
extends = "default"
[profiles.team.checks.ZR001]
max_lines = 60
zerum check . --profile team
Starter files in the repo: zerum.toml.example, zerum.toml.strict.example.
Explain a rule
zerum explain ZR001
zerum explain ZR401
zerum explain ZR501
Shows category, severity, rationale, false positives, tradeoffs, examples, and remediation.
List checks and external checkers
zerum list-checks
zerum list-checkers
list-checks prints the full ZR catalog. list-checkers shows external adapters (e.g. Ruff) and whether they are available on PATH.
Optional Ruff orchestration
Zerum can run Ruff alongside native checks and merge findings:
# one-off
zerum check . --with-external ruff
# or persist in zerum.toml
# external_checkers = ["ruff"]
zerum check .
Requires ruff on PATH. External findings use ids like EXT-RUFF-E501.
Rule categories
| Range | Category |
|---|---|
| ZR001–015 | Readability |
| ZR101–110 | Consistency |
| ZR201–210 | Design |
| ZR301–315 | Refactor |
| ZR401–415 | Warning |
| ZR501–510 | AI (deterministic) |
Output formats
zerum check . --format human # default
zerum check . --format json
zerum check . --format sarif
zerum check . --profile strict
zerum check . --with-external ruff
zerum check . --remediation-prompt fixes.md
zerum list-checkers
| Output | When to use |
|---|---|
human |
Terminal review — rule id, location, explanation, remediation |
json |
CI artifacts, scripts, and custom dashboards |
sarif |
GitHub code scanning and other SARIF consumers |
--remediation-prompt |
Markdown brief for an LLM/editor agent (file on disk; still prints human/json/sarif to stdout) |
Optional external checkers (Ruff) are available from v0.4.0. Use the default profile for low noise on greenfield code; use --profile strict for full catalog coverage.
Tutorial
Educational material lives under docs/tutorial/:
- 00 — Introduction
- 01 — Static analysis basics
- 02 — Parsing Python in Rust
- 03 — Building a rule engine
- 04 — Writing checks
- 05 — Explain mode and configuration
- 06 — Config and profiles
- 12 — Roadmap
Feature demos
Assume a project directory with some Python sources.
1. First pass (quiet default)
zerum check .
# exit 0 → clean under default profile
# exit 1 → findings printed to stdout
2. Full catalog
zerum check . --profile strict
Expect more findings on small modules (docstring / comment / heuristic rules).
3. Machine-readable report
zerum check . --format json > zerum-report.json
4. Learn why a finding fired
zerum check . --format human
# note a rule id, e.g. ZR003
zerum explain ZR003
5. Team config + architecture boundary
zerum init
# edit zerum.toml — set ZR207 rules for your layers
zerum check .
6. Zerum + Ruff in one command
zerum list-checkers
zerum check . --with-external ruff --format json
7. Save a remediation prompt for an LLM / editor agent
zerum check . --remediation-prompt
# → zerum-remediation-prompt.md
zerum check . --remediation-prompt fixes.md
Findings are grouped by type and sorted by severity (critical first).
8. Upgrade later
pip install --upgrade zerum
# or: pipx upgrade zerum
# or: uv tool upgrade zerum
# or: brew upgrade zerum
zerum --version
Add Zerum to CI/CD (Python project)
Fail the job when Zerum finds issues (exit 1). Use JSON if you want artifacts.
GitHub Actions (pip)
name: Zerum
on:
pull_request:
push:
branches: [main]
jobs:
zerum:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Zerum
run: pip install "zerum==0.5.0"
- name: Run Zerum
run: zerum check . --format human
# Optional: remediation prompt artifact for reviewers / agents
# - run: zerum check . --remediation-prompt zerum-remediation-prompt.md || true
# - uses: actions/upload-artifact@v4
# if: failure()
# with:
# name: zerum-remediation-prompt
# path: zerum-remediation-prompt.md
GitHub Actions (uv)
- uses: astral-sh/setup-uv@v4
- run: uv tool install zerum
- run: zerum check .
GitHub Actions (pipx)
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pipx install zerum
- run: zerum check .
Pre-commit
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: zerum
name: zerum
entry: zerum check .
language: system
pass_filenames: false
types: [python]
Install Zerum on the machine (or in CI) before running pre-commit, e.g. pipx install zerum.
GitLab CI
zerum:
image: python:3.12-slim
script:
- pip install "zerum==0.5.0"
- zerum check .
Tips for CI
- Start with default profile; move to
--profile strictonce the baseline is clean. - Pin the version in CI:
pip install "zerum==0.5.0". - Combine with Ruff only if
ruffis installed in the job:
zerum check . --with-external ruff. - Treat exit code
2as infra failure;1as “findings to fix.” - Optionally upload
--remediation-promptoutput as a CI artifact when the check fails.
Changelog
See CHANGELOG.md. Release notes: v0.5.0.
Building from source (contributors)
For hacking on Zerum itself — not required for normal use.
git clone https://github.com/latentmeta/zerum.git
cd zerum
cargo build --release
./target/release/zerum check path/to/python/project
# or run without installing
cargo run -- check path/to/python/project
cargo run -- explain ZR001
cargo run -- list-checks
cargo run -- init
cargo run -- check path/to/project --remediation-prompt /tmp/fixes.md
# editable Python-env install via maturin
pip install "maturin>=1.7,<2.0"
maturin develop
Tests and lint:
cargo test
cargo clippy --all-targets --all-features -- -D warnings
Coverage (rustup cargo; asdf shims break cargo +toolchain):
export PATH="$HOME/.cargo/bin:$PATH"
cargo +1.97.1 tarpaulin \
--engine llvm --all-targets --all-features --follow-exec \
--out Stdout --fail-under 70 -- --test-threads=1
Packaging notes: packaging/ (PyPI, Homebrew). Config for multi-channel scaffolding: Sastri.toml.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zerum-0.5.0.tar.gz.
File metadata
- Download URL: zerum-0.5.0.tar.gz
- Upload date:
- Size: 18.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ba45c4399711295c559409c54bfa7d430054f1a34929015a17e3a2641cff51b
|
|
| MD5 |
d353cbef29c8b9c8594dce4a970d534c
|
|
| BLAKE2b-256 |
0acf63f2dff5465216fc57fdf1239426016edf61d74a31d4fafcd094c2240c3d
|
Provenance
The following attestation bundles were made for zerum-0.5.0.tar.gz:
Publisher:
publish-pypi.yml on latentmeta/zerum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerum-0.5.0.tar.gz -
Subject digest:
0ba45c4399711295c559409c54bfa7d430054f1a34929015a17e3a2641cff51b - Sigstore transparency entry: 2349768572
- Sigstore integration time:
-
Permalink:
latentmeta/zerum@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/latentmeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zerum-0.5.0-py3-none-win_amd64.whl.
File metadata
- Download URL: zerum-0.5.0-py3-none-win_amd64.whl
- Upload date:
- Size: 1.4 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c96003aaaeb5a2bb58ae14b5388a0c2c6dd96392fc21df72e4604d6b336c9939
|
|
| MD5 |
aad6b7bde134d235d53c7aa28ed86b9e
|
|
| BLAKE2b-256 |
76c714cf3c14e4689e4e3e9c3c79cf2e97202ec0a6866ead5d9359e3e4908ecc
|
Provenance
The following attestation bundles were made for zerum-0.5.0-py3-none-win_amd64.whl:
Publisher:
publish-pypi.yml on latentmeta/zerum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerum-0.5.0-py3-none-win_amd64.whl -
Subject digest:
c96003aaaeb5a2bb58ae14b5388a0c2c6dd96392fc21df72e4604d6b336c9939 - Sigstore transparency entry: 2349770353
- Sigstore integration time:
-
Permalink:
latentmeta/zerum@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/latentmeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zerum-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: zerum-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.5 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee1aeb74c1117674cb8ab4b5d154a6935922c58e8c14c7d81f4c27cc9e6df38f
|
|
| MD5 |
1ab5fad6c18e42f699bfc50952702fa7
|
|
| BLAKE2b-256 |
9a7c103d27e18d07e1f5f6cd49c2cbde498ca6d5ae9d18e6a034cb3888eb5a6d
|
Provenance
The following attestation bundles were made for zerum-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish-pypi.yml on latentmeta/zerum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerum-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
ee1aeb74c1117674cb8ab4b5d154a6935922c58e8c14c7d81f4c27cc9e6df38f - Sigstore transparency entry: 2349769828
- Sigstore integration time:
-
Permalink:
latentmeta/zerum@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/latentmeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zerum-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: zerum-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.4 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ef817ca63f8c0829343c1ab255fd4d6762528b4e60abf9bc156e2ea4bbbce6f
|
|
| MD5 |
5303b1025034493384cc06fe3e8ebcb2
|
|
| BLAKE2b-256 |
438705ce996718f85998e545111b0ff81668c1692271daddb3b10fbe2bc118f8
|
Provenance
The following attestation bundles were made for zerum-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
publish-pypi.yml on latentmeta/zerum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerum-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
1ef817ca63f8c0829343c1ab255fd4d6762528b4e60abf9bc156e2ea4bbbce6f - Sigstore transparency entry: 2349770988
- Sigstore integration time:
-
Permalink:
latentmeta/zerum@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/latentmeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zerum-0.5.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: zerum-0.5.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.3 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7585a327e2eba5e0980d56be764e2dde76528d0dd38478fed495d68c078a551
|
|
| MD5 |
2d4aff855556bc6a8f70c0e0330381e8
|
|
| BLAKE2b-256 |
e9a7e9afbbb9ef1bf93072817843aded4f6385af59ded897fa1d1f36c47fff8b
|
Provenance
The following attestation bundles were made for zerum-0.5.0-py3-none-macosx_11_0_arm64.whl:
Publisher:
publish-pypi.yml on latentmeta/zerum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerum-0.5.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
c7585a327e2eba5e0980d56be764e2dde76528d0dd38478fed495d68c078a551 - Sigstore transparency entry: 2349771542
- Sigstore integration time:
-
Permalink:
latentmeta/zerum@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/latentmeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zerum-0.5.0-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: zerum-0.5.0-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 1.4 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c573f7d106e5c3433739a52ef188c389121040d33cd4a61d6716fa52bb1ae1a3
|
|
| MD5 |
84a2e80214efb1b64b4d55f0194bbc4b
|
|
| BLAKE2b-256 |
f39514c327eb11a8cc58f08c76a3e10392f3eb2777511772a6195c4a74df1425
|
Provenance
The following attestation bundles were made for zerum-0.5.0-py3-none-macosx_10_12_x86_64.whl:
Publisher:
publish-pypi.yml on latentmeta/zerum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerum-0.5.0-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
c573f7d106e5c3433739a52ef188c389121040d33cd4a61d6716fa52bb1ae1a3 - Sigstore transparency entry: 2349769277
- Sigstore integration time:
-
Permalink:
latentmeta/zerum@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/latentmeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ffe176e20b472fb477a3f1d565a5ec53f413aa55 -
Trigger Event:
push
-
Statement type: