Skip to main content

repro-engine

The Repro engine: orchestrator, event bus, storage, sandboxed two-sided execution, behaviour tracing, diffing, policy, agents and card export. The design is in docs/ARCHITECTURE.md.

The import package is repro (src layout: engine/src/repro/). The console script is repro.

uv run repro --version
uv run repro doctor          # Python, SQLite, git, uv, Docker and the data paths (--json)
uv run repro run --repo <path> --base <ref> --head <ref>   # the cockpit shows it live
uv run repro run --pr https://github.com/<owner>/<repo>/pull/<n>  # a GitHub pull request
uv run repro replay <run id | log.jsonl>                    # from the start
uv run repro export <run id | log.jsonl> --format md|json|in-toto|sarif|junit  # the Reproduction Card
uv run repro report <run id | log.jsonl>                    # the card on its pull request
uv run repro serve           # the cockpit, the read-only API and the event stream
uv run pytest

run, replay and serve listen on 127.0.0.1:8780 (loopback only; --port, --open to open the browser, --allow-origin for a dev server) and serve the built cockpit (pnpm build; the ui_dir setting points elsewhere). run --no-serve waits for the run and exits 0 if it completed, 1 if not. Every run ends with its card (F05): export assembles it again from the log, as Markdown or canonical JSON (-o FILE to write a file), and exits 1 if it differs from the card the run published; replay reports the same check before serving. Since H04 it also writes an unsigned in-toto Statement, SARIF 2.1.0 and JUnit XML. report (I02) puts the card, checked the same way, on the GitHub pull request the run read (--pr names another): the check run Repro on its head commit, one comment that each report updates, and, in GitHub Actions, the job's summary. It runs none of the target's code; a target it cannot update is named, the others are still made, and it exits 1.

Store

repro.store keeps runs, events, evidence and artifacts in <data_dir>/repro.db (SQLite, WAL) and <data_dir>/runs/<run_id>/. StoreWriter is the only writer: it batches submitted events into transactions, gives each its run's next seq, and resolves callers' futures after the commit. StoreReader pages through a run's events read-only. ArtifactStore writes content-addressed files atomically. The schema itself enforces append-only events and evidence and gap-free seq (ARCHITECTURE §11).

Bus and orchestrator

repro.bus.EventBus is the one way events enter the store: it builds the envelope, removes recognised credentials, validates the event against its kind's generated model, and submits it to the writer; subscribers get events only after they are committed. repro.orchestrator runs a run's jobs as a DAG on daemon threads (at most concurrency at once), with per-job timeouts and cancellation; failures, timeouts and skipped jobs are recorded as fail events and missing items, and run.finished is always emitted.

Git layer

repro.gitops reads an untrusted target repository: Git runs hardened commands (long paths, no hooks, no fsmonitor), resolve and merge_base; diff lists changed files with exact paths and their changed line ranges; snapshot writes a commit's tree, byte for byte, into a .git-less directory, skipping and listing entries that are unsafe on this machine; add_worktree and remove_worktree give host-side checkouts that are always cleaned up.

Static analysis

repro.analysis reads a target's Python source with ast, never importing it. module_names names each file as Python would import it; build_table lists a module's functions, methods and classes with stable IDs (pkg.mod:Class.method#0, <module> for the module's own code); changed_symbols_between(git, base, head) gives each symbol the change touched (modified, renamed, moved, added, removed), as symbol.changed payloads, and lists the files it could not read. import_graph builds one side's module import graph (grimp for packages, in a process of its own; ast for tests and scripts outside packages), and analyse(git, base, head, workdir) returns the changed symbols, both graphs and the blast radius, with graph_payloads and blast.payload() giving their events.

Sandbox

repro.sandbox.ProcessSandbox(work, limits) runs a command at L0 to L2 (ADR-0004): run(argv, cwd=…, env=…, timeout_s=…, stop=…) returns an Execution (outcome, exit status, output, whether processes were left running, and the network, subprocess and file-write attempts the audit hook detected). detection_evidence turns detections into evidence records; ready_payload(side) is the sandbox.ready payload. Containment is a Job Object on Windows and a process group with resource limits on POSIX; the network is detected, not enforced. DockerSandbox(work, git, commit, limits=…) runs at L3: the commit in a volume of its own, each command in a hardened container with no network; read_file reads a result back.

Provisioning

repro.provision.inspect_project(snapshot) reads a project's kind (uv, poetry, pdm, requirements, setuptools) and its requirements from its files. HostProvisioner (the process sandbox) and DockerProvisioner (containers, in repro.sandbox.image's image) build the dependencies' environment from wheels, cache it, copy it for the side and install the project offline in the sandbox; Environment.payload() is env.provisioned, and a failure is a ProvisionError whose reason environment_missing turns into an environment_failed item.

Running tests

repro.runner.run_tests(sandbox, env, tests=..., seed=...) runs node IDs (or every test) with the environment's interpreter, under Repro's pytest plugin (sandbox/_hook/repro_pytest.py) and Repro's own options, whatever the project configures. It returns the execution, the plugin's results read back as untrusted input (complete only with the session record) and the JUnit XML.

Test selection

run_tests(..., map_tests=True) also records the functions each test runs (sys.monitoring); repro.runner.selection turns them into symbols (test_map), picks the tests a change can affect with their reasons (select), and lists the changed symbols no selected test runs (uncovered, no_covering_test). Base maps are cached (MapCache).

Two-sided execution

repro.runner.twosided.reproduce runs A (base), A2 (base, reverse order, another hash seed) and H (head) on the selected tests, reruns flips, runs edited tests' base versions on head, and classifies each test (outcomes) and each change to the tests (testdiff). repro run does all of it in its run and diff stages (repro.run_stage); --analyse-only stops after the build stage.

Performance

repro.runner.perf.time_tests times the tests that pass on both sides, untraced, base and head interleaved (ABBA); assess compares each: ratio of medians, BCa bootstrap interval, Mann-Whitney with Benjamini-Hochberg, a noise floor from the A/A spread, and a timing floor under which a test is not assessable. repro run does it in its perf and perf-diff jobs.

Tracing

run_tests(..., trace=targets) records every call of the target functions (TraceTarget: file, qualified name, line span) in each test: argument and result digests, exceptions, generator yields (sandbox/_hook/repro_trace.py, repro_encode.py). repro.runner.trace builds targets from C06 symbols, groups calls by target, and gives the missing items. scripts/bench_tracer.py measures the overhead.

API

repro.api.create_app serves GET /runs, /runs/{id}, /runs/{id}/evidence/{ev}, /runs/{id}/artifacts/{sha256} and /runs/{id}/card, and the WebSocket /runs/{id}/events?from=<seq>: replay, a caught_up frame, then live events from the bus, each exactly once in seq order. A client that falls behind is closed with 4000 "resync" and resumes with ?from=. Requests must name a local host; WebSocket origins must be allow-listed; and every request for the engine's data must carry the server's session token, as Authorization: Bearer or, on the stream, ?token= (H03, repro.api.token).

Settings

Each setting comes from, in order: a command-line flag, a REPRO_* environment variable, the TOML file (REPRO_CONFIG, else config.toml in the data directory), the default.

Setting Variable Default
data_dir REPRO_DATA_DIR ~/.repro (%USERPROFILE%\.repro)
run_root REPRO_RUN_ROOT %LOCALAPPDATA%\repro\r on Windows, <data_dir>/r elsewhere
log_level REPRO_LOG_LEVEL info (debug, info, warning, error)
log_format REPRO_LOG_FORMAT json (one object per line on stderr), or text
concurrency REPRO_CONCURRENCY the CPU count, at most 4 (1–64)
sandbox REPRO_SANDBOX auto (docker, process)
ui_dir REPRO_UI_DIR this checkout's apps/web/dist, if built (pnpm build)
agents REPRO_AGENTS off: deterministic only (claude, groq, openai)
agent_model REPRO_AGENT_MODEL the backend's per-role models (repro.agents.setup)
agent_base_url REPRO_AGENT_BASE_URL none; required with agents = openai
agent_api_key_env REPRO_AGENT_API_KEY_ENV none: an openai endpoint that takes no key
agent_mode REPRO_AGENT_MODE live (record, replay: cassettes, G02)
agent_cassettes REPRO_AGENT_CASSETTES none; required with record or replay
agent_budget_usd REPRO_AGENT_BUDGET_USD 2.0: the most a run's agent calls may cost together
test_timeout_s REPRO_TEST_TIMEOUT_S 900: each sandboxed test run's wall-clock limit, in seconds (H01)
memory_limit_mb REPRO_MEMORY_LIMIT_MB 4096: the memory a sandboxed run's processes may use (H01)
github_api_url REPRO_GITHUB_API_URL https://api.github.com: GitHub's REST API for repro run --pr (I01); a GitHub Enterprise Server's is https://<host>/api/v3
docker_runtime REPRO_DOCKER_RUNTIME unset: Docker's default. runsc runs the target's containers under gVisor, isolation level L4 (H03)
docker_context REPRO_DOCKER_CONTEXT unset: Docker's own. The Docker context every container command uses: a team's engine on another machine (docker context create builder --docker host=ssh://user@host), K05
sandbox_image REPRO_SANDBOX_IMAGE unset: Repro's own image and environment. An image pinned by digest (name@sha256:...) whose own Python environment the tests run in, as it is: a benchmark task's image, or a CI image (J04). Needs the Docker sandbox
agent_probes REPRO_AGENT_PROBES true: with agents on, the probe writer proposes inputs for the probes (G07)
agent_order_swap REPRO_AGENT_ORDER_SWAP contradicted: which verdicts a second reviewer judges with the evidence in the opposite order (all, off; G08)
agent_panel REPRO_AGENT_PANEL empty: three models, comma-separated, that each judge every claim; a verdict stands when two agree (G08)

With agents on, repro run reads the pull request's claims from --pr-title and --pr-body (or --pr-file: the title, then the description), else from the commit messages. Groq's key is read from GROQ_API_KEY, Claude's from the environment the Agent SDK reads.

Unknown keys and invalid values are errors that name their source. Paths must be absolute after ~ is expanded.

Metadata

Release files for repro-engine 1.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for repro-engine 1.3.1
File Interpreter ABI Platform
repro_engine-1.3.1-py3-none-any.whl Python 3 none any Details

Release files / repro_engine-1.3.1-py3-none-any.whl

Download URL repro_engine-1.3.1-py3-none-any.whl
Size 1.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
d363dbe66fef0f8326aed87ce22a9551c413b52f6309370a4b5d00972ab90001
BLAKE2b-256 checksum
How to use checksums
025571412c7543468b78f3c68161e83e0e5a6ff0ebfc538602a0114063bf34e9
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 Oct 21, 2026.

Transparency log

Security for repro-engine 1.3.1

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 1 file in this release has been uploaded with provenance metadata.

Attestation for repro_engine-1.3.1-py3-none-any.whl

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 Oct 21, 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.

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