doppeldiff 0.0.0-stage โ 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +292 -2
- package/dist/child.js +487 -0
- package/dist/child.js.map +7 -0
- package/dist/cli.js +7043 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.js +5997 -0
- package/dist/index.js.map +7 -0
- package/package.json +96 -4
- package/schema/report.schema.json +891 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 doppeldiff contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,293 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# ๐ฌ doppeldiff
|
|
4
|
+
|
|
5
|
+
**See what your PR actually changed.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/doppeldiff)
|
|
8
|
+
[](https://github.com/7suheel/doppeldiff/actions/workflows/ci.yml)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
Built for refactors and "no behavior change" pull requests. doppeldiff runs the old and the new version of every
|
|
12
|
+
changed function on thousands of inputs your tests never tried, and shows every input where they disagree.
|
|
13
|
+
|
|
14
|
+
<img src="docs/assets/pr-comment.png" alt="A doppeldiff pull request comment listing unclaimed behavior changes with concrete inputs and old/new results" width="760">
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx doppeldiff
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**No API key required. Free and fully local by default.**
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
### What it looks like on a real pull request
|
|
25
|
+
|
|
26
|
+
[Pull request #1](https://github.com/7suheel/doppeldiff/pull/1) is an AI-style refactor of a small shop's cart
|
|
27
|
+
code. Its description says _"Pure refactor, no behavior change. All tests pass."_ They do. doppeldiff ran the old and new
|
|
28
|
+
`cartTotal` on 10,000 generated carts and
|
|
29
|
+
[commented](https://github.com/7suheel/doppeldiff/pull/1#issuecomment-6560143205):
|
|
30
|
+
|
|
31
|
+
> โ ๏ธ **Unclaimed behavior change**: **`cartTotal`**: return value changed ยท 3,212 of 10,000 inputs
|
|
32
|
+
>
|
|
33
|
+
> | Call | Old | New |
|
|
34
|
+
> | ----------------------------------------------------------------------- | ---- | --- |
|
|
35
|
+
> | `cartTotal([{ sku: "", unitPrice: -1, quantity: 1 }], undefined)` | `-1` | `0` |
|
|
36
|
+
> | `cartTotal([{ sku: "", unitPrice: -0.01, quantity: 0.01 }], undefined)` | `-0` | `0` |
|
|
37
|
+
|
|
38
|
+
The refactor clamps the total at zero, so a cart with a credit or refund line no longer adds up. No reviewer was
|
|
39
|
+
asked to look for that, and no test covered it. The same comment flags a second unclaimed change, in `slugify`.
|
|
40
|
+
|
|
41
|
+
**See it live:** [doppeldiff-playground#1](https://github.com/7suheel/doppeldiff-playground/pull/1) is a small
|
|
42
|
+
repository set up exactly as described below, with an open "pure refactor" PR. Its tests pass, and doppeldiff's
|
|
43
|
+
comment shows `splitBill(10, 4)` returning `[3, 3, 3, 3]` instead of `[3, 3, 2, 2]`.
|
|
44
|
+
|
|
45
|
+
## What doppeldiff is good at, and what it is not good at yet
|
|
46
|
+
|
|
47
|
+
**Good at**
|
|
48
|
+
|
|
49
|
+
- **Refactors and "no behavior change" PRs**, AI-written or not. Either it ran thousands of inputs through both
|
|
50
|
+
versions and found no difference, or it shows the exact input where the refactor changed the result, like the
|
|
51
|
+
`cartTotal` example above. `--refactor` (CLI) and `verify_refactor` (MCP) turn that into a pass/fail check.
|
|
52
|
+
- **Finding inputs your tests never tried**: negative amounts, empty and blank strings, `-0`, values just past a
|
|
53
|
+
threshold in the code, strings in the formats the code's regexes parse, accents and lone surrogates. Each
|
|
54
|
+
finding comes with the old and the new result and can be written out as a regression test.
|
|
55
|
+
- **Plain TypeScript/JavaScript functions**: utilities, parsing, formatting, money and string handling in
|
|
56
|
+
libraries and app code.
|
|
57
|
+
- **Running on every PR**: a few seconds per PR, no API key, nothing leaves the machine.
|
|
58
|
+
|
|
59
|
+
**Not good at yet**
|
|
60
|
+
|
|
61
|
+
- **Finding arbitrary bugs.** doppeldiff only reports what differs between two versions, and random inputs rarely
|
|
62
|
+
hit a change that needs several specific conditions at once (an option combination plus a particular kind of
|
|
63
|
+
text). On the latest held-out set of real bug-fix commits it found **3 of 13**
|
|
64
|
+
([evaluation](docs/EVALUATION.md)). Use it next to your tests and review, not instead of them.
|
|
65
|
+
- **Code with side effects** (network, filesystem, databases): the sandbox blocks it, and nothing is mocked.
|
|
66
|
+
- **Class methods, overloaded functions, UMD bundles and class-instance parameters**: reported as not verified.
|
|
67
|
+
- **Callback-heavy APIs**: generated callbacks return fixed values, so a custom sort comparator never reorders
|
|
68
|
+
anything.
|
|
69
|
+
- **"No behavior change" is evidence, not proof**: inputs are sampled, not exhaustive.
|
|
70
|
+
|
|
71
|
+
## The problem
|
|
72
|
+
|
|
73
|
+
Writing code got cheap; verifying it did not. AI-written pull requests compile, pass CI and read well, yet quietly
|
|
74
|
+
change behavior on inputs nobody wrote a test for. AI review bots answer with opinions ("looks good to me"), so
|
|
75
|
+
you end up checking one model's code with another model's guess.
|
|
76
|
+
|
|
77
|
+
doppeldiff answers with evidence instead: **"for `cartTotal([{ unitPrice: -1, quantity: 1 }])` the old code
|
|
78
|
+
returned `-1` and the new code returns `0`"**. You can run that example yourself, and doppeldiff can write it into
|
|
79
|
+
a regression test for you.
|
|
80
|
+
|
|
81
|
+
## How it works
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
git diff โโโถ changed exported functions (+ functions that call changed helpers)
|
|
85
|
+
โ
|
|
86
|
+
โผ
|
|
87
|
+
base version โโโถ sandboxed Node process โโ
|
|
88
|
+
โโโโถ same generated inputs โโโถ compare โโโถ shrink โโโถ group
|
|
89
|
+
head version โโโถ sandboxed Node process โโ (types + values mined returns, throws, minimal,
|
|
90
|
+
from the code + regex mutations, async, readable
|
|
91
|
+
formats + edge cases) timeouts examples
|
|
92
|
+
โ
|
|
93
|
+
โผ
|
|
94
|
+
report: terminal ยท PR comment ยท JSON ยท regression tests (optional LLM: claimed vs unclaimed)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
1. **Find what changed.** It compares each module-level declaration with comments and formatting ignored, and
|
|
98
|
+
follows changed helpers, constants and imports to the exported functions that use them.
|
|
99
|
+
2. **Load both versions side by side.** The base commit goes into a git worktree that reuses your
|
|
100
|
+
`node_modules`, and each version is bundled with esbuild (TypeScript, ESM/CJS, tsconfig `paths`, workspaces).
|
|
101
|
+
3. **Generate inputs.** It derives input generators from the parameter types (via the TypeScript checker,
|
|
102
|
+
[fast-check](https://fast-check.dev) underneath), biased towards values mined from the code: comparison
|
|
103
|
+
thresholds ยฑ1, string literals, regex formats, property names, and boundaries such as `NaN`, `-0` and lone
|
|
104
|
+
surrogates.
|
|
105
|
+
4. **Run both, compare everything.** It compares return values (`NaN` equals `NaN`, `-0` differs from `0`; Dates,
|
|
106
|
+
Maps, Sets and class instances structurally), thrown errors, argument mutations, async results, hangs and
|
|
107
|
+
crashes. The clock is frozen and randomness seeded, so the comparison is reproducible.
|
|
108
|
+
5. **Explain it.** Each difference is shrunk to a minimal example, made readable (`2000` instead of
|
|
109
|
+
`9007199254740991`), re-run to confirm, and grouped into behaviors such as _"now returns NaN (previously a
|
|
110
|
+
number)"_.
|
|
111
|
+
|
|
112
|
+
## Three scenarios
|
|
113
|
+
|
|
114
|
+
The [`examples/demo-shop`](examples/demo-shop) library ships with three pull requests. All three pass the existing
|
|
115
|
+
test suite.
|
|
116
|
+
|
|
117
|
+
| PR | What doppeldiff says |
|
|
118
|
+
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
119
|
+
| **Honest fix**: "Fix discount rounding for prices over $1000" | One behavior change, only for prices above 1000: `applyDiscount(2000, 0.07)` was `1859.99`, now `1860`. |
|
|
120
|
+
| **"AI refactor"**: "Pure refactor, no behavior change" | โ ๏ธ 3 unclaimed changes: negative totals are now clamped to `0`, `cartTotal` returns `NaN` for zero-quantity items with a non-finite price, and `slugify("รฉ")` was `"e"` and is now `""`. `itemCount` really is unchanged. |
|
|
121
|
+
| **Pure refactor**: "No behavior change intended" | โ
No behavior change in 4 functions across 3,242 inputs. The report notes that `addBusinessDays` stopped early (242 inputs) because huge `days` values make both versions loop past the timeout. |
|
|
122
|
+
|
|
123
|
+
Try it: `node examples/demo-shop/try.mjs ai-refactor` creates a scenario repository and prints the command.
|
|
124
|
+
|
|
125
|
+
<img src="docs/assets/demo.gif" alt="Terminal recording of doppeldiff on the AI refactor scenario" width="760">
|
|
126
|
+
|
|
127
|
+
<sub>The image and recording above are rendered from real doppeldiff output on the demo scenario
|
|
128
|
+
(`scripts/render-assets.ts`); the PR comment is rendered with GitHub's Markdown stylesheet.</sub>
|
|
129
|
+
|
|
130
|
+
## Results on real commits
|
|
131
|
+
|
|
132
|
+
We ran doppeldiff on 39 historical commits from 17 libraries (ufo, validator.js, es-toolkit, date-fns, qs, radash,
|
|
133
|
+
โฆ). Most are bug fixes, which is a harder test than its main use: a bug fix changes behavior on purpose, often
|
|
134
|
+
only for a narrow set of inputs. **The full write-up, misses included, is in
|
|
135
|
+
[docs/EVALUATION.md](docs/EVALUATION.md).** In short:
|
|
136
|
+
|
|
137
|
+
- **Refactors:** defu's `isPlainObject` refactor was correctly reported as "no behavior change" across 6
|
|
138
|
+
functions. semver's "faster paths for compare" differs only for negative numbers, which semver never passes;
|
|
139
|
+
that difference is reported but collapsed as "unusual inputs". Real refactor commits are only 2 of the 39, so
|
|
140
|
+
most of the refactor evidence comes from the demo scenarios above.
|
|
141
|
+
- **Latest held-out set** (18 commits from 9 new libraries, picked by a fixed rule, code frozen before the run): it
|
|
142
|
+
found **3 of 13** verifiable bug fixes. The run also exposed tool bugs (a CommonJS export pattern, JSDoc
|
|
143
|
+
`{Object}`, cross-file impact). After fixing those, it finds 7 of 15; those numbers are no longer held out.
|
|
144
|
+
- On the first 21 commits it finds **11 of 13** fixes, with minimal examples. One is `withoutBase("///", "//")`, the
|
|
145
|
+
protocol-relative URL that a ufo security fix was about. Another is `isISO8601("1690360")`, an ordinal date
|
|
146
|
+
that a regex change started accepting.
|
|
147
|
+
- Most misses need two or three specific things at once (an option combination plus a particular shape of text),
|
|
148
|
+
or a real callback such as a sort comparator.
|
|
149
|
+
- It surfaced things the commit title did not say: validator's "restrict allowed characters" also started
|
|
150
|
+
accepting one- and two-character slugs.
|
|
151
|
+
- **Noise:** 91 % of the behaviors in the main list matter (79 % before the noise work). Differences that only
|
|
152
|
+
happen on inputs unlike the project's tests and call sites are collapsed and do not fail the check.
|
|
153
|
+
- It **could not verify 8 of 39 commits**: class methods, overloaded functions, UMD modules, class-instance
|
|
154
|
+
parameters and exported non-function values. Every report lists these explicitly.
|
|
155
|
+
- Each run took 0.5โ8 seconds.
|
|
156
|
+
|
|
157
|
+
## Quick start
|
|
158
|
+
|
|
159
|
+
### CLI
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx doppeldiff # your uncommitted changes vs HEAD
|
|
163
|
+
npx doppeldiff --base main # this branch vs main (merge base, like a PR)
|
|
164
|
+
npx doppeldiff --base main --claim "Fix rounding for prices over $1000"
|
|
165
|
+
npx doppeldiff --refactor # assert: no behavior change (exit 1 otherwise)
|
|
166
|
+
npx doppeldiff --write-tests # pin the examples in tests/doppeldiff/*.test.ts
|
|
167
|
+
npx doppeldiff --json report.json --markdown report.md
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
| Flag | Default | |
|
|
171
|
+
| ---------------------------------------- | --------------------- | ------------------------------------------------ |
|
|
172
|
+
| `--base` / `--head` | `HEAD` / working tree | Revisions to compare (via merge base) |
|
|
173
|
+
| `--claim`, `--claim-file`, `--refactor` | | What the change claims to do |
|
|
174
|
+
| `--runs` | `1000` | Max inputs per function |
|
|
175
|
+
| `--timeout` | `250` | Per-call timeout (ms) |
|
|
176
|
+
| `--budget` | | Total exploration time budget (s) |
|
|
177
|
+
| `--seed` | `1` | Every run is reproducible |
|
|
178
|
+
| `--fail-on` | `unclaimed` | Exit 1 on `unclaimed`, `any` or `never` |
|
|
179
|
+
| `--include` / `--exclude` / `--only` | | Limit files or functions |
|
|
180
|
+
| `--no-freeze` | | Do not freeze the clock / seed randomness |
|
|
181
|
+
| `--llm`, `--llm-model`, `--llm-base-url` | `off` | Optional LLM (below); `ollama` is free and local |
|
|
182
|
+
|
|
183
|
+
Node.js 22.13 or newer. Exit codes: `0` OK, `1` findings matching `--fail-on`, `2` tool error.
|
|
184
|
+
|
|
185
|
+
### GitHub Action
|
|
186
|
+
|
|
187
|
+
Copy the two files in [`examples/workflows/`](examples/workflows) to `.github/workflows/` on your default branch.
|
|
188
|
+
No secrets are required. doppeldiff posts one sticky comment per PR and updates it on every push.
|
|
189
|
+
|
|
190
|
+
It is safe for pull requests from forks. `doppeldiff.yml` runs the PR's code without secrets and with a read-only
|
|
191
|
+
token. `doppeldiff-comment.yml` posts the comment and never runs PR code. These have to be two files because
|
|
192
|
+
GitHub does not let a workflow trigger itself with `workflow_run`. See [docs/ACTION.md](docs/ACTION.md).
|
|
193
|
+
|
|
194
|
+
### MCP server for coding agents
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
claude mcp add doppeldiff -- npx -y doppeldiff mcp
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Two tools: `verify_change` (what did my edit change?) and `verify_refactor` (PASS, or FAIL with
|
|
201
|
+
counterexamples). An agent can check its own refactor before telling you it is done. Setup for Cursor and Codex
|
|
202
|
+
is in [docs/MCP.md](docs/MCP.md).
|
|
203
|
+
|
|
204
|
+
### Claim matching: free, no LLM
|
|
205
|
+
|
|
206
|
+
doppeldiff compares each behavior change with the PR title and description and labels it **claimed**,
|
|
207
|
+
**unclaimed** or neutral, using [documented deterministic rules](docs/CLAIMS.md):
|
|
208
|
+
|
|
209
|
+
- if the PR says it is a refactor, cleanup or performance change, or says "no behavior change", every difference
|
|
210
|
+
is unclaimed;
|
|
211
|
+
- if the PR names a function, that function's changes are claimed.
|
|
212
|
+
|
|
213
|
+
No LLM and no API key are involved.
|
|
214
|
+
|
|
215
|
+
Optionally, you can let an LLM read the description instead. A local model via
|
|
216
|
+
[Ollama](https://ollama.com) is free: `--llm ollama --llm-model qwen2.5:7b`. Any OpenAI-compatible server works,
|
|
217
|
+
and so do Anthropic or OpenAI with your own key. The LLM is never used unless you ask for it. It only labels
|
|
218
|
+
changes the engine actually observed, and its output is checked against the evidence. Details:
|
|
219
|
+
[docs/CLAIMS.md](docs/CLAIMS.md).
|
|
220
|
+
|
|
221
|
+
### Configuration (optional)
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
// doppeldiff.config.ts
|
|
225
|
+
import { defineConfig } from 'doppeldiff';
|
|
226
|
+
|
|
227
|
+
export default defineConfig({
|
|
228
|
+
exclude: ['src/legacy/**'],
|
|
229
|
+
compare: { epsilon: 1e-9 },
|
|
230
|
+
functions: {
|
|
231
|
+
// Declare the real input domain when inference from tests and call sites is not enough.
|
|
232
|
+
'src/internal/identifiers.ts#compareIdentifiers': {
|
|
233
|
+
where: ([a, b]) => [a, b].every((x) => typeof x === 'string' || (Number.isInteger(x) && x >= 0)),
|
|
234
|
+
},
|
|
235
|
+
parseConfig: { skip: 'reads files; covered by integration tests' },
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Limitations
|
|
241
|
+
|
|
242
|
+
v1 is deliberately narrow. Known gaps:
|
|
243
|
+
|
|
244
|
+
- **Exported functions only.** It does not test class methods, overloaded functions, generators, or UMD/IIFE
|
|
245
|
+
modules. They are listed as "not verified" in every report. Class methods are the biggest gap in practice.
|
|
246
|
+
- **Pure-ish code.** Functions that read files, call the network or depend on global state either fail in the
|
|
247
|
+
sandbox or are reported as nondeterministic. They are not mocked.
|
|
248
|
+
- **Generated inputs are not a proof.** "No behavior change across 10,000 inputs" is strong evidence, not a
|
|
249
|
+
guarantee. Bugs that need a very specific input (a valid ISO ordinal date, prototype-chain objects, `DataView`)
|
|
250
|
+
can be missed; see the [misses in the evaluation](docs/EVALUATION.md#why-the-misses-happen). Optional LLM input
|
|
251
|
+
suggestions help with some of these.
|
|
252
|
+
- **Input domain.** Generators follow the types, not the documentation. A function that is only ever called with
|
|
253
|
+
non-negative numbers may report differences for negative ones. Declare the domain in the config (`args`).
|
|
254
|
+
- **Signature changes.** If the parameter types change incompatibly, the old and new versions cannot receive
|
|
255
|
+
the same inputs, so the function is reported as skipped.
|
|
256
|
+
- **Slow paths.** Functions that loop for a long time on large inputs hit the per-call timeout. Exploration stops
|
|
257
|
+
after 20 such inputs, and the report says how many inputs actually ran.
|
|
258
|
+
- **The sandbox is a guardrail, not a security boundary.** The code under test runs with the Node.js permission
|
|
259
|
+
model, no network and a scrubbed environment, but you should only run doppeldiff on code you would run tests
|
|
260
|
+
for. See [SECURITY.md](SECURITY.md).
|
|
261
|
+
- **JavaScript and TypeScript only.**
|
|
262
|
+
|
|
263
|
+
## How it compares
|
|
264
|
+
|
|
265
|
+
| | What it does | How doppeldiff differs |
|
|
266
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
267
|
+
| **AI review bots** | Read the diff and give an opinion | doppeldiff runs the code and shows inputs and results |
|
|
268
|
+
| **[fast-check](https://fast-check.dev)** | Property-based testing library | doppeldiff is built on it; it adds change detection, loading both versions in isolation, generators derived from types and code, comparison semantics and reporting. You write no properties |
|
|
269
|
+
| **[RealDiff](https://github.com/issacnitin/RealDiff)** (formerly BehaviorDiff) | Runs your **existing tests** on both sides of a PR and diffs recorded values (6 languages) | doppeldiff **generates** inputs, so it finds differences on inputs no test covers. RealDiff sees real call sites; the two are complementary |
|
|
270
|
+
| **[receipts-cli](https://www.npmjs.com/package/receipts-cli)** | Enforces that an agent's fix comes with a test that is red on base and green on head, checks that the test has teeth, and adds agent guardrails | It verifies the test the author wrote for the claim; doppeldiff looks for changes nobody claimed or tested. They work well together: doppeldiff's `--write-tests` can produce such tests |
|
|
271
|
+
| **[ProofDiff](https://www.npmjs.com/package/proofdiff)** | Reports what changed, which related tests ran and what remains unverified (static by default) | ProofDiff tracks test evidence; doppeldiff creates behavioral evidence by running old and new code |
|
|
272
|
+
| **[Keploy](https://keploy.io)** | Records and replays API traffic as tests | API level with recorded traffic; doppeldiff works at function level on generated inputs |
|
|
273
|
+
| **[Testora](https://conf.researchr.org/details/icse-2026/icse-2026-research-track/10/Testora-Using-Natural-Language-Intent-to-Detect-Behavioral-Regressions)**, **DiffTestGen**, Mokav (research, Python) | LLM-generated tests that expose differences between versions; Testora classifies them against the PR text | The same idea, and evidence that it works. doppeldiff is a deterministic-first tool you can install (no key needed), for TypeScript, with an Action and an MCP server |
|
|
274
|
+
| **[CrossHair `diffbehavior`](https://crosshair.readthedocs.io/en/latest/diff_behavior.html)** (Python) | Symbolic execution finds inputs where two functions differ | Python only, and you target functions by hand; no git or PR integration |
|
|
275
|
+
| **BERT** (Jin, Orso, Xie 2010; Java) | The original "behavioral regression testing" | The academic origin of the technique |
|
|
276
|
+
|
|
277
|
+
## Roadmap
|
|
278
|
+
|
|
279
|
+
- Class methods (construct instances from constructor parameters, compare state after each call)
|
|
280
|
+
- Coverage-guided input generation (V8 coverage of the changed lines)
|
|
281
|
+
- Overloaded functions and UMD modules
|
|
282
|
+
- Side-effect sandboxing: virtual filesystem and recorded `fetch` for less pure code
|
|
283
|
+
- HTTP-level diffs for request handlers
|
|
284
|
+
- Python
|
|
285
|
+
|
|
286
|
+
## Contributing
|
|
287
|
+
|
|
288
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). The design is documented in
|
|
289
|
+
[docs/DESIGN.md](docs/DESIGN.md).
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
[MIT](LICENSE)
|