research · v0.12 · Splice-first preflight LIVE + Morph fast-apply default + mergiraf structural merge (write-router@0.0.10-verify-sota); wave-4 benchmark executed (2026-08-03) — 34.4× p50 batched vs sequential, all gates PASS; seams landed: lazy_diff original auto-hydration, multi-file batch lazy_diff (one ladder/batch), xxHash64 CAS gate, atomic-rename write_mode, code-map span anchors

Write Strategies for Coding Agents

The 2026 write-side of the memory stack: fast-apply models, atomic patch tools, and structural engines composed by a per-task routing rubric — because subagents issuing many sequential single-line edits is the failure mode most worth fixing.

published 2026-07-12 · updated 2026-08-03
authors: Nexartis
tags: agents · editing · fast-apply · patches · codemods · rubric
Reference design and current defaults

The ozzydev-native-write-mcp reference design is fixed in ADR-008: the D8 rubric below is the routing policy verbatim, every write emits an Ed25519 receipt (interface owned by ADR-009), and the router runs a deterministic splice preflight before delegating to the fast-apply engine. As of write-router@0.0.4 the fast-apply engine is Morph — Morph strictly dominated on accuracy, p50, and p95 in the r5 real-edit bakeoff and refused zero requests across every measurement round. Relace stays fully implemented as the one-flip alternate. This page narrates and links; ADR-008 is canonical for the decisions, and §Measured results below quotes the r5 artifact verbatim.

Executive summary

Working with Large Codebases covers how agents find code; Agentic Velocity Without Quality Loss covers the write layers at a category level. This paper is the operational rubric: given a specific edit, which write tool should the agent reach for, and why. The failure mode we are actually trying to fix is not "slow individual edits" — it is subagents issuing 20 sequential single-line edits when one atomic batch or one fast-apply merge would have done the same work in a single turn.

The 2026 stack decomposes into three families: fast-apply models (Morph, Relace) that merge lazy-diff markers server-side at 10k+ tok/s; atomic patch tools (OpenAI apply_patch V4A, our weave-patch, Claude Code MultiEdit) that batch N edits with all-or-nothing semantics; and structural engines (ast-grep, Codemod JSSG, serena) that apply one rule deterministically across N files. Anthropic Programmatic Tool Calling is the free, no-new-vendor way to eliminate sequential-edit spam wherever the harness supports it.

Bottom line

There is no single best write tool — SWE-Edit (arXiv 2604.26102) shows per-task edit-format routing is a Pareto win (+2.1pp resolve, −17.9% cost) vs any fixed format. The rubric below (a–h) is our routing policy; the buy-list (D9) is what we evaluate next; the evidence gaps at the bottom are the two internal benchmarks we plan to build because nobody else has.

The problem this paper exists to fix

Subagents issue many sequential single edits. That is the observable symptom. The 2026 stack gives us three ways to fix it: fast-apply models absorb multiple in-file changes as one merge; atomic patch tools batch N cross-file edits as one call; structural engines apply one rule to N files. Combined with Anthropic Programmatic Tool Calling — which batches N tool calls in a client-executed code block — the sequential-edit failure mode has more than one cure. The question is which cure fits which change, and that is what this page routes.

Fast-apply commercial engines

Small (3–8B) purpose-trained merge models that ingest a base file plus a lazy-diff marker ("... existing code ...") and emit the merged file. The tok/s and accuracy numbers below are vendor claims — unverified externally until we bench them (evidence gap 1).

  • Morph v3-large
    Throughput: ~4,000 tok/s (claimed). Accuracy: ~98% merge accuracy (claimed). Context: 262K. Pricing: $0.90 / $1.90 per M (in / out).

    Frontier accuracy target; slowest of the Morph tier. Vendor benchmarks only.

  • Morph v3-fast
    Throughput: ~10,500 tok/s (claimed). Accuracy: vendor-cited near-parity with v3-large. Context: 262K. Pricing: $0.80 / $1.20 per M.

    The tok/s number that anchors most Morph marketing. Unverified head-to-head.

  • Morph auto
    Throughput: router (large ↔ fast). Accuracy: router-dependent. Context: 262K. Pricing: $0.85 / $1.55 per M.

    Managed selection between the two Morph tiers; useful when tolerance-per-edit varies.

  • Relace Apply 3
    Throughput: ~10,000 tok/s (claimed). Accuracy: vendor-cited parity with Morph fast. Context: 256K. Pricing: $0.85 / $1.25 per M (via OpenRouter).

    ZDR by default on the direct API; OpenRouter path gets standard OpenRouter terms. Universal merger (lazy diff + udiff + string-replace in one). Training recipe partially open-sourced.

  • Cursor fast-apply
    Throughput: harness-integrated. Accuracy: not published. Context: harness-managed. Pricing: not sold standalone.

    Fireworks-served inside the Cursor harness; no public API, no standalone offering. Included here for completeness — it is not a build-target.

Batch / atomic / structural tools

The non-fast-apply side of the write stack. Deterministic, cross-file, and cheap to reason about.

  • OpenAI apply_patch V4A — atomic multi-file patch (native). Native multi-file patch format; the canonical V4A spec that other tools track. Ships in the OpenAI tools guide as the reference implementation.
  • weave-patch (ours) — atomic multi-file patch. V4A-compatible batch tool: view/read/map/create/write/update/move/delete in one atomic call, dry_run preview, fuzzy anchoring, structured failure diagnostics. Accepts *** Begin Patch blocks unchanged. Our default for interdependent multi-file writes.
  • Claude Code MultiEdit — single-file atomic multi-edit. Batches N edits into one file with atomic semantics; either all edits land or none do. Scoped to a single file — not a substitute for cross-file atomicity.
  • Aider udiff — diff-format edit contract. Aider’s move from search-and-replace to unified-diff edits took refactor-benchmark pass rate from 26% to 59% (Aider blog). Direct evidence that edit format is a first-class variable, not a stylistic preference.
  • Codemod MCP + JSSG — cross-repo structural campaigns. JS/TS structural grep with semantic references via oxc_resolver — resolves imports across a monorepo/campaign, then applies a tested transform deterministically. The right tool for SDK-breaking migrations that must land in dozens of downstream repos.
  • Anthropic Programmatic Tool Calling — sequential-edit-spam fix (free). Lets the model batch N tool calls in a code block executed by the client, rather than issuing them one turn at a time. Fixes the "subagent issues 20 sequential single edits" failure mode without introducing a new tool — enable it wherever the harness supports it. See docs.anthropic.com text-editor-tool + the advanced-tool-use engineering post.

Evidence anchors

The papers and vendor pages that back the rubric. Every recommendation below traces to one of these; anything not backed by an entry here is asserted, not measured, and should be treated accordingly.

  • SWE-Edit (arXiv 2604.26102) — per-task edit-format routing — Routing edit format per task (search-replace vs full rewrite vs diff) is a Pareto win: +2.1pp resolve rate, −17.9% cost vs any fixed format. Direct empirical justification for the rubric approach on this page.
  • Aider polyglot leaderboard — edit-format findings — udiff vs search-replace vs whole-file: 26% → 59% refactor pass rate when switching from search-replace to udiff. Format dominates model choice on the refactor slice.
  • Search-and-replace failure rate — 84–96% first-try success across the fast-apply vendors’ own reports, with 2–3.5× retry turns when it fails. The retry loop is where most "why is my agent slow" complaints come from.
  • Full-file rewrite cost — 10–15× the token cost of a targeted edit for the same change, at ~80 tok/s on frontier models — accurate but too expensive to be the default.
  • Morph Fast Apply — 98–100% merge success with frontier planners; 10,500 tok/s v3-fast; 262K context. Vendor benchmark, unverified externally.
  • Relace Apply 3 — ~10k tok/s via speculative decoding (merge output ≈ copy of input → long accepted drafts); 256k context; ZDR default; on OpenRouter.

The write-strategy rubric

Per-task routing: match the change to a row, use the primary tool, fall back to the secondary only when the primary is unavailable or its precondition fails. This page is the canonical home for the rubric; the workspace AGENTS.md §Write-strategy rubric mirrors it in condensed form, and the memory-tooling decision log tracks the routing directive as D8.

Routing table
  • (a) Single small edit — primary: Native str_replace (harness edit tool); fallback: weave-patch update.
    One line to a handful of lines; fast-apply and codemod overhead is not worth it. Fallback covers the "context drifted, native fails" case with weave’s fuzzy matching.
  • (b) Scattered edits in one large file — primary: Morph fast-apply; fallback: Claude Code MultiEdit.
    Full-file rewrites are 10–15× the token cost; sequential str_replace fails 4–16% at drift. Morph merges lazy-diff markers server-side at 10k+ tok/s. MultiEdit is the atomic same-file fallback when Morph is unavailable.
  • (c) Mechanical change across many files — primary: ast-grep preview → apply; fallback: Codemod JSSG (with semantic references).
    One rule sweeps N files deterministically; zero per-file LLM variance. Codemod JSSG steps up when the rewrite needs import-graph awareness (e.g. SDK export renamed across a monorepo).
  • (d) Large partial rewrite (>200 lines) — primary: Morph v3-large; fallback: weave-patch update (multi-hunk).
    Big rewrites are where fast-apply pays the most: v3-large trades a bit of speed for higher accuracy at scale. Fallback keeps a hand-authored diff path when the merge model is unavailable or a key is not configured.
  • (e) New file — primary: weave-patch create; fallback: .
    Atomic create with dry_run preview and structured failure reporting; nothing to merge, so fast-apply adds no value.
  • (f) Interdependent multi-file refactor — primary: Codemod JSSG OR weave atomic batch; fallback: serena rename (single-repo symbol renames).
    When the edits must land together or not at all. Serena is the right tool for a single-repo symbol rename because the language server has the exact reference set; weave/Codemod cover the cases the LSP does not (multi-repo, non-symbolic patterns).
  • (g) Docs / prose — primary: Fast-apply OR native; fallback: .
    Prose is forgiving of merge errors and fast-apply’s big context handles long docs cheaply. Native is fine when the diff is small.
  • (h) Generated / data files (JSON, YAML, lockfiles) — primary: weave write (overwrite); fallback: .
    NEVER fast-apply. Fast-apply’s ~2% error rate is catastrophic on structured data — a single misplaced brace corrupts a lockfile or config. Overwrite the whole artefact from the generator, or leave it alone.

Cross-cutting rules (apply on top of the rubric)

  • dry_run FIRST on any batch touching more than three files, no matter which tool.
  • Never mix write tools inside a single logical change — pick one, land it, verify, then move on.
  • git diff after every batch. If the diff looks wrong, roll back before running validate; validate results on a wrong diff waste more time than the rollback.
  • Generated / data files (h) NEVER go through fast-apply. Overwrite from the generator or leave alone.
  • Fast-apply keys stay in the console Secrets panel (ADR-006). Never in tracked files, never in shell history.

Buy-list for the max-performance evaluation

Shopping list for the in-flight bakeoff that will pick a default per slot. Each item's status is evaluation/procurement pending until benchmark numbers arrive. Tracked as D9 in the memory-tooling decision log.

  • Morph Enterprise + WarpGrep Pro — Highest-published fast-apply throughput plus the WarpGrep search subagent bundled in the same server. Enterprise tier provides dedicated capacity for the benchmark. Status: evaluation/procurement pending.
  • Relace hosted → VPC — Direct A/B against Morph on a real workload; the VPC upgrade keeps code in tenancy for regulated repos. Status: evaluation/procurement pending.
  • Codemod BYOC — Cross-repo campaigns with tested, versioned transforms — the deterministic path for SDK-breaking migrations. Status: evaluation/procurement pending.
  • Anthropic Programmatic Tool Calling (free) — Enable wherever the harness supports it. Fixes sequential-edit spam without adopting a new vendor.

Measured results — 31 real production edits (r5, 2026-07-16)

Current-state result. r5 executes this page's own §gaps plan — a merge-accuracy task set drawn from our own edit traces. Corpus: 31 single-file production commits mined from 6 workspace repos (cubicube-com 11, nexartis-ozzydev 6, pegasus 3, SDKs 4) plus a 7-fixture CommitPackFT external slice (BigCode; the corpus Osmosis-Apply-1.7B is benchmarked on) — the first external-corpus anchor in this evidence set. Agent-authored commits excluded; lazy-marker updates synthesized as ±3-line-context hunks with comment-style elision markers; rung-3 property checks derive contains / order / js_syntax. Canonical artifact: mcp/servers/ozzydev-native-write-mcp/experiment/results-r5-2026-07-16-mrnv8hrn.json.

arm         overall     p50    p95     tokens in/out
morph.auto  31/31 100%  677ms  1550ms  93,271 / 76,648
relace.auto 30/31  97%  810ms  1726ms  84,628 / 71,158
splice.v1   23/31  74%    0ms     6ms        0 / 0

splice taxonomy: 3× splice_unanchored_segment, 5× splice_ambiguous_anchor
by category:     (b) 12/14 = 86% · (d) 6/11 = 55% · (g) 5/6 = 83%
CommitPackFT:    7/7 merged, 6/7 verified — matches the internal (b) slice

decision: morph wins by strict dominance (accuracy 1.000 ≥ 0.968;
          p50 677 ≤ 810; p95 1550 ≤ 1726)

Two things fell out of this measurement, both live in the router:

  1. Splice-first preflight (74.2% at $0 / 0ms). 74.2% of real edits resolve deterministically before any vendor call. The classified-failure residual (16% ambiguous anchor + 10% unanchored segment) hands off loud to the fast-apply engine — no silent fallbacks; every hop is a classified, telemetered handoff. A combined-system latency/cost artifact from the live router (splice-first + morph residual, end-to-end on the r5 corpus) is queued; until it is measured and persisted, the per-arm rows above are the only load-bearing numbers.
  2. Engine flip relace → morph. Morph won every decision round (r3, r4, r5) and refused zero requests across all rounds; Relace is the only vendor with availability failures (7× total: 6× HTTP 429 in r3, 1× HTTP 400 in r5). fast_apply_engine = morph as of write-router@0.0.4. Relace stays fully implemented as the one-flip alternate.

Known weak spot: the ambiguous-anchor class — 16% of r5, concentrated in category (d) large rewrites where splice resolves only 6/11. Coverage plan lives in §Evidence gaps below. Splice engine source: mcp/servers/ozzydev-native-write-mcp/src/adapters/splice.ts.

r5b follow-up (2026-07-16): tree-sitter scope-anchor rung. A third deterministic anchor rung (exactwhitespacescope, source src/adapters/splice-scope.ts) parses the original with tree-sitter and resolves an ambiguous anchor only when exactly one candidate range uniquely contains the update segment's scope hints — ties and no-grammar languages decline loudly back to the classified failure. Splice-only re-run of the r5 corpus: 24/31 = 77.4% verified (ambiguous-anchor 5 → 4), zero wrong merges, zero regressions. Canonical artifact: experiment/results-r5b-2026-07-16-scoperung1.json; decision record in ADR-008 amendment r5b.

Local-arm bench (2026-07-16): Osmosis-Apply-1.7B. The Apache-2.0 Qwen3-1.7B apply-model (Q4_K_M GGUF via Ollama, deterministic seed) scored 28/31 (90.3%) on the r5 corpus — below morph 31/31 and relace 30/31 — but resolved 5 of the 7 splice-residual fixtures (all 3 unanchored-segment, 2 of 4 ambiguous-anchor) at $0. Latency disqualifies it from the hot path: p50 27.1s / p95 103.6s on Apple Silicon, 10–50× the vendor arms. Both content failures were caught by rung-1 verify (the ladder works); verdict: viable as a strictly opt-in rank-3 ambiguity fallback behind the vendor arm, pending a higher-quant rerun and an integration decision. Canonical artifact: experiment/results-osmosis-2026-07-16-run2.json.

Measured — wave-4 batch-vs-sequential + corruption (2026-08-03)

The atomic-tool corruption benchmark and the batch-vs-sequential latency benchmark (evidence gaps 1–2 above) were built and executed as wave-4 of the classic-unix-write-optimization workstream: 6 write arms × cold/warm × workload axes on a RAM disk, real WriteRouter in-process, p50/p95/p99 + SDI. Prereg: mcp/servers/ozzydev-native-write-mcp/experiment/PREREG-wave4-write-benchmark.md; runner experiment/wave4-write-benchmark.mts; results experiment/results-wave4-2026-08-03-400cefed.json. The dominant cost the benchmark exposes is exactly the one the DESIGN doc predicted: the verify ladder runs N times for N sequential files but once for a batched call (Q4).

arm                      @8 files (p50/p95/p99, SDI)       @32 files (p50/p95/p99, SDI)
seq.native               426 / 464 / 477 · SDI 1.34        1825 / 2029 / 2059 · SDI 1.15
batch.native              45 /  48 /  48 · SDI 1.00          53 /  56 /  57 · SDI 1.33
batch.unified_diff        50 /  55 /  55 · SDI 1.00          57 /  59 /  59 · SDI 1.00
batch.merge_structural    59 /  61 /  63 · SDI 2.00          75 /  78 /  78 · SDI 1.00
seq.whole_file           386 / 397 / 433 · SDI 4.27        1822 / 3164 / 5226 · SDI 2.54
batch.lazy_diff (splice) 389 / 612 / 664 · SDI 1.23        1861 / 1943 / 1998 · SDI 1.67

verdict (all gates PASS):
  F1 batched vs sequential @32f: 1825 / 53 = 34.4× p50 (acceptance ≥3.5×)
  F2 SIGKILL corruption: 0 torn files across 60 trials (baseline clean)
  F3 SDI ≤6: max 4.27 (seq.whole_file@8) — batched arms ≤2.33
  F4 whole-file bounded: 1822 / 53 = 34.4× slower than batch.native @32f

Seams landed on this evidence (write-router@0.0.10-verify-sota, 249/249 tests): lazy_diff original auto-hydration (omit → router hydrates from its own snapshot; stale supplied copy → write_cas_mismatch); multi-file batch lazy_diff (files[], one verify ladder per batch); xxHash64 full-file CAS gate (pre_condition.fingerprint, dependency-free src/xxhash64.ts); atomic-rename write_mode default (same-dir temp + fsync + rename, preserves mode); code-map span anchors (jit_context.anchor_spans resolve splice ambiguity at $0 before escalation). The batch-vs-sequential 34.4× is now the measured justification for the routing rubric's (c)(f) batching rows and for the write-side "one ladder per batch" rule.

Decision history

Reverse chronological. Each row is one measurement round or shipped version; full decision text lives in ADR-008.

round  date        corpus                                                  outcome                                                                                             register change
-----  ----------  ------------------------------------------------------  --------------------------------------------------------------------------------------------------  --------------------------------------------------------------------------
wave4  2026-08-03  6 arms × 2 workloads × 30 reps on RAM disk + 60 SIGKILL  All gates PASS: 34.4× p50 batched-vs-sequential; 0% corruption; SDI ≤4.27;   lazy_diff auto-hydration + files[] batch (one ladder/batch) + xxHash64
                   trials (batch-vs-sequential + corruption benchmark)      whole-file bounded 34.4× slower                                   CAS gate + atomic-rename write_mode + code-map span anchors; router 0.0.10
r5     2026-07-16  31 real production edits (6 workspace repos + CPFT-7)   Morph strict dominance (100% vs 97%; p50 677≤810; p95 1550≤1726); splice-first productionized       fast_apply_engine relace→morph; splice preflight LIVE; write-router 0.0.3→0.0.4
r4     2026-07-16  30 tasks (r3 superset, three-arm; splice added)         Splice hits 66.7% deterministic on adversarial fixture (lower bound of 70–85% pre-declared range)   splice shipped as first-class FAST_APPLY_ENGINES value + adapter; not yet router-wired
r3     2026-07-16  30 tasks (14× category-(d) large rewrites)              TIE by declared rule; new signal — Relace HTTP 429 ceiling under sustained load (6× 429 in tail)   registry enabled:false held; vendor observability headers captured on 429
r2     2026-07-15  20 tasks; rung-3 property-check verify added            Relace strictly dominates on matched set (small p50 margin, stable −15% input-token advantage)      fast_apply_engine morph→relace; write-router 0.0.1→0.0.2
W6     2026-07-13  6 tasks, gold-substring verify                          Relace 6/6 vs Morph 5/6; medians within noise floor; sample too small to flip                       @nexartis/ozzydev-native-write-mcp v0.0.1 shipped; first live Morph-vs-Relace bakeoff

Data artifacts (git-tracked, all under mcp/servers/ozzydev-native-write-mcp/experiment/): results-r2-2026-07-15.json, results-r3-2026-07-16-run{1,2}.json, results-r4-2026-07-16-run{1,2}.json, results-r5-2026-07-16-mrnv8hrn.json. Fixture-synthesis and mining pipeline: experiment/fixture-synthesis.mts, mine-real-edits.mts, fetch-commitpackft.mts, curate-r5.mts.

Serial-vs-parallel receipt POST — race candidate

The write MCP is serial on the receipt POST (write → verify → receipt POST → telemetry POST). The paired search MCP W3 optimization landed the opposite pattern — Promise.all([receiptPost, telemetryPost]) with fail-closed preserved — and measured a ~50% single-query latency drop (see moe-search-router §Latency). The two writes are independent by construction. Flagged as a race candidate for the ADR-009 R3 shared-receipt consolidation; the fix is one Promise.all per emitter, measured before adopting.

Evidence gaps — the benchmarks we still need

Both are cheap to build internally, and both are prerequisites for adopting any of the commercial engines as a default rather than an option.

  • Ambiguous-anchor coverage for the splice preflight. r5b landed the tree-sitter scope-anchor rung (ambiguous-anchor 5→4; splice 24/31 = 77.4%). The Osmosis-Apply-1.7B local arm is now BENCHED (see measured results): it resolves 5/7 of the splice residual at $0 but 27s p50 — viable as an opt-in rank-3 fallback behind the vendor arm, not the hot path. WAVE-4 (2026-08-03): the code-map span anchor rung (jit_context.anchor_spans) resolves splice ambiguous anchors deterministically at $0 before escalation; remaining residual is now the unanchored-segment class. Remaining queued in ADR-008: grammar coverage beyond TS/JS/svelte-script, higher-quant Osmosis rerun (Q6_K) on the 2 long-context failures, integration decision for the rank-3 arm.
  • Atomic-tool corruption + batch-latency benchmark — CLOSED (wave-4, 2026-08-03). BUILT and EXECUTED: 6 arms × cold/warm × workload axes on a RAM disk; 34.4× p50 batched-vs-sequential (acceptance ≥3.5× PASS); 0% corruption under SIGKILL across 60 trials (PASS); SDI ≤6 (PASS); whole-file bounded (PASS). Prereg + runner + results artifact under mcp/servers/ozzydev-native-write-mcp/experiment/. The seams this justified are landed: lazy_diff original auto-hydration, multi-file batch lazy_diff (one ladder/batch), xxHash64 CAS gate, atomic-rename write_mode, code-map span anchors.

Clarifications on tools that get miscited

  • GritQL — Moved to the Biome organisation. Still active as a structural-search language; the standalone product page redirects. Use via Biome tooling.
  • PASTE (as a product) — No credible product exists under this name. The name most likely mislabels Cursor’s speculative-edit feature (harness-integrated fast-apply on the write path). The arXiv PASTE paper is a serving-layer research idea, not a shippable tool.

How this paper stays current

Living document. r5 landed the first evidence-driven engine default (Morph) and the splice-first preflight; subsequent rounds replace the current numbers as the corpus grows and the ambiguous-anchor coverage lands. The rubric moves as the evidence moves; the version at the top bumps on every material change, and the decision-history table above is the single tracker of what shipped when.