Deterministic by design
The app applies the edit, never the model. The same input always produces the same output.
Local and free
The merge runs in the Electron main process with no external call by default.
Never rewrites whole files
Only the matched spans change. Everything else in your file is preserved byte for byte.
Fails loud, never guesses
If a match is ambiguous or missing, the edit is rejected with a clear reason.
Why a deterministic engine
Small local models are great at describing a change but unreliable at reproducing long stretches of code verbatim. If you let the model rewrite an entire file, it tends to drop comments, reorder imports, or quietly mutate code far from the edit. Fast Apply removes that risk by making the model responsible only for the text of the change, while the app is responsible for where and how that text lands.The model only produces the edit text. The applier (
parseEditBlocks, applyEditBlocks, and mergeLazyEdit in src/main/services/smart-exec/morph.ts) is what mutates your file. This split is what makes every apply reproducible and reviewable.The two edit formats
Fast Apply understands two shapes of edit. Both keep the model honest by forcing it to anchor on lines that actually exist in your file.- Search and replace blocks
- Lazy edits
A block names the exact lines to find and the lines to put in their place:Rules:
- Several blocks per file are allowed and applied in order.
- An empty SEARCH means insert or create: the REPLACE is prepended to the file (or becomes the whole file if it was empty).
- An empty REPLACE means delete: the matched SEARCH lines are removed.
- Each block must match exactly once. More than one match is rejected as ambiguous.
How a search and replace merge works
When Fast Apply processes a search and replace block, it walks through tiers from strictest to most tolerant and stops at the first tier that produces a single, unambiguous match.Parse the blocks
parseEditBlocks scans the model output for SEARCH, =======, and REPLACE fences. Prose around the blocks is ignored, stray markdown code fences are stripped, and truncated blocks (missing a divider or end marker) are discarded rather than half-applied.Try an exact match
The engine counts occurrences of the SEARCH text in the file. Exactly one occurrence is replaced in place. Zero or more than one moves to the next decision.
Reject ambiguous matches
If the SEARCH text appears more than once, the edit is rejected with a reason like
SEARCH matches 3x (ambiguous), needs more context. Fast Apply will not pick one of several spots for you.Try a whitespace-normalized match
If there was no exact hit, the engine compares the SEARCH against line windows of the same size with trailing and internal whitespace normalized. It applies the change only when exactly one window is equivalent.
Try a safe anchored fuzzy match
As a last resort it scores each same-size window for similarity. It applies only when exactly one window scores at or above the similarity threshold. This catches near-misses from spaces or quotes without ever guessing.
How a lazy merge works
Lazy edits are merged bymergeLazyEdit. The update is split into snippets at each ellipsis marker, and each snippet is stitched back into your file using its anchor lines.
Segment the update
The update is split into contiguous snippets wherever an
... existing code ... marker appears. The marker is recognized across many comment styles, so //, #, --, /* */, <!-- -->, and ; all work.Pick the anchors
For each snippet, the first and last non-blank lines become the start and end anchors. Blank edge lines are skipped because they would match any empty line.
Locate the anchors in your file
anchorPositions searches for each anchor from strictest to most tolerant: exact line, trimmed line, then whitespace-normalized line. If those miss, it tries a structural key for common JS and TS declarations (so export default function foo can still anchor to export function foo when that name is unique), then a single high-similarity fuzzy line. Short lines like } or ) are never fuzzy-matched.Choose the best span
When several anchor pairs are possible, the engine picks the span whose length is closest to the snippet length. This avoids the classic bug of closing on the first nested
} instead of the function’s real closing brace.Preserve everything outside the span
The original lines before each snippet (the head and the gaps between snippets) and after the last snippet (the tail) are copied through unchanged. Only the matched spans are replaced.
In a lazy merge the real anchor lines from your file are kept, not the model’s reformatted versions. So even when the model adjusts spacing on an anchor, your file’s exact line survives the merge.
Why ambiguity is rejected
The whole engine is built on one promise: if it is not sure, it does not write. That promise shows up in several places.Multiple matches mean stop, not pick
Multiple matches mean stop, not pick
When a SEARCH or anchor matches more than once, the engine refuses rather than choosing. A wrong choice would edit the wrong region and pass review unnoticed. Rejection forces a more specific edit or an escalation.
Tiers go strict to loose, and stop early
Tiers go strict to loose, and stop early
Each matcher tries exact first, then trimmed, then normalized. The first tier that finds any match wins, so an exact match is never diluted by looser matches lower down.
Fuzzy is anchored and capped
Fuzzy is anchored and capped
Similarity matching only applies with a single window above a high threshold. It exists to absorb whitespace and quote noise, not to invent content.
Clear failure reasons
Clear failure reasons
Every rejection carries a human-readable reason: ambiguous match, anchor not found, or suspicious shrinkage. That reason is what lets the orchestrator decide to repair or escalate.
How Fast Apply pairs with the Forge
The Forge is the bundled local model (Qwen2.5-Coder) that executes code changes. Fast Apply is its hands. The Forge writes a lazy edit; Fast Apply merges it deterministically; only if the merge cannot find its anchors does the work move up a tier.A premium model plans
A premium orchestrator reads your repo and decides what should change, then delegates the concrete edit to the Forge.
The Forge emits an edit
The Forge produces a short lazy edit (or search and replace blocks), keeping only a couple of anchor lines around each change.
Fast Apply merges locally
The deterministic applier merges the edit into your file at zero API cost. Most edits resolve here.
There is an optional hosted fallback (
morphFastApply, the morphllm.com API) that can merge a lazy edit before premium escalation. It is off by default and only activates when a MORPH_API_KEY is present in the environment. It is not exposed in the UI, because Orkestral is local-first.What to do next
Meet the Forge
Learn how the bundled local model executes code changes at zero API cost.
Review changes
See how merged edits surface as diffs you can approve before they hit your repo.
Configure tools
Manage Fast Apply behavior and optional providers from the Tools area.
How orchestration works
Understand how planning, delegation, and execution fit together.