Fix a finding
emendant fix turns scan findings into patch files. It does not edit your source files or Git history. You review and apply each patch yourself.
Can AI generate safe patches for SDK migrations?
Section titled “Can AI generate safe patches for SDK migrations?”Not on its own authority. A generated patch needs your review before you apply it. Structural checks, your project’s typecheck, and your tests give bounded evidence that a patch did what it claimed. They are not a guarantee that the migration is complete or that the code behaves as before.
How Emendant treats that evidence:
- Deterministic fixes come first. Most patches are a mechanical transform on your machine, such as one name for one name. They use no model. See fixes written in two ways.
- A model writes only when you name a provider. Migrations with no safe mechanical transform stay unfixed unless you pass
--model-provider. The patch header and report say that a model wrote it, and it goes through the same checks. - A patch can be declined. Emendant withholds a patch as
failedwhen the checks show it is wrong, and asinconclusivewhen the machine could not decide. The report says which and why. See read the evidence. - A check that did not run is not a pass. With no typecheck or test script, a patch is graded
structurally checkedat best. Emendant does not invent a command.
A green result also says nothing about changes the feed does not report. One recorded migration shows both sides.
Preview the changes
Section titled “Preview the changes”Start with a dry run:
npx emendant fix --dry-runThis prints each candidate diff without writing .emendant/. It also does not run your repository’s checks, install a target SDK release, or call a model. Emendant applies the edits in memory and matches the workspace again, so a successful preview is structurally checked.
A preview shows what Emendant plans to change. It does not show that the result compiles, passes tests, or works with the target SDK release.
Generate the patches
Section titled “Generate the patches”Run fix mode without --dry-run when the preview is correct:
npx emendant fixEmendant writes the result below .emendant/:
.emendant/ report.json report.md patches/ <finding>.patch all.patchall.patch is present only when two or more offered patches also pass together.
The command first checks every patch in memory. It then finds a typecheck and test command that the repository already owns. When it finds checks, it runs them against a patched copy outside your working tree. For a pending upgrade, it also uses your package manager to install the target SDK release into a source-free preparation directory before it checks the copy.
Emendant does not invent a command when the repository has no typecheck or test script. It still writes a patch that passes the structural check, and grades it structurally checked.
Read the evidence
Section titled “Read the evidence”Open .emendant/report.md, then read the header and diff inside the patch. Each patch has one of four grades:
| Grade | What Emendant proved |
|---|---|
test verified |
The repository’s tests passed with the patch. A selected typecheck also passed. |
typechecked |
The repository’s typecheck passed. No test command ran. |
structurally checked |
The edits applied and matching found no new affected use. No code compiled or ran. |
unchecked |
Emendant could not obtain structural, type, or test evidence. |
A patch can be withheld after Emendant creates its edits:
failedmeans the baseline was green and the patched copy was red, or the structural check found that the patch did not do what it claimed. This result is about the patch.inconclusivemeans the machine could not decide. For example, the baseline was already red, an install did not complete, or a command timed out. This result is not evidence that the patch is wrong.
The report names the checks that ran, where they came from, and why a patch was written or withheld. See the CLI reference for flags that override check discovery or keep the verification copies.
Choose a patch
Section titled “Choose a patch”Emendant writes one patch per finding. When two or more offered patches also pass together, it writes all.patch.
- Use a per-finding patch when you want to apply one change.
- Use
all.patchwhen you want every offered change. Do not apply all of the per-finding patches one after another. Nearby patches can share context and fail when applied in sequence.
Review the diff before you apply it:
git apply --check .emendant/patches/all.patchgit apply .emendant/patches/all.patchFor one finding, replace all.patch with that patch’s filename. git apply --check confirms that the patch still fits your current files. git apply makes the source changes. Review the resulting Git diff and run any additional checks your project needs.
Fixes written in two ways
Section titled “Fixes written in two ways”Most patches use a deterministic transform on your machine. They need no model, account, or model request.
Some migrations have no safe mechanical transform. These findings stay unfixed unless you name a model provider:
npx emendant fix --model-provider codex # the login your Codex CLI already hasnpx emendant fix --model-provider claude # the login your Claude Code CLI already hasWithout --model-provider, and without a provider in emendant.json, nothing is sent anywhere. When you name a provider, Emendant sends a bounded excerpt of the matched file directly from your machine to that provider. The result goes through the same bounds and verification as a deterministic patch. The report and patch header state that a model wrote it.
See Security and privacy for the data boundary and the configuration reference for persistent provider and verification settings.
A recorded example
Section titled “A recorded example”Everything on this page above is a general description. This section is a recorded result, taken from one measured AI SDK 6 to 7 migration of steel-dev/atlas. Nothing below is illustrative.
Pinned details. Repository steel-dev/atlas at commit 7aea679b740afa97b26617e53e5c1f2e1429eec5, emendant@0.5.0 from npm, feed snapshot 2026-09-23.8, Node.js 22.23.3, run on 29 September 2026. The example page gives every command to repeat it.
The proposed edit. npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --dry-run printed one deterministic patch, +7 −7 in 2 files, graded structurally checked. It renames stepCountIs to isStepCount:
import { generateText, type ModelMessage, stepCountIs } from "ai";import { generateText, type ModelMessage, isStepCount } from "ai";The checks and results.
| Check | Result |
|---|---|
| Structural check | Passed. The edits applied and matching found no new affected use. |
| Verification run 1 | Withheld as inconclusive. The baseline npm run test was already red, because one test file could not listen on 127.0.0.1 in the sandbox. |
| Verification run 2 | Withheld as inconclusive. With that file excluded, the baseline passed, but the typecheck failed on a copy that had ai@7.0.0 and the old provider packages. |
Both withheld results were about the verification environment, not the patch. The patch applied cleanly, and the repository’s lint, typecheck, 262 tests and build all passed after the full migration.
The work that remained. Emendant does not change dependency versions or run lint, so those were manual. The second finding, a provider type change, needs a model provider, and none was named, so it got no patch. Three changes were not reported at all:
- The
wrapLanguageModelreturn type changed toLanguageModelV4. The typecheck found it, and the edit touched 13 files. result.response.messagesbecame the last step only. The types are unchanged, so the typecheck and all 262 tests passed. A mock-model run on both versions showed it.- A
cachedInputTokensread arrived as a function parameter, which the matcher does not follow.
What this shows. A patch that was graded only structurally checked was correct, and a clean toolchain still missed a behaviour change. It is one repository and one release. It does not measure how often Emendant finds or misses a change, and the assisted patch was not tested. Read the full example for the commands, outputs and limits.