PHPackages                             rewrit/rewrit - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. rewrit/rewrit

ActiveLibrary[Testing &amp; Quality](/categories/testing)

rewrit/rewrit
=============

PHP SDK for emitting Rewrit observations from Pest, PHPUnit and Laravel tests.

1.0(yesterday)31↑2900%MIT OR Apache-2.0RustPHP &gt;=8.2CI passing

Since Jul 1Pushed yesterdayCompare

[ Source](https://github.com/leofmarciano/rewrit)[ Packagist](https://packagist.org/packages/rewrit/rewrit)[ Docs](https://github.com/leofmarciano/rewrit)[ RSS](/packages/rewrit-rewrit/feed)WikiDiscussions main Synced today

READMEChangelog (1)DependenciesVersions (2)Used By (0)

Rewrit
======

[](#rewrit)

[![CI](https://github.com/leofmarciano/rewrit/actions/workflows/ci.yml/badge.svg)](https://github.com/leofmarciano/rewrit/actions/workflows/ci.yml)[![crates.io](https://camo.githubusercontent.com/90405fc6e2e5fa4c19ec59a6a5b270b9bcd9e2e627d0538e0508f0c241a335ae/68747470733a2f2f696d672e736869656c64732e696f2f6372617465732f762f7265777269742d636f72652e737667)](https://crates.io/crates/rewrit-core)[![npm package](https://camo.githubusercontent.com/005814518ecb4120b8fa9acd005bbd798fe84a45b018fc7f12c43b7b24e6a119/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e706d2d406c656f666d61726369616e6f2532467265777269742d2d6e6f64652d4342333833372e737667)](#packages)[![Packagist](https://camo.githubusercontent.com/c0cdf137cc3779b30f0f60976bb625bdb01dec19ef613fae0a135e21288d450e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7265777269742f7265777269742e737667)](https://packagist.org/packages/rewrit/rewrit)[![License: MIT OR Apache-2.0](https://camo.githubusercontent.com/6a380a6e924e76196bdb305d9a425347f5c103a5dfd60ae3b2722cd075777220/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542532304f522532304170616368652d2d322e302d626c75652e737667)](#license)[![Rust 1.80+](https://camo.githubusercontent.com/7c2d98e0b64fcb8f6f6fff73bde07ac70adc93f77a6193410df9e454547e01c9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f727573742d312e38302532422d6f72616e67652e737667)](Cargo.toml)

Rewrit is a parity engine for software rewrites.

It compares the observable behavior of a **reference** implementation and a **candidate** implementation so teams can move code across languages, frameworks, services, and architectures without silently changing behavior.

Use Rewrit when "the tests pass" is not enough. It checks the values, errors, HTTP responses, side effects, contracts, and policy decisions that matter during a migration.

```
reference runtime          candidate runtime
       |                         |
       v                         v
    adapter                   adapter
       |                         |
       +------ observations -----+
                  |
                  v
        normalize, compare, report
                  |
                  v
          CI-friendly exit code
```

Why Rewrit?
-----------

[](#why-rewrit)

Large rewrites usually fail in the gaps between test suites:

- a Laravel endpoint becomes an Encore service,
- a Django path moves to Rust,
- a PHP monolith function is extracted to Node,
- an HTTP API keeps the same shape but changes internals,
- a "harmless" refactor changes an error, header, decimal, queue message, or database side effect.

Rewrit gives those gaps names and evidence. Instead of flattening everything into "test failed", it reports structured divergences such as `type_mismatch`, `schema_mismatch`, `missing_candidate_case`, `side_effect_mismatch`, `timeout`, and `waiver_expired`.

Current Status
--------------

[](#current-status)

Rewrit is pre-1.0. The MVP workspace is implemented and covered by tests, but the public protocol and SDK ergonomics may still change before a stable 1.0 release.

Today the repository includes:

- Rust CLI and engine,
- versioned NDJSON adapter protocol,
- JSON contract and report schemas,
- command and HTTP adapters,
- PHP, Node, Python, and Rust SDK surfaces,
- terminal, JSON, NDJSON, JUnit, SARIF, HTML, and Markdown report code,
- runnable migration examples.

Packages
--------

[](#packages)

Rewrit packages are published across the registries used by each supported ecosystem. The CLI is not published as a standalone crates.io package yet; use the source build in the quickstart below.

EcosystemPackageInstallRust crates on crates.io[`rewrit-core`](https://crates.io/crates/rewrit-core), [`rewrit-model`](https://crates.io/crates/rewrit-model), [`rewrit-protocol`](https://crates.io/crates/rewrit-protocol), [`rewrit-report`](https://crates.io/crates/rewrit-report), [`rewrit-macros`](https://crates.io/crates/rewrit-macros)Example: `cargo add rewrit-core`Node SDK via npm`@leofmarciano/rewrit-node``npm install @leofmarciano/rewrit-node@0.1.0`PHP SDK on Packagist[`rewrit/rewrit`](https://packagist.org/packages/rewrit/rewrit)`composer require rewrit/rewrit`Quickstart
----------

[](#quickstart)

Install Rust 1.80 or newer, then build the CLI from source:

```
git clone https://github.com/leofmarciano/rewrit.git
cd rewrit
cargo build -p rewrit-cli --release
./target/release/rewrit --help
```

Run a passing Laravel-to-Encore shaped fixture:

```
cargo run -p rewrit-cli -- run --manifest examples/laravel-to-encore/rewrit.toml
```

Expected result:

```
Equivalent: 1
Blocking divergences: 0
Parity: 100.00%
Exit: 0
```

Run an intentionally failing command-to-command fixture:

```
cargo run -p rewrit-cli -- run --manifest examples/command-to-command/rewrit.toml
```

That fixture exits with `1` on purpose. It demonstrates how Rewrit reports a candidate returning money as a number instead of a decimal string, plus a missing candidate case.

Create a Project
----------------

[](#create-a-project)

Generate a runnable scaffold:

```
cargo build -p rewrit-cli
export PATH="$PWD/target/debug:$PATH"

rm -rf /tmp/rewrit-demo
mkdir /tmp/rewrit-demo
cd /tmp/rewrit-demo

rewrit init --template laravel-to-encore
rewrit run --mode mirror
rewrit capture --runtime legacy_laravel
rewrit verify --runtime encore_ts
rewrit audit
```

Available templates:

- `command-to-command`
- `laravel-to-encore`
- `django-to-rust`

Core Ideas
----------

[](#core-ideas)

### Reference and Candidate

[](#reference-and-candidate)

The reference is the implementation treated as the source of truth. The candidate is the implementation being validated.

### Cases

[](#cases)

A case is one stable behavior ID, such as `billing.invoice.create.success` or `auth.login.invalid_password`.

### Contracts

[](#contracts)

Contracts declare what must stay equivalent: inputs, outputs, status codes, schemas, headers, expected errors, side effects, tolerances, and policies.

### Observations

[](#observations)

Adapters translate framework-specific execution into canonical Rewrit observations. The Rust core does not need to understand Laravel, Django, Vitest, Pest, Pytest, Cargo test, or any other test runner.

### Policies, Normalizers, and Waivers

[](#policies-normalizers-and-waivers)

Policies define what counts as a real difference. Normalizers remove accepted noise such as generated IDs or timestamps. Waivers are explicit, owned, and expiring exceptions that remain visible in reports.

Example Manifest
----------------

[](#example-manifest)

Rewrit projects are configured with `rewrit.toml`.

```
[project]
name = "billing-migration"
reference = "legacy_laravel"
candidate = "encore_ts"
contracts_dir = "contracts"
baselines_dir = ".rewrit/baselines"
reports_dir = ".rewrit/reports"

[runtimes.legacy_laravel]
adapter = "command"
cwd = "../legacy"
command = ["vendor/bin/pest", "--rewrit"]
timeout_ms = 30000

[runtimes.encore_ts]
adapter = "command"
cwd = "../candidate"
command = ["npm", "run", "test:rewrit"]
timeout_ms = 30000

[[reports]]
kind = "terminal"

[[reports]]
kind = "json"
path = ".rewrit/reports/latest.json"
```

Adapters emit versioned NDJSON events:

```
{"schema_version":"rewrit.event.v1","kind":"observation","case_id":"billing.invoice.create.success","runtime_id":"reference","status":"passed","value":{"kind":"json","value":{"status":"open","amount":"199.90"}}}
```

CLI
---

[](#cli)

```
rewrit init --template laravel-to-encore
rewrit doctor
rewrit discover
rewrit run --mode mirror
rewrit capture --runtime legacy_laravel
rewrit verify --runtime encore_ts
rewrit audit
rewrit explain billing.invoice.create.success
rewrit schema export --kind all --out-dir dist/schemas
rewrit report open
rewrit completions --shell zsh
```

Common modes:

- `run --mode mirror`: run reference and candidate in the same execution.
- `capture --runtime `: store a baseline reference observation.
- `verify --runtime `: compare a candidate against the stored baseline or declared contracts.
- `audit`: check for missing required cases.

Examples
--------

[](#examples)

ExampleWhat it shows[`examples/command-to-command`](examples/command-to-command)Generic command adapter and intentional divergences[`examples/http-to-http`](examples/http-to-http)HTTP contract comparison and schema/type mismatches[`examples/laravel-to-encore`](examples/laravel-to-encore)PHP-shaped reference to Encore/Node-shaped candidate[`examples/django-to-rust`](examples/django-to-rust)Django/Python-shaped reference to Rust candidate[`examples/php-to-node-monolith`](examples/php-to-node-monolith)Monolith function extraction with stable case IDsDocumentation
-------------

[](#documentation)

Start with the [documentation index](docs/README.md).

Common entry points:

- New to Rewrit: [Parity](docs/concepts/parity.md), [contracts](docs/concepts/contracts.md), and [observations](docs/concepts/observations.md).
- Wiring a runner or framework: [Adapter protocol v1](docs/protocol/adapter-protocol-v1.md)and the adapter guide for your stack.
- Using an existing SDK: [PHP Pest](docs/adapters/php-pest.md), [PHPUnit](docs/adapters/php-phpunit.md), [Node Vitest](docs/adapters/node-vitest.md), [Node Jest](docs/adapters/node-jest.md), [Node Encore](docs/adapters/node-encore.md), [Python Pytest](docs/adapters/python-pytest.md), [Django](docs/adapters/django.md), or [Rust Cargo test](docs/adapters/rust-cargo-test.md).
- Planning a migration: [Laravel to Encore](docs/migrations/laravel-to-encore.md), [Laravel to Node](docs/migrations/laravel-to-node.md), or [Django to Rust](docs/migrations/django-to-rust.md).
- Reviewing design decisions: [ADRs](docs/adr).

Repository Layout
-----------------

[](#repository-layout)

```
crates/
  rewrit-model             canonical data model
  rewrit-core              normalization, comparison, policy, validation
  rewrit-engine            discovery, scheduling, runtime execution, baselines
  rewrit-protocol          adapter protocol types
  rewrit-report            terminal and machine-readable reports
  rewrit-cli               command-line interface
  rewrit-adapter-*         built-in adapter crates

sdks/
  php/
  node/
  python/
  rust/

docs/
  concepts/
  protocol/
  adapters/
  migrations/
  adr/

examples/
  command-to-command/
  http-to-http/
  laravel-to-encore/
  django-to-rust/
  php-to-node-monolith/
```

Development
-----------

[](#development)

Run the full Rust gate before opening a pull request:

```
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo doc --workspace --all-features --no-deps
```

Generate protocol and report schemas:

```
cargo run -p rewrit-cli -- schema export --kind all --out-dir dist/schemas
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.

Security
--------

[](#security)

Rewrit executes project code through adapters. Treat configured runtimes as trusted project code unless you explicitly run them inside a sandbox.

Supported guardrails include timeouts, stdout/stderr limits, secret redaction, temporary run directories, environment allowlists, and optional Docker or Podman sandboxing.

See [SECURITY.md](SECURITY.md) for details.

License
-------

[](#license)

Rewrit is licensed under either of:

- [MIT](LICENSE-MIT)
- [Apache License 2.0](LICENSE-APACHE)

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/258d8c77d6db911a7f5776f374de33b7aedf3619180e109c2008429222f96594?d=identicon)[leofmarciano](/maintainers/leofmarciano)

---

Top Contributors

[![leofmarciano](https://avatars.githubusercontent.com/u/131293652?v=4)](https://github.com/leofmarciano "leofmarciano (58 commits)")

---

Tags

testingphpunitpestlaravelmigrationparityrewrit

### Embed Badge

![Health badge](/badges/rewrit-rewrit/health.svg)

```
[![Health](https://phpackages.com/badges/rewrit-rewrit/health.svg)](https://phpackages.com/packages/rewrit-rewrit)
```

###  Alternatives

[code-distortion/adapt

A Laravel package that builds databases for your tests, improving their speed.

2838.1k](/packages/code-distortion-adapt)[mrpunyapal/rector-pest

Rector upgrade rules for Pest - refactoring and best practices for Pest testing framework

6977.7k58](/packages/mrpunyapal-rector-pest)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
