AI Coding Guides Deep Dives
SEARCH/REPLACE • Byte-exact match • Edit-gate review • DeepSeek-native

Reasonix: Byte-Exact SEARCH/REPLACE with Edit-Gate Review

Reasonix enforces a strict byte-for-byte SEARCH/REPLACE protocol: the SEARCH block must match the file content exactly, empty SEARCH creates new files, and every edit passes through a review gate before landing on disk. No fuzzy matching, no silent corrections — just exact semantics and predictable outcomes.

Why It Belongs in a File-Editing Blog

Reasonix is a DeepSeek-native coding agent engineered around DeepSeek's prefix-cache mechanic. That means every layer — including file editing — is designed for stability. The editing protocol is not an afterthought or a cascade of forgiving fallbacks. It is a deliberate choice to prefer loud failure over silent corruption.

In a landscape where many agents pad their edit engines with 4, 7, or 9 matching layers, Reasonix draws the opposite line: one exact match, period. If the SEARCH block does not appear byte-for-byte in the current file, the edit is rejected with a clear status code. The model gets precise feedback and retries with better context. This is the DeepSeek-native approach: lean on the model's ability to self-correct rather than on the framework's ability to guess.

The SEARCH/REPLACE Protocol

The core of Reasonix's editing engine lives in src/code/edit-blocks.ts. It defines a simple, parseable block format that the model emits in its response text:

Edit Block Format
path/to/file.ts
<<<<<<< SEARCH
existing code to match exactly
=======
new code to replace it with
>>>>>>> REPLACE

The parser uses a single regular expression to extract these blocks from the model's output. The regex anchors each block with ^ (line start) and the m flag so that a file containing the literal text <<<<<<< SEARCH does not accidentally parse as a real edit block.

Condition Status Behavior
SEARCH non-empty, exact match found "applied" First occurrence replaced on disk
SEARCH empty, file does not exist "created" New file written with REPLACE content
SEARCH non-empty, no match in file "not-found" Rejected; model must provide better context
SEARCH empty, file already exists "not-found" Empty SEARCH only creates new files
SEARCH non-empty, file missing "file-missing" Model told to create with empty SEARCH instead
Path escapes rootDir "path-escape" Refused on safety grounds
Filesystem operation throws "error" IO or permission error
🎯

No fuzzy matching, ever

There is no whitespace-normalized matcher, no anchor fallback, and no diff rescue pass. The only leniency is line-ending adaptation: the engine normalizes \r\n vs \n to match the target file's actual line endings. That is it. "A silent wrong edit beats a missing one."

Each edit replaces only the first occurrence of the SEARCH text. If the same string appears multiple times, the engine refuses to guess which one to change — the model must include more surrounding context to disambiguate. Auto-expanding to replace-all is deliberately avoided as a footgun.

For whole-file rewrites, Reasonix provides toWholeFileEditBlock(), which reads the existing file content as the SEARCH block (or empty string if the file does not exist) and wraps the new content as REPLACE. This lets the model emit a single block that covers the entire file when needed.

The Edit-Gate Review Mechanism

Reasonix separates the parsing of edit blocks from their application. Blocks are parsed from the model's response text first, then presented through an interactive review gate:

Command Behavior
/apply Confirms pending edits; writes to disk after user approval
Auto-apply When configured, edits land immediately without prompting
/undo Restores pre-edit snapshots; created files are deleted
/history Shows the sequence of applied edits
/show Displays before/after diff for a specific edit

Before any edit is applied, Reasonix captures a snapshot of each affected file's content. The snapshot is de-duplicated by path — one "before" per file even when multiple blocks target the same file. These snapshots power /undo: if a file was created by an edit, undo deletes it; if it existed before, undo restores the exact pre-edit content.

Each edit produces a diff display showing the change with line numbers, so the user sees exactly what changed before approving. The edit_file tool reports both the character-length delta (e.g., 45→120 chars) and a rendered diff block.

⚠️

First-occurrence only

The engine replaces only the first occurrence of SEARCH text. If the same string legitimately appears in several unrelated places, the model must emit separate blocks with more surrounding context for each target. This prevents accidental mass replacement.

Tool-Call Repair Pipeline

Before edits are even parsed, the model's tool-call output passes through a four-stage repair pipeline. This ensures the edit blocks that reach the parser are structurally sound:

Stage What It Does Problem It Solves
flatten Converts schemas with >10 leaf params to dot-notation; re-nests after dispatch DeepSeek drops args on deeply nested or wide schemas
scavenge Regex + JSON parser sweeps reasoning_content for forgotten tool calls Model emits tool JSON inside reasoning but forgets the tool_calls field
truncation Detects unbalanced JSON; repairs by closing braces or requesting continuation Model output gets cut off mid-JSON by token limits
storm Suppresses identical (tool, args) tuples within a sliding window; injects a reflection turn Model loops, emitting the same edit call repeatedly

The pipeline runs in order: scavenge → truncation → storm. Schema flatten runs at loop construction time, not per-turn. The scavenge stage bounds its input to 100 KiB to prevent ReDoS on adversarial input. The storm breaker tracks recent calls per session and clears read-only entries after mutating calls, so a post-edit re-read is not flagged as a repeat.

Sandbox and Binary Protection

Reasonix enforces strict filesystem boundaries and defends against accidental binary file corruption:

Defense Mechanism
Path sandboxing Every path is resolved against rootDir; paths escaping the root are refused with "path-escape" status
Binary detection Extension blocklist (common binary types) plus NUL-byte sniff in the first 8 KiB of file content
Outline mode Files exceeding 512 KiB are shown as structure outlines instead of full content, with a configurable threshold
Hard size cap Files above 32 MiB are refused entirely — outline mode would have to slurp the whole file to scan it
Session approval Read access to directories outside the root requires explicit user approval, cached for the session

The sandbox check uses resolve() to normalize .. segments, then verifies the resolved target starts with the normalized root path. The platform-aware separator (Windows \, POSIX /) ensures correct comparison on both systems. Snapshot restore also runs the same escape check, so /undo cannot write outside the sandbox either.

🔒

Layered binary defense

Binary protection works on two levels: the extension blocklist catches known binary types immediately, and the NUL-byte sniff catches mislabeled files (e.g., a binary with a .txt extension). UTF-16 files are accepted as a known false positive since they are rare in source code.

How It Compares

Repo Main editing idea Failure strategy
Cline Custom search/replace plus layered fallbacks Try more forgiving matchers until something lands
Codex / Claude Code Patch-oriented mutation surface Patch parsing plus runtime validation
OpenCode Exact edits plus a large fallback stack Nine matching layers before giving up
Reasonix Byte-exact SEARCH/REPLACE, no fallback Return precise status codes; model retries with better context
ADK-Rust Provider-native wrappers Return precise errors and let the caller or model retry

What I Would Steal from It

Exact match is a feature, not a limitation

Reasonix proves that a single, unforgiving exact matcher works when paired with precise failure signals. The model learns to include more context rather than the framework learning to forgive less context.

Edit-gate with snapshots is the right UX

Capturing before/after snapshots and gating edits behind /apply gives the user a clean undo path. The de-duplication by path prevents redundant reads, and the diff display makes every change auditable before it lands.

Repair pipeline catches model failures before they hit the editor

Scavenge, truncation repair, and storm suppression form a defense layer that catches the model's structural mistakes before they become edit-block parsing errors. This keeps the editing surface clean without adding matching complexity.

Sandbox enforcement at the boundary

Path escape checks run at both apply time and snapshot restore time, with the platform-aware separator handling Windows and POSIX correctly. Binary detection combines extension blocklists with content sniffing — simple, layered, effective.