Skip to main content

warrantlib

A column of PASS cannot say whether anything was decided. A grid sample over a continuous range and an exhaustive enumeration over a declared finite set can both come back clean. Only the second settled the question. warrantlib is a small vocabulary for keeping that difference in a check suite's output instead of losing it there, and a pytest plugin that reports it.

pip install "warrantlib[pytest]"   # the vocabulary and the plugin
pip install warrantlib             # the vocabulary alone

Python 3.11 and up. The standard library is the only dependency. The extra adds pytest and nothing else, and import warrantlib pulls in no pytest either way.

Running checks under pytest

The plugin loads through pytest's pytest11 entry point. Nothing to configure and no -p flag.

A test hands its findings over with the record_check fixture.

def test_the_coefficient_is_closed_form(record_check):
    record_check(measure_c2())
test_the_coefficient_is_closed_form NOT TRIGGERED                        [100%]

=============================== warrant summary ================================
1 registered, 1 tested here, none fired
   PROVED        NOT TRIGGERED   1

A check's outcome decides its test's, in pytest's own three:

Outcome The test Letter -v reads
NOT_TRIGGERED passes . NOT TRIGGERED
FIRED fails F FIRED
NOT_RESOLVED fails ? NOT RESOLVED
NOT_APPLICABLE skips v NOT APPLICABLE
NOT_RUN_HERE skips e NOT RUN HERE

NOT_RESOLVED fails because a falsifier that ran and could not decide has not left the claim standing. The two that never ran skip, and the letters keep them apart where the skip does not.

The counting category stays pytest's own, so N passed counts what it always counted and assert_outcomes still sees it. A fired check reads like any other failing test: a FAILURES block carrying the check's reason, a row in the short summary, and the run's accounting underneath with the fired count in it.

Flags

Flag What it does
(none) progress letters, then the registered / tested here / fired accounting
-v one line per check, naming it and its outcome
--warrant-detail each check's own line: outcome, warrant, tier, the reason it gives, the refs it was registered at
-vv the same as --warrant-detail, which is pytest's own spelling for more detail
pytest --warrant-detail
================================ warrant checks ================================
T3 gain: NOT TRIGGERED (PROVED, tier exact). PASS — K = σ²/R̄ − σ⁴/R̄² + O(σ⁶).
got: K = sigma**2/Rbar - sigma**4/Rbar**2 provenance: Step 3, the expansion in
prior spread (registered at 99e3c34, measured at 23f0c47)

The row a run prints without it carries the verdict and not the reason, which is the half a reader acts on.

Declaring what a suite is registered to report

A count of checks says a suite got shorter. It cannot say which check left, and a check renamed or swapped for another leaves the count untouched, so the gate passes on a suite that is now measuring something else.

A manifest declares them instead. It names each suite's entry point and every id it reported when the file was written.

# Generated. Do not edit by hand.
schema_version = "1.0"

[suites.series_kernel]
entry_point = "research.checks.series_kernel:run_checks"
checks = [
  "series_kernel.first_cumulant_is_the_mean",
]

Point pytest at it and give it the path to collect:

# pyproject.toml
[tool.pytest.ini_options]
warrant_manifest = "research/registered_checks.toml"
pytest research/registered_checks.toml

Every declared check becomes an item, and the item exists because the manifest declares it rather than because the suite reported it. A check that stops reporting still has a row, and the row fails naming the check and the entry point it went missing from. One further item per suite fails on any id the run reported that the manifest does not carry, so a rename reports as one drop and one addition, which is what it is.

The suite runs once per session however many checks it declares, so a suite costing half a minute costs that once rather than once per row.

Keeping it current

Command What it does
python -m warrantlib.manifest <path> rewrite it from the suites it names
python -m warrantlib.manifest --check <path> say whether it is current, change nothing, exit non-zero if not
--layout-only, with either act on the layout alone, running no suite

--check compares the file as text, so a layout the writer no longer produces counts as stale too. It runs every suite to read their ids, which on a project whose suites are expensive is a heavy way to ask about whitespace.

--layout-only restricts either form to that half, answering from the ids the file already declares. It runs nothing, it says in its own output that it did not ask whether the ids are current, and its write form prints relaid rather than rewritten. Where a runner reconciles the declared ids as it runs them, this is the only question left for a separate command, and it costs milliseconds.

Adding a suite means adding its [suites.<name>] table by hand, with no checks, and then rewriting. The new ids land in the diff, which is where they get reviewed.

Warrant

Warrant says how well a claim is warranted, by the prover class behind it.

Prover What it does Label
1 pen-and-paper theorem, within stated hypotheses PROVED
2 symbolic computation: closed-form identities, algebraic non-existence PROVED
3 · enumeration exhaustive enumeration over a finite domain PROVED, with a completeness certificate
3 · validated validated numerics over a compact domain CERTIFIED
3 · sample sampling a continuum CORROBORATED

CERTIFIED sits between the other two. Validated numerics prove a universal over a compact domain, and the proof carries the bound it was computed with. Borrowing PROVED overclaims. Borrowing CORROBORATED throws the bound away.

An action sweep over a continuous range is a finite grid over an infinite domain, so it samples. A policy enumeration over a declared finite set enumerates. The warrant follows from which of those the check did, not from how clean the answer looked.

Outcome

Outcome says what a registered falsifier did. A falsifier does not pass. It fires or it does not, and PASS is absent from the vocabulary rather than disambiguated by a column beside it.

Value What happened
NOT_TRIGGERED it ran, the condition did not obtain, the claim survives it
FIRED the condition obtained. The claim is refuted, and that is the result
NOT_RESOLVED it ran and the ordering is genuinely undetermined, because the two quantities' intervals overlap
NOT_APPLICABLE void by construction, so it is evidence for nothing and is not a survivor
NOT_RUN_HERE measured elsewhere, or not yet. The detail says where

Collapsing the last three loses the survivor accounting, and burns the word a real tie needs. The last two never ran, so they carry no warrant, and CheckReport enforces that.

The rest

Tier says what the check was measured against. EXACT against a closed form, BOUNDED against a stated bar, COMPUTED where there is no bar to state. It cuts across the other two rather than ranking them.

CheckReport is what a check emits. Frozen, because editing a report after the check ran is editing the finding. It refuses PROVED with nothing behind it.

Evidence comes in two families, one per decisive prover. CompletenessEvidence backs an exhaustive enumeration, recording the domain it covered against the count it visited. Its leaves differ only in the shape of that domain: CompletenessCertificate over a tree, |A|^H on one versioned action set, and ProductCompletenessCertificate over a cross, the product of declared AxisDeclaration axes. A bare count separates neither, since 81 is 9**2 and 3**4 and 12 is 3 x 4 and 2 x 6, so both carry their factors and their versions. SymbolicReduction backs a theorem or a symbolic identity, and names where the symbolic setup was checked by hand against the analytic problem it stands for. A CAS establishes that one expression equals another. Whether those are the right expressions is a human obligation, and this is where it is discharged rather than assumed.

check_summary prints a run as counts per (warrant, outcome).

from warrantlib import (
    CheckReport, Outcome, Provenance, SymbolicReduction, Tier, Warrant, check_summary,
)

report = CheckReport(
    name="second gap coefficient",
    check_id="gap_series.c2_closed_form",
    warrant=Warrant.PROVED,
    outcome=Outcome.NOT_TRIGGERED,
    tier=Tier.EXACT,
    detail="the CAS reduces the integral to the quoted constant",
    evidence=(
        SymbolicReduction(
            claim="the second gap coefficient equals the quoted constant",
            correspondence="hand derivation, section 3",
            assumptions=("the expansion is formal, not convergent",),
        ),
    ),
    provenance=(
        Provenance(
            registered_at="a76cf1b",
            measured_at="9baaa22",
            registered="the coefficient's closed form, registered 2026-08-07",
        ),
    ),
)

print(check_summary([report]))
1 registered, 1 tested here, none fired
   PROVED        NOT TRIGGERED   1

Registering four falsifiers and testing two is a different claim from testing four, and one number cannot carry both. The header separates them.

Two names per check

A report carries prose and a key, and they do different jobs.

name reads in a summary line, so it is reworded whenever the wording improves. check_id is what a manifest declares before a run and what joins one run's report to the next: dot-separated segments of letters, digits and underscores, refused at construction if anything else appears in it.

Deriving the key from the prose ties the two together, and the first reworded name then reads as one check dropped and one added, which is the reading a comparison exists to rule out.

Provenance

Evidence says a claim was decided. It does not say when the bar was set, and a bar chosen after the number is visible decides nothing. Provenance is the pointer a reviewer follows: the ref where the prediction or the derivation was registered, the ref whose tree produced the number, and one line saying what they will find at the first.

A ref is a git commit SHA, an http(s) URL or a DOI. A path, a branch, a tag and HEAD are refused, because each resolves to a different tree every time it is read. A PROVED report requires one, on the same terms as it requires evidence.

Where the two refs name one commit, the render says the ordering is not established by history. That is not a failure. It is what happens whenever a check and the derivation behind it land together, and the marker keeps it from reading as something a reviewer could verify.

The type compares refs. It cannot order them, so a registration written after the fact still renders without a marker. That is a git merge-base --is-ancestor away, which is why the refs are refs.

Writing a run down

A run that prints and exits leaves nothing to compare against. report_to_dict writes one report as a JSON-ready mapping, and report_from_dict reads one back.

from warrantlib import SCHEMA_VERSION, report_from_dict, report_to_dict

record = report_to_dict(report)
assert report_from_dict(record) == report

Key order is fixed rather than taken from the input, so two runs of one suite produce the same bytes and a diff shows what changed rather than what moved. Enums travel as their values, which is why those values are words.

Reading goes through the constructor rather than around it, so every precondition still applies. A record naming PROVED with its evidence stripped does not construct. A record from a schema version this one does not know, an evidence kind with no class behind it, or an enum value no member carries is refused rather than resolved to whatever fits.

Nothing here touches a filesystem. The caller decides where the bytes go. A JSON Schema for the record ships beside the code at warrantlib/report.schema.json, for a consumer reading a ledger without Python.

Where it comes from

warrantlib was factored out of cpomdp, where it labels a research programme's falsification battery. It is developed in that repository and released separately. The API reference is at cpomdp.inferogenesis.com/api/warrant.

Release files for warrantlib 0.3.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 warrantlib 0.3.0
File Size Uploaded
warrantlib-0.3.0.tar.gz 35.5 kB Details

Built distribution (wheel)

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

Total release size: 75.4 kB

Release files / warrantlib-0.3.0.tar.gz

Download URL warrantlib-0.3.0.tar.gz
Size 35.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6b73166a4dcc6f25959c0b49a5d4b4aefaa35d7eb8a98ac7e188efca84750a78
BLAKE2b-256 checksum
How to use checksums
d209c423b0d3affa7aa539fb7040acedc171bc203239f126b3a4c7a7165935c3
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 Aug 26, 2026.

Transparency log

Release files / warrantlib-0.3.0-py3-none-any.whl

Download URL warrantlib-0.3.0-py3-none-any.whl
Size 39.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
af1b60169cbe94c9534cfe8433807fd4c8db6f0f0a26f1b969b8e1ab9f1a3596
BLAKE2b-256 checksum
How to use checksums
540db41b677225afc4ad65f92effc31a0c8cb58a3dc0548716e6038b891f7a75
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 Aug 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

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