Developer tool · Static analysis
Patchwork
Detects when a Stripe API change affects a repository, proves the exact usages with the TypeScript compiler, rewrites them only where the fix can be proven safe, and verifies the patch in a sandbox before opening a pull request.
- Analysis
- TypeScript Compiler APIStripe usages proven against a trusted stub
- Verification
- E2B sandboxInstall, typecheck and tests, with no secrets
- Architecture
- Fastify, PostgreSQL, Next.jsGitHub App, leased job queue; not hosted
- Benchmark
- 73 cases, gated in CIFails on any false “not affected”; 45 test files
- Links
- Source

01
The problem
Stripe ships breaking API changes in dated versions. A changelog says what changed, but not whether a particular codebase uses the changed field, method or value. Searching for a property name finds strings, comments and unrelated objects as readily as real usages.
Patchwork answers the narrower question that matters to a team: does this change affect this repository, at this commit, and exactly where? When the answer is yes and a safe rewrite exists, it prepares the fix, proves it builds and tests in isolation, and only then opens a pull request. It never merges and never deploys.
02
Proving impact
Patchwork snapshots the default branch at an exact SHA and resolves the stripe-node version per workspace from package-lock.json or pnpm-lock.yaml. A cheap text search only nominates candidate files; it never decides anything. Each candidate is then compiled in a small in-memory TypeScript Program alongside a hand-written Stripe type stub, and the TypeChecker decides whether a reference really resolves to the changed Stripe symbol.
Compiling against a trusted stub instead of the installed SDK means Patchwork never has to run npm install on code it does not trust just to analyse it.
- Verdicts are deliberately asymmetric
- Affected requires positive evidence. Not affected requires explicit negative proof: an applicable SDK version, full coverage of the files, and zero matches. Anything unresolved, such as an
any-typed client, an unknown SDK version or a file that failed to load, becomes Uncertain rather than a confident guess in either direction. - Applicability before matching
- A usage only counts if the workspace is on an affected SDK major or pins an affected API version. Conflicting version evidence makes applicability unknown, which also resolves to Uncertain.
- A benchmark that gates CI
- 73 cases (44 control, 26 realistic, 3 taken from real public repositories) run inside the test suite. The safety gate fails the build on a single false Not affected or any unsafe certainty.
- benchmark cases in CI
- 73
- benchmark cases in CI
- false “not affected” allowed by the gate
- 0
- false “not affected” allowed by the gate
- Stripe change rules encoded
- 4
- Stripe change rules encoded
- test files across API, worker and web
- 45
- test files across API, worker and web

03
Fixing only what is provably safe
Remediation is deterministic, not generated by a model. The current recipe rewrites invoice.subscription to invoice.parent?.subscription_details?.subscription ?? null; the recipe registry records why the other three rules have no automatic fix yet.
After rewriting, the same compiler engine re-checks the result: the source still parses, the old pattern is gone and the replacement is present. If any single finding cannot be rewritten safely, the whole attempt is refused and the reason is shown, for example destructuring, optional chaining, a write position, an ambiguous access, a forbidden path such as a lockfile or .github/, or an oversized diff.

04
Verification and trust boundaries
Web
Next.js · TypeScript
Repositories, impact reports, findings, patches, verification and pull request state.
API
Fastify · PostgreSQL
GitHub App integration, analysis and patch generation, and the only place jobs are enqueued.
Analysis engine
TypeScript Compiler API
In-memory programs against a trusted type stub; tri-state verdicts with file and line findings.
Worker
E2B · GitHub App
Claims verification and pull request jobs from a Postgres queue, runs the sandbox, publishes branches.
- Repository code never runs in Patchwork’s processes
- The API and worker only read and parse text. Install, typecheck and tests run in an E2B sandbox built from a fixed template, whose only environment is CI=1 and NODE_ENV=test: no GitHub token, database credentials or Patchwork secrets. Install traffic is limited to the default registries, then the network switches to deny-all.
- A Postgres queue with leases, not silent retries
- Jobs are claimed with FOR UPDATE SKIP LOCKED and a renewed 20-minute lease. An expired lease is recorded as an infrastructure error rather than retried. Duplicate verification runs are blocked, and an existing branch is reconciled, never overwritten.
- Pull requests are gated twice
- Both the API and the worker require a generated patch, a passed verification, a verified diff whose hash matches the current diff, and no forbidden paths. The worker also refuses if the default branch has moved since analysis. The PR body lists only the checks that actually ran.
- AI explains; it never decides
- An optional explanation layer summarises established verdicts in plain English. It receives server-built evidence, never source code, returns schema-checked output, and writes to its own table, so it cannot change a verdict, a safety decision or a verification result.

05
Limits
- Four hand-encoded Stripe rules and one automatic fix recipe. GitHub, TypeScript and stripe-node only, one repository at a time, with analysis triggered by the user.
- Alias resolution is same-file and there is no interprocedural analysis, so a Stripe client imported from another file is reported as Uncertain rather than guessed.
- One documented false-negative path remains: data passed into a call from another file can slip past the argument filter.
- Verification can only run the typecheck and test scripts a repository defines; a repository without them gets a weaker check.
- Not hosted: running it needs the GitHub App, PostgreSQL, the API, the worker and an E2B key.