Skip to main content

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.

PASSED means 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: FAILED because behaviour or an enforced performance policy differed; and
  • exit 2: ERROR because 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

parity_check-0.20.0.tar.gz (552.8 kB view details)

Uploaded Source

Built Distribution

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

parity_check-0.20.0-py3-none-any.whl (261.6 kB view details)

Uploaded Python 3

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

Hashes for parity_check-0.20.0.tar.gz
Algorithm Hash digest
SHA256 6c63a279244ffeb7ff2e608a49ac204319badbbb145932e43de8fedb0e67c10a
MD5 2eb915f5ae85762de10cc9daf9ad866a
BLAKE2b-256 47974cbaa75b0b9c92c9c8fede14c99bf2f9951f6a6339d1e9d5ca1760b7ed62

See more details on using hashes here.

Provenance

The following attestation bundles were made for parity_check-0.20.0.tar.gz:

Publisher: release.yml on leighshepperson/parity

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

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

Hashes for parity_check-0.20.0-py3-none-any.whl
Algorithm Hash digest
SHA256 26229e4111090d76fd792ff470d2f32a042aebc1b1f8c7022ccc0f43437eedc3
MD5 ef2a71657137d7fe013011cf4f95d3a0
BLAKE2b-256 3ebb7972c1c18002e60a0a70db7bf880f72dda5b0c6d5c6f934378d3551be149

See more details on using hashes here.

Provenance

The following attestation bundles were made for parity_check-0.20.0-py3-none-any.whl:

Publisher: release.yml on leighshepperson/parity

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

Release history Release notifications | RSS feed

0.21.0

2 files

0.20.1

2 files

This release

0.20.0 This release

2 files

0.19.1

2 files

0.19.0

2 files

0.18.1

2 files

0.18.0

2 files

0.17.0

2 files

0.16.0

2 files

0.14.3

2 files

0.14.2

2 files

0.14.1

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.10.0

2 files

0.9.2

2 files

0.9.1

2 files

0.8.0

2 files

0.1.0

2 files

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