Skip to main content

Turn GitHub issues into repeatable, sandbox-verified failing pytest tests.

Project description

ReproAssert

The test before the fix.

CI License: MIT Python 3.10+

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.patch adds only tests/reproassert/test_issue_NUMBER.py.
  • reproassert-report.json records 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


Download files

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

Source Distribution

reproassert-0.2.3.tar.gz (951.2 kB view details)

Uploaded Source

Built Distribution

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

reproassert-0.2.3-py3-none-any.whl (634.9 kB view details)

Uploaded Python 3

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

Hashes for reproassert-0.2.3.tar.gz
Algorithm Hash digest
SHA256 e67d6ea5f596128ef6578184dc8fec40fa3ff6780acf1e1f38ef8d14f4bab577
MD5 dfa66bbe6f3e04ba6e7609b84c2bb1a7
BLAKE2b-256 589758f03acf349bc3cab82aa436b2dd5c17e3bb0f4496c2420c43fc9208f382

See more details on using hashes here.

Provenance

The following attestation bundles were made for reproassert-0.2.3.tar.gz:

Publisher: release.yml on Atomics-hub/reproassert

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

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

Hashes for reproassert-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 1c4681269ead1e52e52846d08de90ae8f7c35d7ac21f8895ae26526e4a567b8a
MD5 27e587c1d9f249175a9f14de3aa5fb7e
BLAKE2b-256 7cd809ce0571ffe406a95ab2a5356d4cb6715fc32b33bebb33caed833efd05f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for reproassert-0.2.3-py3-none-any.whl:

Publisher: release.yml on Atomics-hub/reproassert

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page