Skip to content

Porting with an AI Tool

Arttu Jantunen edited this page Nov 2, 2026 · 1 revision
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.

parityrun port --to typescript|javascript|rust|ts|js|rs [--into FOLDER] [--name NAME] [--agent claude|"COMMAND"]
               [--turns 20] [--patience 3] [--turn-minutes 60] [--setup-only]

What it does

The recordings are what the port is held to, so port first records any scenario that has no recording yet. Then it

  1. writes the port's starting point, an app that runs and answers 501 everywhere, in --into (default: beside the app, <app>-ts, <app>-js or <app>-rs):

    • JavaScript is server.js (CommonJS),
    • TypeScript is server.ts, run by node 23.6 or newer as it is (type stripping, no build),
    • Rust is a Cargo package of its own, built with cargo build --release.

    A file that exists is never written over, so port goes on from what is there;

  2. adds the port to impls.json (default name js, ts or rust), with its port in PORT;

  3. writes PORTING.md in the port's folder, the task: how the port is started, the environment every run gives it (from your env(), with sample values), what is compared, every scenario with its recording, how to check, and the rules;

  4. loops. Each turn runs your AI tool in the port's folder. The first turn ports the whole app; then parityrun builds the port, sees that it starts, runs check, and the next turn gets the failing checks of one red scenario (path: expected != yours, with the paths of the scenario, its recording and the port's own recording in out/<name>/<scenario>/recording.json). A crash comes with the port's own output; a build that fails, with the compiler's.

It stops at 100 % parity (exit 0), after --turns turns, or when every red scenario has been worked on for --patience turns without getting better, or the port hasn't built or started for that many turns (exit 1: what is still red is printed). Run the same command again to go on.

The AI tool

--agent claude (the default) runs Claude Code as claude -p --permission-mode acceptEdits with the task on stdin: it may read the app and the parityrun folder (--add-dir), edit files in the port's folder and run parityrun check, node/npm or cargo, nothing else. It uses your own Claude Code login and plan.

Any other tool: give its command line, with {prompt_file} (the turn's task as a file) and {folder} (the port's folder) filled in; it runs in the port's folder and gets the task on stdin too (write paths in the command with forward slashes):

parityrun port --to rs --agent "yourtool --message-file {prompt_file}"

PARITYRUN_PROJECT points the tool's own parityrun check at the project.

What the tool may not change

The parityrun folder (scenarios, recordings, fixtures, settings) is kept aside and put back after every turn; the next turn is told what was put back. The original app's files are watched: when one changes, port stops (exit 2), as the recordings may no longer be the original's.

What is kept

Each turn's task and the tool's output are in out/port/turn-<n>.prompt.md and turn-<n>.log; each turn's result (parity, every scenario's passing checks, the time the tool took) is a line of out/port-log.jsonl.

Porting by hand

--setup-only writes the folder, the impls.json entry and PORTING.md and stops, for porting by hand or in your own tool's chat: parityrun check --impl ts checks the port at any time.

A library is ported the same way, with a shim to fill in: see Libraries.

Clone this wiki locally