Dankaro Agent Quality Harness
The harness evaluates the result of an AI agent before that result is released. It treats the following as release failures that can be measured: claims without sources, comparisons without a declared baseline, evidence that cites itself, excessive complexity, results that change between runs, contradictions and missing deliverables.
The central design rule is that a filled field proves nothing on its own. A source field that contains text is not evidence that the text points to anything a reader can check.
Gates
| Gate | The release is blocked when |
|---|---|
contract |
the result does not match the typed task and result contract |
outcome |
a required deliverable is missing, or a placeholder remains in the output |
evidence |
a factual claim has no source that a reader can follow |
provenance |
the evidence is circular, or a recommendation rests on facts that were not checked in this run |
baseline |
a comparison names no baseline, or does not include a naive one |
statistics |
an effect has no sample size, or its significance rests on a single test |
simplicity |
the number of controls, decisions or setup steps exceeds its limit |
explainability |
an interpreted claim gives no reasoning |
contradiction |
one key holds conflicting values, either in this run or in the claim ledger |
stability |
repeated runs give different values for the same key |
adversarial |
a deterministic critic or a registered reviewer raises an objection |
By default the gates are conjunctive: every gate must pass on its own, or the release is blocked. The harness also reports a weighted overall score so that you can follow a project over time, but that score cannot block a release by itself. A release should only fail for a reason that a specific gate names. To let the overall score block as well, set conjunctive = false in thresholds.toml.
Install
pip install dankaro-agent-quality
The package has no dependencies beyond the Python standard library and needs Python 3.11 or later.
Command line
dankaro-quality --version
dankaro-quality init # write thresholds.toml to edit
dankaro-quality evaluate result.json # full report in Markdown
dankaro-quality evaluate result.json --format json --out reports
dankaro-quality validate a.json b.json # one line per file
evaluate and validate exit with 0 when every result passes and 2 when any result is blocked. The thresholds come from the file named with --settings; without that option, from ./thresholds.toml if it exists; otherwise from the defaults shipped with the package.
Working on the harness
python -m pip install -e .
python -m unittest discover -s tests -v
dankaro-quality evaluate fixtures/passing.json
What counts as a source
The evidence gate requires a locator, not just a string. A source such as x or trust me is blocked. The following forms are accepted:
https://example.com/report measurement://prototype/workflow-v2
runs/2026-08-06/RUN_REPORT.md:24 docs/report.md
$ pytest -q tests/test_pipeline.py Companies House 05662277
sheet 12 a1b2c3d (commit)
A source that refers to the agent's own earlier output, such as "our earlier draft" or "as stated above", is rejected as circular by both the evidence gate and the provenance gate. A number in an earlier draft is not a source. It exists only because someone wrote it down before.
The baseline rule
A comparison must state what it was compared against, and at least one comparison must be against a naive baseline. Examples of naive baselines are uniform rotation, the base rate, the hand set prior, an empty field, the most recent item and the most popular item.
AgentResult(
output="The scheduler outperforms the alternative.",
comparisons=(
Comparison(
subject="thompson scheduler",
baseline="uniform rotation",
baseline_kind=BaselineKind.NAIVE,
metric="fund multiple",
effect="+3.94x",
sample_size=40,
tests=("paired t", "Wilcoxon signed-rank", "sign"),
),
),
)
This gate exists because of a measured case. In a simulation, a policy compared only against a plausible alternative won by 2.08 times (t = 2.08). When a third policy was added that read no input and rotated uniformly, the first policy never beat it at any coverage level tested: +0.66 (p = 0.23), −0.69 (p = 0.52) and +0.64 (p = 0.58). The original comparison was not wrong. It was incomplete, and the missing baseline reversed its meaning.
The claims contract
Every substantive statement in a result is a Claim.
- A
factneeds a source that resolves, aconfidencevalue and averifiedflag. - An
inference, ajudgmentor arecommendationneeds arationale.
The gates also read these fields:
| Field | Meaning |
|---|---|
key and value |
The unit that can be compared. The contradiction and stability gates need it |
verified |
True only if the fact was measured in this run. False means it was recalled or inherited |
id and supersedes |
The claim's identity, and the earlier claim it explicitly replaces |
sample_size and tests |
The data and the tests behind a statistical claim |
The verified flag separates "I checked this" from "this is how it usually works". Recalled facts are allowed, but they cannot make up most of the evidence, and they cannot be the only support for a recommendation.
Stability measures content, not wording
The stability gate compares keyed claims across repeated runs. The same key with a different value counts as drift and blocks the release. The same key and value in different words does not count as drift. A result that contains no keyed claims is reported as not comparable, rather than as stable, because a run that cannot be measured has not passed.
The claim ledger across runs
Checking for contradictions inside one result does not catch the failure that happens in practice: a claim disproved in March reappears in a June report, because the June run never saw the correction. To catch it, point the harness at a claim ledger that persists between runs:
[global]
claim_ledger = "memory/claims.jsonl" # leave empty to search upwards for memory/claims.jsonl
The ledger holds one JSON object per line. Each object has a status of live or demoted, and may have a watch regular expression:
{"id":"CLM-1","text":"Thompson beats greedy by 2.08x","status":"demoted",
"reason":"Never beats round-robin; the gain belongs to breadth",
"demoted_by":"review/verify_baseline.py","watch":"2\\.08x"}
A run whose output matches the watch pattern of a demoted claim is blocked. If the new result names that claim in supersedes, it is allowed through, because an explicit replacement is legitimate. A missing or malformed ledger is treated as empty and never causes an error.
Simplicity limits
AgentResult(..., exposed_controls=5, required_decisions=3, configuration_steps=1)
The limits are set in thresholds.toml: by default at most 7 exposed controls, 5 required decisions and 3 configuration steps. Keeping them in a settings file makes simplicity an engineering constraint that the team agrees once, rather than a design opinion that is argued again in every sprint.
Hooks and adversaries
hooks = HookRegistry()
@hooks.add_pre_run
def require_owner(task):
return [] if task.metadata.get("owner") else [Finding("OWNER", "Task owner missing")]
An error from a hook that runs before the agent prevents the agent from running. Hooks that run afterwards, and adversaries, contribute to the release decision. Model based review should stay secondary to the deterministic checks: a critic model may point out a problem, but it should not be the only authority on evidence, required deliverables or numeric thresholds.
Enforcing the gates with a decorator
@quality_gated(harness, repeats=3)
def product_agent(task):
return AgentResult(...)
A blocked run raises RuntimeError. The full structured report is available on exception.quality_report.
Regression fixtures
Each file in fixtures/regression/ records a case that an earlier version of the harness passed when it should have blocked it. If one of these fixtures starts to pass, a gate has been weakened.
| Fixture | The case it records |
|---|---|
junk_source.json |
The source x once scored 100 on the evidence gate |
circular_source.json |
An agent cited its own earlier draft as evidence |
no_baseline.json |
A claimed win with no declared comparison |
single_test.json |
Significance that rested on a single test |
Repository map
src/dankaro_quality/
├── contracts.py task, result, claim and comparison types
├── sources.py rules for what counts as a source
├── ledger.py the claim ledger that persists between runs
├── harness.py orchestration and the release decision
├── hooks.py hooks before and after a run, and adversaries
├── adversaries.py deterministic critics
├── reporting.py JSON and Markdown reports
├── serialization.py
├── cli.py
└── evaluators/ one module for each gate
Release files for dankaro-agent-quality 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dankaro_agent_quality-0.1.0.tar.gz | 29.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dankaro_agent_quality-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 65.7 kB
Release files / dankaro_agent_quality-0.1.0.tar.gz
| Download URL | dankaro_agent_quality-0.1.0.tar.gz |
|---|---|
| Size | 29.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
df6bf5fcac9068540d3fe08fb8f8fa89653f4ffc3ea3cf2d5d0a1c0f3319ced9
|
|
BLAKE2b-256 checksum How to use checksums |
8afb273b88f26a71fdec6581296a8c84e833aa92e51aca29f3875dfc69afcec1
|
| 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 Sep 18, 2026.
Transparency logRelease files / dankaro_agent_quality-0.1.0-py3-none-any.whl
| Download URL | dankaro_agent_quality-0.1.0-py3-none-any.whl |
|---|---|
| Size | 36.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
21bf1147766603a8e34a782684b986057b0370d464ee3bc4a2f827186634aa80
|
|
BLAKE2b-256 checksum How to use checksums |
946786b437d3123af57df9c5f57520663b54c22dea104fde8756b4d8f1254a22
|
| 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 Sep 18, 2026.
Transparency log