Metadata-Version: 2.4
Name: parityrun
Version: 0.6.1
Summary: Port your app to another language with your AI tool, and prove the port behaves exactly like the original
Author: Arttu Jantunen
License-Expression: MIT
Project-URL: Homepage, https://github.com/arttujan/parityrun
Keywords: testing,golden,snapshot,conformance,parity,rewrite,llm
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Testing
Classifier: Intended Audience :: Developers
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.28
Requires-Dist: PyYAML>=6.0
Requires-Dist: pillow>=11.0
Requires-Dist: numpy>=2.0
Requires-Dist: scipy>=1.13
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Dynamic: license-file

# parityrun

Prove that a rewrite of your app behaves exactly like the original. parityrun records what your app does (every
page, form answer, redirect, database row and file it writes) and then checks a new version against that recording:
a port from PHP to JavaScript, TypeScript or Rust, a framework upgrade, or simply the next release. With your own AI
tool, it can do the port too, turn by turn, until the new version matches.

## Install

```
pip install parityrun
```

That needs Python 3.10 or newer. Without Python, one line installs everything. On Windows (PowerShell):

```
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/arttujan/parityrun/main/install.ps1 | iex"
```

On macOS and Linux:

```
curl -LsSf https://raw.githubusercontent.com/arttujan/parityrun/main/install.sh | sh
```

The installer gets [uv](https://docs.astral.sh/uv/), which installs parityrun as a command of its own, with its own
Python. Run it again to update (or `pip install -U parityrun`). Your app still needs whatever it needs to run (for a
PHP app: `php`).

## Use it

In your app's folder:

```
parityrun
```

That's the one command to remember. The first time, it finds how to start your app and every route in its code,
and records what the app does. Every time, it shows where you are and the next command to run:

```
my-app: 1 of 1 scenario recorded
  3/14 routes visited

Next: parityrun capture
      use the app in your browser: 11 routes not visited yet
```

`parityrun capture` opens the app in your browser: use it as people do, then press Finish. Every page shows how
many of the app's routes you have visited, and `http://127.0.0.1:8000/__parityrun` lists the ones still to go. Your
app's own files are never changed.

When you have a new version of the app (or let your AI tool make one, below), tell parityrun how to start it in
`parityrun/impls.json`:

```json
"node": {"cwd": "../my-app-js", "cmd": ["node", "server.js"]}
```

(it gets its port in the `PORT` environment variable) and check it:

```
parityrun check --impl node      # every difference, and the parity %; exits 0 only at 100 %
parityrun coverage               # the routes and lines of code no scenario has run yet
```

## Let your AI tool port it

```
parityrun port --to ts      # TypeScript; or js (JavaScript), rs (Rust)
```

parityrun writes the new app's starting point beside yours (`../my-app-ts`), adds it to `impls.json` and gives your AI
tool the task in `PORTING.md`. Then it goes turn by turn: the tool works, parityrun checks, and the differences of one
scenario at a time become the next turn's task, until parity is 100 %. A scenario that stays red is reported, never
hidden. By default the tool is [Claude Code](https://claude.com/claude-code) (`claude` on PATH, your own account);
for another one, give its command: `--agent "yourtool --message-file {prompt_file}"`. The tool may not change your app
or the recordings: parityrun puts its own folder back after every turn and stops if your app changes.

## What "the same" means

Pages are compared as a browser reads them (indentation and attribute order don't count, text and links do), along
with status codes, redirects, database rows and written files; with the built-in stubs, also every prompt sent to an
LLM and every call to an outside service. Random ids, tokens and times are normalised. A check proves the new version identical on the recorded scenarios, which visit the routes and
(for PHP) run the share of the code that `parityrun coverage` shows; a path no scenario takes is not proven, so
capture until coverage is where you want it.

`init` recognises PHP (plain, Laravel, Symfony, WordPress), Node.js, Python (Django, Flask, FastAPI) and Rust apps,
and PHP libraries: code with no pages, whose scenarios call its functions and classes (`fn:`, `new:`) instead of
visiting pages, and whose port gets a ready-made shim to fill in.
Everything else (settings, scenario files, databases, LLM and outside-service stubs, examples) is in
[the wiki](https://github.com/arttujan/parityrun/wiki), and on one page in
[docs/reference.md](https://github.com/arttujan/parityrun/blob/main/docs/reference.md).

## Licence

MIT. Copyright (c) 2026 Arttu Jantunen.
