Skip to main content

assurance-core

PyPI Tests Python License

Every tool call returned 200. The answer was still built on two thirds of the data.

Your traces are green. Your groundedness check passes — every sentence maps to something in the context. And the context was missing four of the nine documents the question actually spanned.

Groundedness checks the output against the input. Nothing in your stack checks the input against the question. That is the gap this library closes, in arithmetic, with no model involved anywhere.

pip install assurance-core

Zero dependencies. Python 3.10+. Nothing is pulled in and nothing phones home.

from assurance_core.coverage import Coverage

coverage = Coverage.of(
    expected=["msa.md", "amendment-1.md", "amendment-2.md", "amendment-3.md"],  # what the question spans
    found=["msa.md", "amendment-1.md"],                                          # what your retriever returned
    scope_label="documents this question spans",
    where="the retrieved set",
)

coverage.complete          # False
coverage.summary()         # '2 of 4 documents this question spans —
                           #  not in the retrieved set: amendment-2.md, amendment-3.md'

Two amendments never reached the model. The answer it writes will be fluent, fully grounded in everything it was given, and wrong — because the terms changed in amendment 3. No eval score catches that. The ratio does.

Does this apply to what you're building?

If you can name what a task was supposed to cover, this tells you what it actually covered.

If you build The question this answers
RAG / retrieval Did the retriever return every document the question spans, or just the top k?
Agentic code review Did the agent open all 40 changed files, or 31 of them?
Document / contract analysis Were all the amendments read, or only the master agreement?
Compliance & audit evidence Which controls have evidence, and why do the rest not?
Data pipelines Are all 24 hourly partitions there, or did the listing cap at 24?
Batch / ETL agents Did the run process every record it declared, or every record it managed?
Eval harnesses Did the suite execute all 200 declared cases, or the 180 that didn't time out?
Migrations & translations Every table, every string — or the ones that didn't error?

Six runnable examples in examples/, each one a different industry's version of the same question. Run them; they are held green by CI.

01_did_it_read_everything.py                    the two-year trend built on 22 months
02_is_this_document_still_true.py               the report whose source changed on Thursday
03_did_the_retriever_see_the_whole_question.py  top-k is a budget, not an answer
04_which_controls_actually_have_evidence.py     "18 of 20" is not one fact, it is six
05_review_gate_for_an_agent.py                  a CI gate that fails an incomplete review
06_a_capped_listing_is_worse_than_no_number.py  "24 of 24" from a listing that stopped at 24

Six ways an expectation fails to be evidence, and they are not the same fact

Most tools give you one missing bucket. That bucket destroys the only information a person needs, which is what to do next — and these send five different people in five different directions:

Outcome What it means Who acts
missing Expected, and nothing matched it Chase the owner
gone A tombstone says it was here That is an incident, not a gap
ambiguous Two candidates, never resolved by picking A human names the real one
unreadable Present, and nothing legible came out Untested, not absent
unauthorized Present, and this principal may not see it Escalate the task, never the answer
truncated The enumeration hit a cap, so the denominator is wrong Fix the listing before trusting any ratio

That last one is the subtle one. A capped listing reporting "24 of 24 — complete" is worse than no number at all, because it is confidently wrong. So truncated makes complete false on its own: we do not know what we did not see is not nothing.

The vocabulary also refuses to state conclusions it cannot support. It says "not in this folder", never "missing" — the first is a fact about a directory listing, the second is an inference about the world.

Coverage.of derives the gap. It does not trust you to hand it over

The plain constructor takes missing as an argument, which means a caller who forgets it gets complete is True on a record that read eleven of twelve things. That is the exact successful-looking wrong answer this library exists to prevent, produced by the library's own API. It was found on 2026-08-29 and fixed in 0.3.0 three ways:

  • Coverage.of(...) computes the difference, so the common path cannot lie
  • unaccounted — an expectation that landed in none of the six outcomes now blocks complete, and says so in the sentence, so a hand-built record cannot pass as a clean one
  • read counts the intersection, not len(found) — evidence from outside the declared scope is recorded and earns no credit against the denominator

If you are on 0.2.x and building Coverage(...) directly, move to Coverage.of(...).

What this is not

  • Not a runtime, an agent framework, or anything that does something on its own
  • Not an orchestrator, not capabilities, not services, not UI, not a database
  • Not a fork that will drift — changes are made in I-Ops and copied out deliberately

Provenance: cut from I-Ops 0.56.2. I-Ops is upstream; this repo is a publication, never a source.

The three legs

1. Declared postconditions. Before anything runs, the task contract states what done means in terms a machine can check. Each line is true or false. Partial completion is declared up front too.

2. Independent verification. A verifier reads the state of the world outside the run. The worker is never the judge. Where no verifier exists, the honest answer is complete but unverified.

3. Evidence coverage. Did the agent look at everything it was supposed to look at? Derived from the scope by code, compared against what was actually opened. A gap blocks verified completion.

Cross-verification is not verification. Four agents agreeing tells you the models agree; it does not tell you the draft exists, the file reopens, or that two months were never read.

Model independence is gated, not claimed

Every module is walked by an AST test that forbids model and service imports:

def test_coverage_never_consults_a_model():
    tree = ast.parse(Path(assurance_core.coverage.__file__).read_text())
    imported = [...]                      # every ImportFrom and Import in the module
    assert not [n for n in imported if any(t in n for t in
        ("model_source", "vinci_client", "mlx", "openai", "anthropic"))]

CI runs it on 3.10 through 3.13, then imports all sixteen modules on an installed copy and asserts nothing leaked into sys.modules. Swap the model and the prose changes; the arithmetic does not. That is what "works with any brain" has to mean if it means anything.

Modules

Module Question it answers
coverage Did the worker read everything the task required?
staleness Do recorded figures still match the source?
admission Should this source inform the answer, given provenance?
verification What does checked mean for a postcondition?
run_outcome What actually happened, derived from structured signals?
task_contract What would count as done, declared before the run?
policy · principal · worker · effects Who may have which worker produce which effect?
rule_of_two Does this session hold too many risk properties at once?
run_budget Are loop, retry and spend limits enforced by code?
report_period Which month does this request mean?
sequence Which series is this — monthly, quarterly, numbered?
semantic_checks Deterministic figure and text checks

Honest limits

A governance library that oversells itself refutes its own thesis in public.

  • verified_complete was unreachable by construction until real verifiers shipped; many conditions still have no verifier, and the vocabulary exists so the honest answer stays available
  • Source admission is provenance-only; on a corpus with no tombstones or supersession events it is inert — it admits everything with no provenance and excludes only what the record says to
  • Standing staleness compares recorded figures to a fresh recompute; it needs a prior artifact record, which this library does not provide
  • The rule of two deliberately under-counts files inside an explicitly granted workspace; a stated calibration trade-off, not an accident
  • Coverage does not derive your expected set for you. That is the caller's declaration on purpose: a denominator a tool invents is a denominator nobody can argue with

Also in this family

Run the tests

pip install -e ".[dev]" && python -m pytest -q

Licence · Contributing · Security

Apache-2.0. See LICENSE, CONTRIBUTING.md (feature requests belong upstream), and SECURITY.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

assurance_core-0.3.0.tar.gz (102.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

assurance_core-0.3.0-py3-none-any.whl (81.1 kB view details)

Uploaded Python 3

File details

Details for the file assurance_core-0.3.0.tar.gz.

File metadata

  • Download URL: assurance_core-0.3.0.tar.gz
  • Upload date:
  • Size: 102.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for assurance_core-0.3.0.tar.gz
Algorithm Hash digest
SHA256 120819818158b0966e814c43a6b46404097f4a5d41fbc5493b8c76ac013a9c39
MD5 cb4200a1cbabc9143a4fe0e9e89602a9
BLAKE2b-256 8ef9230d1fd4faf01786a9b6b4032af416bf36217bf098889f5dabec6af65920

See more details on using hashes here.

Provenance

The following attestation bundles were made for assurance_core-0.3.0.tar.gz:

Publisher: publish.yml on i-ops-hq/assurance-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file assurance_core-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: assurance_core-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 81.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for assurance_core-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 481db955964a0d27e259030d6d734c71bfef1899d0e3c5c435113cec1d7e0dcf
MD5 1d6a06b66d28b04b30a6576d3c558322
BLAKE2b-256 c5244f419f75f6e62e9cf91d9a175744da85c4c888c21c8022becc33974841b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for assurance_core-0.3.0-py3-none-any.whl:

Publisher: publish.yml on i-ops-hq/assurance-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.11.0

2 files

0.10.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 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