emlint
stim simulates. sinter samples. emlint verifies.
emlint is a static linter for Stim Detector Error Models (DEMs). It finds structural problems before simulation.
Why emlint?
- Catch DEM bugs before the expensive simulation. A silent DEM bug can inflate logical error rates and may not surface until 10⁶ simulation shots (≈45 min). emlint finds structural problems in milliseconds.
- Counter-examples. A failing check identifies the offending mechanism, detector, or observable, so you know where to look.
- Severity that respects the stakes.
errorfindings are unconditional DEM bugs;warningfindings are heuristic or intentional-pattern candidates you review before acting. - Agent- and CI-friendly. Exit codes distinguish clean, error, warning, and unlintable input; JSON and SARIF output feed scripts and code-scanning tools.
See it in action
A d=5 surface-code DEM with a missing detector connection
(examples/buggy.dem):
$ emlint check examples/buggy.dem
Detectors: 24 Observables: 1 Error mechanisms: 31
DEM-only analysis: findings do not establish circuit semantic correctness.
✓ detectability: All error mechanisms that flip observables also trigger detectors.
✗ [warning] sensitivity: 1 detector(s) are never triggered by any error mechanism.
Counter-example: Detector(s) not triggered by any error mechanism: D17@(1,0,2)
Hint: Hypothesis: a detector no mechanism ever triggers is either miswired or
redundant. Expected shape: at least one mechanism per stabilizer check per
round; look for a missing noise instruction on the gates feeding this detector,
or a DETECTOR declared on a comparison that never receives a fault. Limit: a
detector with a constant outcome (fires every shot, or never fires) also has no
mechanism in the DEM, so it is indistinguishable from a dead detector here;
check the reference sample before treating this as a wiring bug.
✓ observable_coverage: All declared observables are flipped by at least one error mechanism.
✓ probability_bounds: All error mechanism probabilities are finite and strictly positive.
✓ duplicates: No duplicate mechanism signatures found.
✓ correctability: Every syndrome maps to at most one distinct set of logical observables.
✓ null_mechanism: No mechanism has empty detector and observable support.
✓ high_probability_mechanisms: All mechanism probabilities are ≤ 0.3.
✗ [warning] connectivity: Decoding graph has 2 connected components.
Counter-example: Component 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 18, 19, 20, 21, 22, 23]; Component 2: [17]
✓ boundary_symmetry: Skipped: fewer than 2 logical observables (boundary symmetry requires ≥ 2).
After reconnecting D17 to its neighbours
(examples/fixed.dem), every check passes. The
counter-examples name the exact detector (D17@(1,0,2)) and the isolated
decoding-graph component ([17]), so the fix is localized. More generated
examples live in examples/.
v0.2 is preliminary. The user-facing behavior is usable, but internal APIs and implementation details may change before v1.0.
Quick start
Install emlint:
pip install emlint
Lint a DEM file or raw DEM text:
emlint check circuit.dem
emlint check 'error(0.01) D0 L0
detector D0'
A failing result includes a counter-example identifying the relevant mechanism,
detector, or observable. Select checks with --only/--ignore, or emit SARIF for
code-scanning tools:
emlint check circuit.dem --only detectability,sensitivity
emlint check circuit.dem --format sarif > emlint.sarif
Project defaults can be placed in pyproject.toml under [tool.emlint]. For
all command options, configuration examples, and exit codes, see the CLI reference.
Python
import emlint
import stim
circuit = stim.Circuit.generated(
"surface_code:rotated_memory_z",
distance=3,
rounds=3,
after_clifford_depolarization=0.001,
)
report = emlint.check(circuit.detector_error_model())
print(emlint.format_text(report))
emlint.check() also accepts a raw DEM string or an explicit pathlib.Path.
Documentation
| Guide | Use it for |
|---|---|
| CLI reference | Commands, options, output, and exit codes |
| Agent integration guide | Running emlint in a coding-agent fix-loop: exit-code handling, JSON contract, context declaration |
| Check catalog | What the seven checks detect and how to interpret findings, plus the machine-readable output contract |
| Debugging guide | Check-specific causes and investigation steps |
| Formal grounding | DEM vocabulary and formal properties |
| v0.2 limitations | Scope, warnings, repeat blocks, and provisional behavior |
| Changelog | Version history and user-visible changes |
Note: on DEMs produced with
decompose_errors=True, theduplicatescheck (warning) may report mechanisms that share a detector signature after graphlike decomposition as "duplicates". This is expected decomposition output, not a concatenation bug. Thegraphlike-decoderprofile requires an explicit decoder context for compatibility checks; it does not suppress this warning, which can still make a correct DEM exit with code 2. Use--ignore duplicatesonly when you deliberately want to skip this advisory check.
Exit codes
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
At least one error-severity finding |
2 |
Warnings only |
3 |
Unlintable input (unreadable or unparseable) |
Treat exit code 3 as an input failure, not as findings: an agent loop that
parses the report on exit 3 would reason about a DEM that emlint never saw.
Epistemic boundary: emlint analyzes DEMs only. Its findings establish structural properties of the DEM itself; they do not establish that the underlying circuit is semantically correct, nor that a decoder succeeds. Circuit-level verification (e.g. with
stim) remains necessary.
CI
For a minimal GitHub Actions setup, install emlint and run it on changed DEM files:
- name: Install emlint
run: pip install emlint
- name: Lint DEM files
run: find . -name "*.dem" -print0 | xargs -0 -r -n1 emlint check
For local commits, the repository includes a pre-commit hook configuration:
pip install pre-commit
pre-commit install
The hook runs emlint check --severity error on staged .dem files. Warning-only
findings remain non-blocking; input failures still return exit code 3.
Contributing
See CONTRIBUTING.md for development setup, how to add a check, test requirements, and quality gates.
Acknowledgments
emlint is developed with AI assistance for scaffolding, test generation, red-teaming, and documentation. All design decisions, technical content, and final code are reviewed and owned by the maintainers.
Funding
This project is funded by a Mozilla Foundation fellowship and supported by the Unitary Foundation.
License
Apache 2.0
Metadata
Release files for emlint 0.2.4
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 |
|---|---|---|---|---|
| emlint-0.2.4.tar.gz | 113.1 kB | With provenance | Details for emlint-0.2.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| emlint-0.2.4-py3-none-any.whl | Python 3 | none | any | Details for emlint-0.2.4-py3-none-any.whl |
Total release size: 163.4 kB
Release files / emlint-0.2.4.tar.gz
| Download URL | emlint-0.2.4.tar.gz |
|---|---|
| Size | 113.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dd5196ace31098ad855249f38799a235c98db150f37d9bf7281d04df29470775
|
|
BLAKE2b-256 checksum How to use checksums |
391bd95e3abdcf8a2d893a146a70c6e874abdf110e81a54110dc3bfc7213340c
|
| 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 Oct 27, 2026.
Transparency logRelease files / emlint-0.2.4-py3-none-any.whl
| Download URL | emlint-0.2.4-py3-none-any.whl |
|---|---|
| Size | 50.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
210167b055c3f155628b5f86589f2a1a0f102ec983f055e327840eeceeec11e5
|
|
BLAKE2b-256 checksum How to use checksums |
ddf3d2f427912f3474a4a57cf6ea8226b23e1669a42e46bc96a4d7bb8a8c67e6
|
| 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 Oct 27, 2026.
Transparency logSecurity for emlint 0.2.4
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 emlint-0.2.4.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 Oct 27, 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.