Skip to main content
Home/Blog/Hash-Anchored Editing Explained: Why Oh My Pi Edits Files Differently
Developer Tools

Hash-Anchored Editing Explained: Why Oh My Pi Edits Files Differently

Oh My Pi replaces line-number and string-replace edits with content-hash anchors that survive line drift and reject stale patches. Here's how hashline editing works and why it cuts tokens while raising edit accuracy.

By Sean

Every AI coding agent has to solve the same unglamorous problem: how does the model tell the tool exactly which bytes to change? Get that wrong and the model writes the right code to the wrong place, or silently clobbers lines that drifted since it last looked. Most agents settle on one of two answers, and both have a known failure mode.

Oh My Pi (omp) — the MIT-licensed terminal coding agent from Can Boluk, forked from Mario Zechner's Pi — picks a third answer. Its headline feature is hash-anchored editing, or "hashline." It's a small idea with outsized results, and it's worth understanding because the benchmark numbers are hard to ignore.

The two ways edits usually break

Line-number addressing. The model says "replace line 42." Cheap to express, but line numbers are positional. The moment another hunk inserts or deletes lines above 42, every number below shifts. Multi-edit patches that reason about original line numbers and then apply sequentially are a classic source of off-by-N corruption.

String-replace. The model reproduces the exact text it wants to change so the tool can find and swap it. This is what most popular agents lean on. It's position-independent, but it's verbose — the model re-emits whole blocks of unchanged context just to anchor the match — and it's fragile when whitespace or near-duplicate lines confuse the matcher.

Hashline keeps the position-independence of string-replace without paying for it in tokens.

How hashline actually works

When omp reads a file, it doesn't just hand the model raw text. Every line is tagged with an anchor derived from that line's content:

1:a3|function hello() {
2:f1|  console.log("hi");
3:0e|}

The format is LINE:HASH (also written LINE#ID). The number is a position hint; the short hex string is a content hash of that line. The model then edits by copying those tags verbatim and naming an operation — it never retypes the line it's changing.

The mental model: line numbers are a hint, the hash is the source of truth. You're pointing at a line's identity, not its address.

The operations are explicit verbs:

OperationWhat it does
replace line 2:f1Rewrite a single anchored line
replace range 1:a3 through 3:0eRewrite an inclusive span
insert after 3:0eAppend new lines after an anchor
insert before 1:a3Prepend new lines before an anchor
delete (lines=null / [])Remove the anchored line(s)

A patch is grouped under a section header of the form [PATH#TAG], where TAG is a 4-hex-character snapshot identifier for the file version the agent last read or searched. That per-file tag complements the per-line hashes: one detects whether the file drifted, the others pin which lines you mean.

Why it survives line drift

This is the part that matters. Because an anchor is bound to a line's content rather than its index, it stays valid even when surrounding lines move. Insert ten lines at the top of the file and 3:0e still resolves to the same closing brace. Within a single patch, line numbers refer to the original file state and don't shift as hunks apply, so multi-hunk edits don't trip over themselves.

The hash is also computed on normalized, whitespace-insensitive content. Reindent the block, reformat it, change the leading tabs to spaces — the anchor still matches because the hash never saw the whitespace. That's a meaningful robustness win over string-replace matchers that choke on exactly this.

Stale-anchor rejection: failing loud, not silent

The most valuable property isn't the token savings — it's what happens when something is wrong.

If a file changed since the agent last read it (you saved over it, another tool touched it, a formatter ran), the current line hashes no longer match the anchors in the model's patch. omp detects the divergence and rejects the entire patch before applying anything. From the README:

Edit a stale file and the anchors diverge — we reject the patch before it corrupts anything.

Compare that to a line-number or fuzzy-match approach, which will happily apply edits to whatever now lives at those coordinates. Stale-anchor rejection turns "silent corruption you find three commits later" into "an error you fix in the next turn." When the patch is rejected, the error returns updated LINE:HASH references (flagged with >>> markers) so the agent re-reads and retries against the real current state. omp also ships repair heuristics — boundary echo repair to drop duplicated edge lines, delimiter balance repair to keep brackets matched, and depth-guided landing to slide an insertion past closers until indentation depth lines up.

The numbers

Two claims, both worth separating: tokens and accuracy.

Tokens. Because the model never re-emits unchanged context, output shrinks. The project reports Grok 4 Fast using 61% fewer output tokens on the same work versus traditional edit formats; Better Stack measured roughly 50% output-token savings on Claude Opus. On a long agentic session, that's real money and real latency.

Accuracy. This is the headline. Holding model weights constant and switching only the harness to hashline raised Grok Code Fast 1's edit success rate from 6.7% to 68.3% — about 10x. MiniMax improved ~2.1x, and Gemini variants reportedly gained 5–14 percentage points. The lesson is uncomfortable for the rest of us: a large fraction of "the model can't edit code" is actually "the edit format is hostile to the model."

There's also a press narrative that Google banned Boluk's Gemini account after a hashline benchmark put Gemini 3 Flash above Google's own best result. That comes from a single source and isn't backed by primary documentation — treat it as unconfirmed gossip, not fact.

The workflow rules you have to respect

Hashline's guarantees only hold if you follow its grain:

  • One edit call per file, all operations batched. Tags shift after each applied edit. If you fire sequential edits at the same file without re-reading, your later anchors are already stale.
  • Re-read between rounds. Need a second pass on a file you just edited? Read it again to get fresh tags first.
  • Don't target shared boundary lines. Anchoring an op on } else { or },{ is asking for ambiguity — those lines recur and sit on structural seams.
  • Copy tags exactly from the most recent read. Anchors are not something the model should reconstruct from memory.

Where it fits among omp's other modes

Hashline is the default, but omp offers three edit modes:

ModeMechanismBest for
hashlineContent-hash anchorsDefault; most reliable and token-efficient for LLM agents
patchStandard unified diff with context matchingInterop with diff-shaped workflows
replaceText replacement with fuzzy whitespace / similarity thresholdsSimple swaps where exact text is known

Hashline is positioned as the most reliable and token-efficient of the three. It's also one feature among many — omp is roughly 55,000 lines of Rust (in-process search, shell, AST, syntax highlighting) plus TypeScript agent logic, with LSP refactoring, real debuggers (lldb/dlv/debugpy), ast-grep, and 32 built-in tools, across macOS, Linux, and Windows.

The idea is leaking outward, too. There's an open Claude Code issue requesting hash-based line addressing for its Edit tool (anthropics/claude-code #25775), a parallel request in Hive (#4752), and independent benchmark write-ups comparing hashline against replace. That's a healthy sign: the mechanism is being scrutinized by people who don't have a stake in omp winning.

Bottom line

Hash-anchored editing is a clean fix for a problem the whole field has been papering over. By addressing lines by content hash instead of position — whitespace-insensitive, drift-resistant, and validated against a file snapshot — omp makes edits both cheaper (50–61% fewer output tokens) and dramatically more reliable (Grok Code Fast 1 from 6.7% to 68.3% on harness change alone). The stale-anchor rejection is the quiet hero: it converts the worst failure mode, silent corruption, into a loud, recoverable error.

If you're building or choosing an AI coding workflow, the practical takeaway is that the edit format is a first-class lever, not plumbing. Respect hashline's rules — batch per file, re-read between rounds, avoid boundary lines — and you get most of the benefit for free. And if you're stuck on another agent, watch those upstream feature requests: the rest of the ecosystem is clearly paying attention.

Frequently Asked Questions

Find answers to common questions

Hashline is Oh My Pi's default edit mode. Instead of pointing at line numbers (which shift) or reproducing chunks of text to match against (which is verbose and brittle), the model references content-hash anchors. Each line in the read output carries a short unique identifier derived from that line's content, and the model points at those anchors with verb-based operations rather than retyping the lines it wants to change.

Each line gets a short identifier hashed from its content, shown in read output as a LINE#ID or LINE:HASH tag (for example '2#abc123' or '1:a3'). The hash is computed on normalized, whitespace-insensitive content, so reindentation or reformatting does not change it. The model must copy these tags verbatim from the most recent read.

Because the anchor is bound to the target line's content, not its position. If lines above it are inserted or deleted and everything shifts, the anchor still resolves to the same line. That decoupling is the whole point: position-based edits break the moment anything moves, while content-based anchors do not.

If a file changed since the agent last read it, the line hashes no longer match the anchors in the patch. Oh My Pi detects the divergence and rejects the entire patch before applying anything, instead of writing edits to the wrong lines. The error returns updated LINE:HASH references (marked with >>>) so the agent can re-read and retry against the current file.

The savings come from not re-emitting unchanged context lines. The project reports Grok 4 Fast spending 61% fewer output tokens on the same work versus traditional edit formats, and Better Stack cites roughly 50% output-token savings on Claude Opus.

Switching only the harness to hashline (same model weights) raised Grok Code Fast 1 edit success from 6.7% to 68.3%, about a 10x improvement. MiniMax success rose roughly 2.1x, and Gemini variants reportedly gained 5 to 14 percentage points.

Tags shift after each applied edit, so anchors from an old read become stale. The rule is one edit call per file with all operations batched together; if you need a second round of edits on the same file, re-read first to get fresh tags. Also avoid targeting shared boundary lines like '} else {'.

Yes. Oh My Pi (omp) is MIT-licensed and open source, created by Can Boluk as a fork of Mario Zechner's Pi (badlogic/pi-mono). The repo is github.com/can1357/oh-my-pi and the site is omp.sh. It supports 40+ LLM providers, so hashline is not tied to one model.

Building Something Great?

Our development team builds secure, scalable applications. From APIs to full platforms, we turn your ideas into production-ready software.