Parity
Migration verification by observable behaviour—across versions, implementations, runtimes and languages.
Parity runs a reference and a candidate on the same complete calls, compares what they return or raise, searches for differences, shrinks failing invocations and saves replayable evidence.
canonical input ──┬──> reference ──┐
└──> candidate ──┴──> compare ──> shrink ──> replay
Parity's unit of work is an explicit callable(*args, **kwargs) contract, not a dataframe. Its
arguments can be ordinary JSON, frames, variable-length sequences or project-generated structures
such as recursive programs and event streams. The two sides can use different dependency versions,
APIs, architectures, Python environments or languages; they only need the same observable contract.
Use Parity for dependency upgrades, refactors, backend changes, branch/worktree comparisons and cross-language rewrites. It verifies a migration; it does not write or repair one.
PASSEDmeans Parity found no difference in the configured domain and search budget. It is executable evidence, not a proof of equivalence.
Five-minute start
The Parity controller requires Python 3.11 or later.
python -m pip install parity-check
parity init
parity check
parity init creates a runnable, JSON-only parity.toml and parity_example.py. Edit the two
example functions to call the old and new behaviour, then rerun parity check.
Dataframes are one optional contract shape. Add parity-check[pandas] or parity-check[polars]
when targets in the controller environment use those adapters; neither library is installed by the
base package. Managed package-upgrade environments use parity-check[workspace].
Put it around real code
A useful first case needs representative calls and an explicit comparison policy. This example compares two pricing-rules implementations using only JSON values:
version = 2
[[cases]]
name = "pricing-rules"
[[cases.invocation.args]]
kind = "json"
values = [
{ plan = "basic", seats = 1, coupons = [] },
{ plan = "pro", seats = 25, coupons = ["LOYALTY"] },
]
[cases.invocation.kwargs.region]
kind = "json"
values = ["GB", "US"]
[cases.reference]
target = "migration_adapters:reference_quote"
[cases.candidate]
target = "migration_adapters:candidate_quote"
[cases.comparison]
check_exceptions = true
check_input_mutation = true
rtol = 0.0
atol = 0.0
[cases.generation]
max_examples = 250
max_findings = 4
[cases.performance]
enabled = false
Paths are relative to parity.toml. Targets use module:callable syntax and should be small,
project-owned wrappers around the public behaviour being migrated.
Repeat [[cases]] for independent behaviours. Inside one case, repeat
[[cases.invocation.args]] or add [cases.invocation.kwargs.<name>] for many inputs. Use
kind = "frame" when table structure is part of the contract, kind = "frames" for one
variable-length dataframe sequence, or a project-owned generator for dependent structures such as
ASTs and stateful event streams. Zero-argument calls, expanded *frames and relationally generated
joins are supported too. See invocation configuration.
parity doctor --config parity.toml
parity check --config parity.toml
parity check --case pricing-rules --max-examples 1000
parity check --json .parity/report.json --junit .parity/junit.xml
The CLI has a stable outcome contract:
- exit
0:PASSED; - exit
1:FAILEDbecause behaviour or an enforced performance policy differed; and - exit
2:ERRORbecause configuration or execution could not produce reliable evidence.
What Parity checks
- Complete calls with zero or many positional and keyword JSON values, frames and frame sequences.
- Nested JSON or frame returns, raised exceptions and input mutation.
- Fixtures, deterministic edge cases, built-in generation and project-owned Hypothesis strategies for arbitrary bounded domains such as recursive ASTs or operation streams.
- When frames are present: column and row order, keyed rows, dtypes, null/NaN rules, numeric tolerances, datetimes and relational constraints.
- Several independent mismatch classes in one run, each with a stable
ms3:identifier. - Runtime and selected dependency provenance for each isolated target.
- Optional paired runtime and peak-memory regression evidence after semantic success.
Both sides receive the exact same call shape and values. Their internal APIs do not need to match; adapt each side into the shared input and output contract:
def reference_quote(request, *, region):
from legacy import quote
return quote(request, market=region)
def candidate_quote(request, *, region):
from rewritten import Engine, QuoteRequest
result = Engine(region=region).quote(QuoteRequest.from_dict(request))
return {"decision": result.status, "price": result.amount, "reasons": result.reasons}
Keep side-specific imports inside their wrappers when the two environments intentionally contain
different packages. A configured canonicalizer can project a successful domain object into an
Arrow or JSON-like result. Target exceptions remain observable behaviour.
Upgrade two released packages
The managed workspace creates separate, locked target environments. The controller and targets do not share a dependency graph.
python -m pip install "parity-check[workspace]"
parity migration init \
--reference-package 'your-library==1.2.3' \
--candidate-package 'your-library==2.0.0' \
--scaffold \
--json
This creates a deliberately incomplete adapter, tiny JSON fixture, case configuration, migration
inventory, workspace and four-item review checklist under migrations/. Implement the adapter,
review the fixture/domain and comparison policy, resolve the checklist, then run:
parity migration validate --json
parity migration run --json
Validation does not create environments or invoke targets. It remains non-passing until the generated contract has been reviewed. The final run prepares both sides, checks every declared case in every dependency lane and writes a data-safe report per lane.
Each side may instead be an existing checkout, so the same workflow covers released/local and local/local comparisons:
parity migration init \
--reference-path ../main-worktree \
--candidate-path ../feature-worktree
parity migration run
Parity does not clone, switch or edit worktrees. It installs each checkout separately and binds its Git/content identity into the evidence. See the user guide for custom targets, dependency lanes and rolling A→B→C upgrades.
Cross-language targets
Any local executable can be a reference or candidate through Parity's versioned Arrow/JSON target protocol. For a Python boundary around C, C++, Fortran, Rust, Java or a legacy CLI, scaffold the protocol process and implement only the domain translation:
parity adapter init adapters/legacy.py --program bin/legacy-target
[cases.reference]
command = ["parity", "adapter", "serve", "adapters/legacy.py"]
The adapter process needs Parity; the wrapped program does not. Compilation, images and dependency installation stay outside the behavioural contract. Start with the adapter SDK guide; implement the language-neutral protocol directly only when Python is unsuitable.
The maintained JavaScript-to-Python rules-engine proof uses recursive JSON programs, nested returns and domain exceptions—without pandas, Polars, Arrow inputs or tabular outputs. It verifies a correct port, discovers and minimizes three independent defects in a naive port, retains them as regressions and replays the saved evidence.
Findings and replay
A confirmed difference creates a private artifact containing the minimized invocation, any Arrow leaves, hashes, comparison contract, runtime identities and exact replay information:
parity replay .parity/pricing-rules/<finding-directory>
parity evidence verify .parity/report.json --json .parity/evidence-status.json
replay reproduces the semantic result, so a reproduced incompatibility still exits 1.
evidence verify answers a different question and exits 0 when every report-referenced finding
reproduces its recorded mismatch class.
Terminal, JSON, Markdown and JUnit reports omit compared values. Counterexample and distilled contract directories contain real inputs and outputs; keep them private and upload them only when your data policy permits it.
Reviewed intentional differences can be recorded as exact case/finding approvals. Discovered regressions can also be distilled into a candidate-only contract before the old implementation is removed. See compatibility budgets and distilled contracts.
CI
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: leighshepperson/parity@v0
with:
config: parity.toml
performance: "false"
The moving v0 Action installs the same Parity source revision as the selected action. Pin a
reviewed full commit SHA when CI must be immutable. Artifact upload is opt-in because findings can
contain sensitive inputs. See the GitHub Action guide.
Read next
| Need | Document |
|---|---|
| See a JSON-only cross-language proof | JavaScript to Python rules engine |
| Build a practical campaign | User guide |
| Look up every TOML field | Configuration reference |
| Decide whether Parity fits | Use cases and boundaries |
| Verify a coding-agent migration | Agent migration protocol |
| Use pytest | Pytest integration |
| Understand internals and contracts | Architecture |
| Handle artifacts and untrusted code | Security and privacy |
| Explore dataframe migrations | pandas-to-Polars fault corpus |
| Explore other executable proofs | C++ order book, Fortran summation and external validation |
Boundaries and status
Parity compares canonical returns, raises, mutation and process performance. A reviewed wrapper can project a CLI, file or database result into that contract, but Parity does not yet capture and restore filesystem, database or network effects itself. Target processes isolate failures; they are not a security sandbox for hostile code.
Parity is Apache-2.0 licensed and pre-1.0. The current minor release is the supported line, and a minor release may deliberately change public contracts before 1.0.
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 parity_check-0.20.0.tar.gz.
File metadata
- Download URL: parity_check-0.20.0.tar.gz
- Upload date:
- Size: 552.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c63a279244ffeb7ff2e608a49ac204319badbbb145932e43de8fedb0e67c10a
|
|
| MD5 |
2eb915f5ae85762de10cc9daf9ad866a
|
|
| BLAKE2b-256 |
47974cbaa75b0b9c92c9c8fede14c99bf2f9951f6a6339d1e9d5ca1760b7ed62
|
Provenance
The following attestation bundles were made for parity_check-0.20.0.tar.gz:
Publisher:
release.yml on leighshepperson/parity
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parity_check-0.20.0.tar.gz -
Subject digest:
6c63a279244ffeb7ff2e608a49ac204319badbbb145932e43de8fedb0e67c10a - Sigstore transparency entry: 2576161866
- Sigstore integration time:
-
Permalink:
leighshepperson/parity@57c903f42622c99be16ae721b47ab1d7d6e092bb -
Branch / Tag:
refs/heads/main - Owner: https://github.com/leighshepperson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@57c903f42622c99be16ae721b47ab1d7d6e092bb -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file parity_check-0.20.0-py3-none-any.whl.
File metadata
- Download URL: parity_check-0.20.0-py3-none-any.whl
- Upload date:
- Size: 261.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26229e4111090d76fd792ff470d2f32a042aebc1b1f8c7022ccc0f43437eedc3
|
|
| MD5 |
ef2a71657137d7fe013011cf4f95d3a0
|
|
| BLAKE2b-256 |
3ebb7972c1c18002e60a0a70db7bf880f72dda5b0c6d5c6f934378d3551be149
|
Provenance
The following attestation bundles were made for parity_check-0.20.0-py3-none-any.whl:
Publisher:
release.yml on leighshepperson/parity
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parity_check-0.20.0-py3-none-any.whl -
Subject digest:
26229e4111090d76fd792ff470d2f32a042aebc1b1f8c7022ccc0f43437eedc3 - Sigstore transparency entry: 2576161936
- Sigstore integration time:
-
Permalink:
leighshepperson/parity@57c903f42622c99be16ae721b47ab1d7d6e092bb -
Branch / Tag:
refs/heads/main - Owner: https://github.com/leighshepperson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@57c903f42622c99be16ae721b47ab1d7d6e092bb -
Trigger Event:
workflow_run
-
Statement type: