How the Hashline Edit Format Uses Content-Hash Anchors in Oh-My-Pi

The hashline edit format uses content-hash anchors—pairs of line numbers and two-character hashes derived solely from line content—to ensure edits target exact lines even when surrounding content changes, enabling reliable, atomic patches in the oh-my-pi coding agent.

The hashline edit language is a compact, line-anchored patch format implemented in the can1357/oh-my-pi repository. Unlike traditional line-number-based diffs that break when preceding lines are inserted or deleted, hashline anchors remain stable because they depend only on the target line's text, not its position.

What Are Content-Hash Anchors?

A content-hash anchor (also called a LID anchor) combines a line number with a content-derived hash. This pairing allows the system to verify that the line being edited matches the line the model originally saw, preventing accidental modifications to the wrong text.

Anchor Structure and Format

Each anchor follows the pattern LINE+HASH, producing strings like 42sr:


42sr   → line 42, hash "sr"

The two-character hash is generated by computeLineHash() in [hash.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/hash.ts#L48-L57). Because the hash derives exclusively from the line's cleaned text, the anchor stays valid even if surrounding lines shift, provided the line's own content remains unchanged.

The Hash Computation Algorithm

The computeLineHash() function implements a deterministic hashing pipeline designed for stability:

// hash.ts – core of the anchor generation
export function computeLineHash(idx: number, line: string): string {
    void idx;                              // line number is ignored for stability
    line = line.replace(/\r/g, "").trimEnd();
    return HL_BIGRAMS[Bun.hash.xxHash32(line, 0) % HL_BIGRAMS_COUNT];
}

The algorithm executes four steps:

  1. Normalize the line by stripping \r and trailing whitespace
  2. Hash the cleaned string using xxHash32 with a fixed seed of 0
  3. Select a bigram by taking the modulo of HL_BIGRAMS_COUNT (647 pre-generated English letter pairs)
  4. Return the two-character bigram as the hash suffix

Where Anchors Appear in the Hashline Format

Anchors serve dual roles: they provide human-readable file displays and act as machine-verifiable edit targets.

File Headers and Display Format

When displaying a file to the model, the system prefixes each line with its anchor using formatHashLine() in [hash.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/hash.ts#L70-L94):

export function formatHashLine(lineNumber: number, line: string): string {
    return `${lineNumber}${computeLineHash(lineNumber, line)}${HL_BODY_SEP}${line}`;
}

This produces output where HL_BODY_SEP (the pipe character |) separates the anchor from the content:


1bm|function hi() {
2er|    return;
3ab|}

Diff Operations and Validation

Every edit operation (+, <, -, =) in a hashline diff must reference a full anchor (LINE+HASH). The parser validates these anchors using the LID_CAPTURE_RE regular expression defined in [parser.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/parser.ts#L5-L16):

const LID_CAPTURE_RE = new RegExp(`^${HL_HASH_CAPTURE_RE_RAW}$`);
function parseLid(raw: string, lineNum: number): Anchor {
    const match = LID_CAPTURE_RE.exec(raw);
    if (!match) throw new Error(`line ${lineNum}: expected a full anchor …`);
    return { line: Number.parseInt(match[1], 10), hash: match[2] };
}

How the Engine Validates Anchors

Before applying any mutations, the system verifies that all anchors in the patch still match the current file content.

Re-hashing and Mismatch Detection

The applyHashlineEdits function in [apply.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/apply.ts) re-hashes the target file line-by-line during application. It compares the freshly computed hash against the anchor in the patch:

  • Match: The line is confirmed as the correct target
  • Mismatch: A HashlineMismatchError is thrown, aborting the operation

This verification happens before any write operations, ensuring that partial edits cannot corrupt the file.

Atomic Application Guarantees

Because all anchors are validated prior to mutation, the hashline format provides atomicity: either every anchor matches and the entire edit succeeds, or the operation fails without modifying the file. This prevents scenarios where a line number shift causes an edit to land on the wrong line mid-patch.

Recovering from Stale Anchors

When external tools modify a file after the model has read it, anchors may become stale (the line numbers shift while content remains). The system implements automatic recovery via caching.

The Read-Snapshot Cache Mechanism

The coding-agent maintains a read-snapshot cache (FileReadCache) that stores previously seen file versions. When executeHashlineSingle in [execute.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/execute.ts#L73-L118) detects a hash mismatch, it invokes tryRecoverHashlineWithCache() from [recovery.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/recovery.ts#L89-L107):

  1. The system attempts to apply the patch against the cached snapshot where the anchor was valid
  2. If successful, the edit is committed and a warning is emitted
  3. If recovery fails, the original HashlineMismatchError bubbles up to the caller

This recovery path allows the model to succeed even when the filesystem has drifted from the expected state.

Why Two-Character Hashes?

The hashline format specifically uses two-character hashes from a set of 647 bigrams (HL_BIGRAMS) for three reasons:

  • Stability: The bigram set is frozen forever; changing it would invalidate all stored anchors
  • Token efficiency: Each bigram tokenizes as a single token in modern BPE vocabularies (Claude, GPT-4o), keeping the anchor as a one-token feature in LLM prompts
  • Collision resistance: The ~10 bits of entropy provided by 647 possibilities makes accidental collisions extremely unlikely for normal source files while keeping the format compact

Practical Code Examples

Generating a Hashline View

import { formatHashLines } from "@oh-my-pi/pi-coding-agent/hashline";

const source = `function greet(name) {\n    return "hi " + name;\n}\n`;
console.log(formatHashLines(source));
/* Output:
1bm|function greet(name) {
2er|    return "hi " + name;
3ab|}
*/

Source: [hash.tsformatHashLines](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/hash.ts#L85-L94)

Applying an Edit with Anchor Verification

const diff = `
= 2er..2er
~return "hello " + name;
`.trim();

const result = applyHashlineEdits(source, parseHashline(diff));
console.log(result.lines);
/* → 
1bm|function greet(name) {
2er|return "hello " + name;
3ab|}
*/

This example uses parseHashline ([parser.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/parser.ts#L47-L55)) to validate the 2er anchor, then applyHashlineEdits ([apply.ts](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/hashline/apply.ts#L640)) recomputes the hash for line 2 to confirm the match before replacing the payload.

Summary

  • Content-hash anchors combine line numbers with two-character hashes derived from xxHash32 output, creating stable references that survive line insertions and deletions elsewhere in the file
  • computeLineHash() in hash.ts generates these anchors by normalizing line text and selecting from 647 stable bigrams
  • Validation occurs in applyHashlineEdits before any mutations, ensuring atomic operations that either fully succeed or fail without side effects
  • Stale-anchor recovery uses the FileReadCache to replay edits against cached snapshots when external modifications cause hash mismatches
  • The two-character format optimizes for LLM tokenization while providing sufficient entropy to prevent collisions in typical codebases

Frequently Asked Questions

What happens if a line's content changes after the model sees it?

If the line content changes, the computed hash will not match the anchor in the patch, causing applyHashlineEdits to throw a HashlineMismatchError. The system will then attempt to recover using the read-snapshot cache if the original version was cached; otherwise, the edit fails and must be regenerated by the model.

Why does hashline use bigrams instead of hex or base64 encoding?

The 647 English-letter bigrams are specifically chosen because they tokenize as single tokens in modern BPE models like Claude and GPT-4o, reducing prompt size, while shorter encodings like hex would require two tokens per hash. This design keeps the anchor as a compact, model-efficient feature.

How does the system handle Windows-style line endings (\r\n)?

The computeLineHash() function explicitly strips \r characters and trailing spaces before hashing, ensuring that line endings do not affect the hash. This normalization guarantees that anchors remain consistent across different operating systems and editor configurations.

Can I use hashline edits without the oh-my-pi coding agent?

While the hashline format is designed for the oh-my-pi coding agent, the standalone functions in the hashline package (parseHashline, applyHashlineEdits, formatHashLines) can be imported and used independently. However, the stale-anchor recovery mechanism requires the FileReadCache infrastructure provided by the agent's execution context.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →