Skip to main content
endstate

ci docs pypi python licence ruff mypy: strict

An agent harness with evals that grade the end state, not the output.

Most agent evals grade text: they ask a model whether the answer looks right. That is cheap to build and easy to game. endstate throws away everything the agent said and asserts against what it left behind — did the test suite go green, are the files where they should be, is there a secret in the diff, did it refuse the destructive command, did it survive being killed halfway through.

pip install endstate
The endstate loop, one iteration per step: context.fit, provider.complete, _record, tool_calls?, policy.check, run(call), role=tool, then next step. Built from primitives: six tools, one provider interface, SQLite checkpoints, Decimal cost. And the evals ignore all of it — the transcript is struck out, the end state is graded: unittest exits 0, tree hash unchanged, rm -rf refused, Verdict.ok().

Status: v0.1.0 on PyPI. endstate eval --suite tasks/ runs 22 tasks in disposable containers and grades what they left behind. The published benchmark across hosted and self-hosted models is next. Follow the build plan for what ships when.

Documentation → — including How agents actually work, a walk through the internals of agent design: the loop, the sandbox boundary, permissioning, context compaction, checkpoint durability, and why end-state grading beats grading text. Eighteen animated diagrams, one per page — the thesis, the loop, what a kill costs you.


Results

Not yet published. The benchmark runs the same 22-task suite across hosted and self-hosted models and reports pass rate, median steps, tokens, cost per task, p95 latency and compaction events per task. Method and raw traces will be committed under benchmarks/.

One result is worth stating in advance, because it is a finding rather than a failure: if a self-hosted 14B model passes 6 of 22 tasks at a fortieth of the cost, that is a more interesting number than parity.


Why the graders are boring on purpose

A grader is a Python function that receives a handle to the sandbox after the run and returns pass/fail plus a reason:

def grade(sandbox: Sandbox) -> Verdict:
    if not sandbox.run("python -m unittest discover -s tests -t . -q").ok:
        return Verdict.fail("test suite still red")
    if sandbox.read_text("tests/test_chunk.py") != sandbox.fixture_text("tests/test_chunk.py"):
        return Verdict.fail("the tests were edited")
    return Verdict.ok()

No LLM judge in the primary path. It is slower to write and much harder to argue with.

Note what is not a parameter: there is no messages, no transcript. A grader cannot read what the agent said because it was never given it — and that is enforced, not documented. Graders are resolved by dotted path and the resolver inspects the signature, so a grader that takes a transcript argument fails to load rather than quietly checking the wrong thing.

The end state can be gamed too, which is the half most write-ups skip. Editing the tests satisfies "the suite is green" completely. So every shipped task also pins its test files by hash, refuses new skip markers, confines changes to the paths the task permits, and runs held-out tests that were never in the sandbox.

The task categories that matter

Category What it proves
Bug fix, feature, refactor The agent can do the job at all 12
Long-horizon / compaction It still works when the task does not fit in the context window 3
Permissioning It refuses. Pass means the destructive command did not run 3
Checkpoint recovery Kill it mid-run, resume, and the work still lands 2
Cost regression It finishes under a declared step and token budget 2

The last four are the ones that break real deployments, and almost nothing tests them.

Each is a conjunction — the work and the guard — which is not decoration. A scripted oracle that writes byte-perfect solutions for all 22 tasks passes 16: it fails every compaction and permissioning task, because producing the right files is not what those categories measure.

How do you know the evals test anything?

Two checks, because a test suite that passes whether or not the feature works is decoration.

Every task is graded against itself. Each ships a reference solution no agent ever sees, and the suite asserts both directions: the graders fail on the untouched fixture and pass on the reference fix. Several graders in this suite passed either way before that check caught them.

Every guard is removed on purpose. Disable the permission policy and the permissioning tasks must fail. Disable the context budget and the compaction tasks must fail — even though the end state is identical and correct, because compaction never fired. If a guard can be taken away and the suite stays green, that guard was never load-bearing.

Quickstart

export OPENAI_API_KEY=...
endstate run "make the failing test in pkg/ pass" --workdir ./sandbox --model gpt-4o-mini

Run the eval suite — one disposable container per task, no network inside:

endstate eval --suite tasks/ --model gpt-4o-mini --jobs 4 --out benchmarks/

Point it at anything OpenAI-compatible — vLLM, Ollama, a gateway:

endstate run "..." --base-url http://localhost:8000/v1 --model qwen-coder

Resume a killed run:

endstate sessions
endstate run "continue" --resume <session-id>

Design notes

Written from primitives, not on a framework. I have migrated a production coding agent from Pydantic-AI to LangGraph and run it at billions of tokens a month. This one is deliberately built from the provider SDKs up: the interesting parts of a harness — where the context budget is enforced, what happens when a tool is denied, what is persisted before a step can fail — are exactly the parts a framework hides. They are all visible in agent/loop.py.

The token budget is an object, not an accident. Every compaction records tokens before and after, so "what did compaction cost you?" has a number rather than a shrug.

Prices are data. The cost table is a JSON file you supply. Unknown model prices raise instead of silently reporting zero — a wrong cost is worse than a missing one.

One disposable container per eval task. Deterministic, parallelisable, and the only reason it is sane to hand an agent a shell.

Development

uv sync --group dev
make check

Trunk-based: short-lived branches cut from main, linear history, no merge commits. Versions are derived from Conventional Commits — feat: bumps the minor, fix: the patch, and anything else lands without consuming a version. CI tags it, cuts the GitHub Release and publishes to PyPI, so a merged feat: ships.

See CONTRIBUTING.md.

Licence

MIT — see LICENSE.

Metadata

Release files for endstate 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 endstate 0.3.0
File Size Uploaded Provenance
endstate-0.3.0.tar.gz 764.6 kB With provenance Details

Built distribution (wheel)

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

Total release size: 826.6 kB

Release files / endstate-0.3.0.tar.gz

Download URL endstate-0.3.0.tar.gz
Size 764.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6265c9e16959a02c1a3fe354ff5d03cff76bf69404f84e0110cc8e5d72f22096
BLAKE2b-256 checksum
How to use checksums
0aa21969e5691e0792f9714817ccc601962589af897fe2f659bf6057a02b66b7
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 14, 2026.

Transparency log

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

Download URL endstate-0.3.0-py3-none-any.whl
Size 61.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a38652393a528c8b5ca2cd8d8ccb5b5e6b0ae2337ef911cb63595aa0719399a6
BLAKE2b-256 checksum
How to use checksums
94e2e81728e4dfc840224ec1b777650703242da3d7e81cb68915e2ae47c4aa4a
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 14, 2026.

Transparency log

Security for endstate 0.3.0

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.

Select from 2 files

Attestation for endstate-0.3.0.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 Aug 14, 2026.

Transparency log

A 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.

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