Turn GitHub issues into repeatable, sandbox-verified failing pytest tests.
Project description
ReproAssert
The test before the fix.
ReproAssert turns a public GitHub issue into a candidate pytest regression test, then proves that the test collects and fails consistently on the exact buggy commit inside a locked-down Docker sandbox. It produces the test patch, a machine-readable evidence report, and one-command replay before anyone tries to fix the bug.
See it work
You need Python 3.10+, uv, and a running Docker Engine or Docker Desktop. This command uses ReproAssert's public issue #1 and pinned buggy commit. It needs no API key, makes no model call, and builds the hash-locked verifier image automatically on first use.
uvx reproassert demo
The proof ends with artifacts you can inspect and replay:
claim repeatable_base_failure
outcome repeatable_base_failure
patch .../candidate.patch
report .../reproassert-report.json
replay reproassert replay .../reproassert-report.json
GitHub issue + exact commit
|
v
candidate.patch + one-command replay + reproassert-report.json
It never edits production code. It never silently falls back to running repository code on your host. Its strongest public CLI claim is deliberately narrow: this test produced the same issue-marked failure on the pinned base revision across repeated sandboxed runs.
Install and use
You need Python 3.10+, uv, and Docker Engine or Docker Desktop.
uv tool install reproassert
For a current issue, HEAD is resolved and recorded as an exact 40-character SHA. Provider use is
always explicit:
export OPENAI_API_KEY="..."
reproassert issue https://github.com/OWNER/REPOSITORY/issues/123 --provider openai
For a historical issue, pass the known buggy revision yourself; ReproAssert does not guess history:
reproassert issue https://github.com/OWNER/REPOSITORY/issues/123 \
--commit <buggy-commit-sha> \
--provider openai
That result means the candidate collected and produced a stable, expected failure on the exact base SHA. It does not mean the test passes on a fix, captures the issue's true semantics, or has been accepted by a maintainer.
Without uv
python3 -m venv .venv
. .venv/bin/activate
python -m pip install reproassert
reproassert demo
The controller supports macOS and Linux. WSL is treated as Linux but is not yet independently verified. Native Windows execution and Windows containers are unsupported. There is no native execution fallback.
Choose how the test is created
Every run requires exactly one candidate source.
A trusted generator adapter
reproassert issue https://github.com/OWNER/REPOSITORY/issues/123 \
--commit <buggy-commit-sha> \
--generator-command ./your-trusted-adapter \
--pass-env PROVIDER_API_KEY
The adapter is a program you trust. ReproAssert sends it a bounded JSON request on stdin and expects
one JSON object on stdout containing test_content, expected_symptom, and rationale. It receives
untrusted issue and repository text, so it must keep those inputs in the data plane—not interpret
them as commands. Only environment variables named with --pass-env are forwarded.
See the working offline deterministic adapter and the architecture for the protocol and trust boundary.
Generator protocol response
{
"test_content": "def test_issue_123_reproduction():\n assert observed == expected, 'duplicate separators remain'\n",
"expected_symptom": "duplicate separators remain",
"rationale": "Exercises the user-visible normalization invariant."
}
The adapter must emit exactly those three string fields. The expected symptom must appear literally in the test, normally as its assertion message. Output is capped at 64 KiB and execution at 300 seconds. The command runs directly without shell expansion.
A human-written test
For issue 123, create one synchronous test named test_issue_123_reproduction:
from your_package import normalize
def test_issue_123_reproduction() -> None:
observed = normalize("Alpha Beta")
assert observed == "alpha-beta", "duplicate separators remain"
Then verify it through the same policy and Docker boundary:
reproassert issue https://github.com/OWNER/REPOSITORY/issues/123 \
--commit <buggy-commit-sha> \
--candidate-file ./candidate.py \
--expected-symptom "duplicate separators remain" \
--rationale "Exercises adjacent-space normalization through the public function."
The built-in OpenAI adapter (explicit opt-in)
export OPENAI_API_KEY="..."
reproassert issue https://github.com/OWNER/REPOSITORY/issues/123 \
--commit <buggy-commit-sha> \
--provider openai
The provider is never selected merely because an API key exists. The default model is
gpt-5.4-mini; use --model MODEL to choose another. This sends bounded public issue and selected
source context to https://api.openai.com/v1/responses and may incur charges on your account.
ReproAssert requests store: false, makes no automatic retries, and caps request, response, and
output sizes. Review your provider's data policy before use; the source-context filter is not a
data-loss-prevention system.
What ReproAssert verifies
The first product profile is intentionally Python + pytest:
| Stage | Required evidence |
|---|---|
| Source | Canonical public issue, exact 40-character commit SHA, and files that reconstruct the commit's Git root tree |
| Candidate | One new synchronous pytest test, no production edits, strict static policy, at most 32 KiB |
| Collection | The candidate collects successfully inside Docker |
| Execution | It fails 2-10 times (default 3) with the expected symptom and a stable failure fingerprint |
| Boundary | No network, read-only root and workspace, non-root user, dropped capabilities, bounded CPU, memory, PIDs, time, and output |
| Result | A test-only patch, replay command, and machine-readable evidence report |
Syntax errors, collection/import/setup failures, missing dependencies, generic crashes, timeouts,
unrelated failures, and inconsistent one-off failures are rejected rather than counted as
reproductions. Ordinary issue runs do not install project dependencies; the current wedge is best
suited to repositories whose test environment is already self-contained under the strict profile.
The claim ladder stays explicit:
rejected -> collected -> repeatable_base_failure [public CLI ceiling]
|
+-> differential_reproduction [capability-gated evaluation]
+-> maintainer_validated [external evidence only]
Inspect and replay the evidence
Run artifacts live under $XDG_STATE_HOME/reproassert/runs or
~/.local/state/reproassert/runs. Choose another controller-owned directory with --run-base.
candidate.patchadds onlytests/reproassert/test_issue_NUMBER.py.reproassert-report.jsonrecords issue and source provenance, the candidate and its digest, Docker policy and immutable image ID, collection and rerun outcomes, bounded logs, failure fingerprint, artifact hashes, and explicit limitations.
Replay a report with controller-owned commands:
reproassert replay ~/.local/state/reproassert/runs/issue-.../reproassert-report.json
Replay reacquires and verifies the exact source, regenerates safe pytest arguments, and creates a new report. It does not execute command-looking fields from the original report. A successful replay is fresh bounded evidence—not semantic proof.
Print the exact report schema bundled with the installed controller without a network request:
reproassert schema
The published schema is also available at
reproassert-report.schema.json.
Security model
Repository code, issue text, source files, dependencies, generated tests, pytest results, and imported reports are untrusted. The verifier receives no host secrets, SSH agent, browser state, cloud credentials, proxy variables, Docker socket, or unrelated host directories. Archive paths and types are checked, accepted source files must reconstruct the pinned Git tree, and repository code runs only inside Docker with the recorded restrictions.
Residual risk remains. Docker shares the Linux host kernel or Docker Desktop VM; hostile pytest
code may try to forge in-process result detail; and a user-selected generator adapter is a trusted
host process. Treat repeatable_base_failure as bounded evidence, not proof.
Before running an unfamiliar repository, read the security model, threat model, and sandbox profiles. Report vulnerabilities through GitHub's private process described in SECURITY.md, not a public issue.
Benchmark status
Measured results remain intentionally separate from product capability.
- v0.1 is immutable at 0/20 because of its provenance erratum.
- v0.2 freezes 20 leak-audited cases from pinned upstream data and independently attests the parser boundary and exact Git object graph.
- The v0.2.1 campaign evaluated the complete 20/20 denominator and accepted 0/20 as L1 deterministic reproductions. Seventeen outputs failed the frozen candidate contract; three reached six-run Docker evaluation but their JUnit files were lost by an evaluator transport bug, so they correctly failed closed without an attributable fingerprint. The v0.2.1 result remains immutable and is not retroactively upgraded.
- The frozen OpenAI run made exactly 20 calls for $0.688111 total, with a maximum case cost of $0.051351, under the approved $5 total / $0.25 per-case zero-overage limits. Cost per success is undefined because there were no successes.
- The result has 0 L2 claims, 0 human reviews, 0 maintainer validations, and no outreach. It misses the preregistered 6/20 continuation gate and is evidence against claiming benchmark-ready accuracy for the current generation profile.
The v0.2 preparation and evaluation machinery is default-deny: source, dependency, hidden-fix,
request, pricing, authorization, causal-control, and reviewer commitments must be bound before a
provider-capable run. Public issue creation now precedes the fixing artifact for 20/20 cases under a
hash-bound chronology receipt. The dataset title/body snapshots remain
historical_public_contamination_exposed because full pre-fix revision capture is unavailable; no
historical-cleanliness claim is implied.
The executed v0.2.1 path was fail-closed on spend: it reserved each case before inference, persisted provider response cost before completion, disallowed overage, and resumed only from hash-bound durable receipts. The public result bundle contains the aggregate and redacted per-case evidence; provider responses, hidden fixes, credentials, and private evaluator paths are not published.
See the v0.2 protocol, evaluation model, and market-validation gates. The failed 6/20 continuation gate means the next work is generator/contract diagnosis, not a larger paid run or a performance claim.
Development
git clone https://github.com/Atomics-hub/reproassert.git
cd reproassert
uv sync
uv run pytest
uv run ruff check .
uv run mypy src
Start with CONTRIBUTING.md. The deeper product contract lives in:
ReproAssert is alpha software available under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reproassert-0.2.3.tar.gz.
File metadata
- Download URL: reproassert-0.2.3.tar.gz
- Upload date:
- Size: 951.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e67d6ea5f596128ef6578184dc8fec40fa3ff6780acf1e1f38ef8d14f4bab577
|
|
| MD5 |
dfa66bbe6f3e04ba6e7609b84c2bb1a7
|
|
| BLAKE2b-256 |
589758f03acf349bc3cab82aa436b2dd5c17e3bb0f4496c2420c43fc9208f382
|
Provenance
The following attestation bundles were made for reproassert-0.2.3.tar.gz:
Publisher:
release.yml on Atomics-hub/reproassert
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reproassert-0.2.3.tar.gz -
Subject digest:
e67d6ea5f596128ef6578184dc8fec40fa3ff6780acf1e1f38ef8d14f4bab577 - Sigstore transparency entry: 2155382686
- Sigstore integration time:
-
Permalink:
Atomics-hub/reproassert@6a6e78028bef8c3366063a7f399206d6c8468a02 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/Atomics-hub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a6e78028bef8c3366063a7f399206d6c8468a02 -
Trigger Event:
push
-
Statement type:
File details
Details for the file reproassert-0.2.3-py3-none-any.whl.
File metadata
- Download URL: reproassert-0.2.3-py3-none-any.whl
- Upload date:
- Size: 634.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c4681269ead1e52e52846d08de90ae8f7c35d7ac21f8895ae26526e4a567b8a
|
|
| MD5 |
27e587c1d9f249175a9f14de3aa5fb7e
|
|
| BLAKE2b-256 |
7cd809ce0571ffe406a95ab2a5356d4cb6715fc32b33bebb33caed833efd05f6
|
Provenance
The following attestation bundles were made for reproassert-0.2.3-py3-none-any.whl:
Publisher:
release.yml on Atomics-hub/reproassert
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reproassert-0.2.3-py3-none-any.whl -
Subject digest:
1c4681269ead1e52e52846d08de90ae8f7c35d7ac21f8895ae26526e4a567b8a - Sigstore transparency entry: 2155382899
- Sigstore integration time:
-
Permalink:
Atomics-hub/reproassert@6a6e78028bef8c3366063a7f399206d6c8468a02 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/Atomics-hub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a6e78028bef8c3366063a7f399206d6c8468a02 -
Trigger Event:
push
-
Statement type: