Skip to content

feat(hashline): EPIC — Content-addressed line editing (hashline mode) #209

@randomm

Description

@randomm

Overview

Implement hashline, a new edit mode that replaces verbatim context matching with content-addressed line anchors. Instead of asking the model to reproduce surrounding code exactly (which causes hallucination and patch failures), hashline assigns a unique CJK character to every line. The model references lines by LINENUM꜀ anchor — making edits unambiguous and verifiable.

Inspired by the hashline implementation in can1357/oh-my-pi (branch exp/hashline2cjk). We are adding new tools alongside the existing read/edit tools — not replacing them.

Why

Benchmarks from the reference implementation on a React codebase (60 tasks, 180 runs each):

Model Patch SR Hashline SR Delta Output tokens
Claude Sonnet 4.5 65.6% 80.0% +14.4pp −24%
GLM-4.7 51.7% 61.7% +10.0pp −33% input
Gemini 2.5 Flash Lite 16.7% 36.7% +20.0pp

The existing edit tool uses 8 fuzzy-matching strategies to compensate for model hallucination of context lines. Hashline eliminates the need for fuzzy matching entirely — the anchor either matches or it doesn't, and errors are structured and self-correcting.

How It Works

hashline_read outputs each line with a CJK hash character anchor:

3丒export function greet(name: string) {
4专  return "hello"
5且}

The model edits by referencing those anchors — no context lines reproduced:

{ "op": "set_line", "anchor": "4专", "new_text": "  return `hello, ${name}`" }

If the file changes between read and edit, the error shows updated anchors the model can copy and retry with — no silent corruption.

Architecture

read (existing)            hashline_read (new)
    │                            │
    │  14: function hello() {    │  14丐function hello() {
    ▼                            ▼
edit (existing)            hashline_edit (new)
    │                            │
    │  oldString / newString      │  { op, anchor: "14丐", new_text: "..." }
    ▼                            ▼
    8 fuzzy replacers        hash lookup → validate → bottom-up splice

Scope

What we are building:

  • src/tool/hashline.ts — core algorithm: hash function, apply engine, error types
  • src/tool/hashline_read.ts + .txt — new read tool using CJK hash anchors
  • src/tool/hashline_edit.ts + .txt — new edit tool using hash anchors
  • src/flag/flag.tsOPENCODE_EXPERIMENTAL_HASHLINE feature flag
  • registry.ts — register new tools behind the flag, suppress edit when active

What we are NOT touching:

  • Existing read, edit, write, apply_patch tools — zero behavioural changes
  • The one exception: edit.ts gets export added to normalizeLineEndings (additive only)

Implementation Order

Phases must be implemented in order — each depends on the previous:

  1. Phase 1 — Core algorithm (feat(hashline): Phase 1 — Core algorithm module (hash function + apply engine) #210): hashline.ts — hash function, apply engine, error types
  2. Phase 2 — hashline_read (feat(hashline): Phase 2 — hashline_read tool #211): new read tool + feature flag
  3. Phase 3 — hashline_edit (feat(hashline): Phase 3 — hashline_edit tool #212): new edit tool + registry wiring

Key Files

packages/opencode/src/tool/
├── hashline.ts          ← NEW: core algorithm (Phase 1)
├── hashline_read.ts     ← NEW: read tool (Phase 2)
├── hashline_read.txt    ← NEW: model-facing description
├── hashline_edit.ts     ← NEW: edit tool (Phase 3)
├── hashline_edit.txt    ← NEW: model-facing description
├── edit.ts              ← MODIFY: export normalizeLineEndings (Phase 3, 1-line change)
├── registry.ts          ← MODIFY: register tools + suppress edit (Phase 3)

packages/opencode/src/flag/flag.ts
                         ← MODIFY: add OPENCODE_EXPERIMENTAL_HASHLINE flag (Phase 2)

packages/opencode/test/tool/
├── hashline.test.ts     ← NEW: 17 tests (Phase 1)
├── hashline_read.test.ts ← NEW: 11 tests (Phase 2)
├── hashline_edit.test.ts ← NEW: 10 tests (Phase 3)

Enabling Hashline

OPENCODE_EXPERIMENTAL_HASHLINE=1 bun dev
# or enable all experimental features:
OPENCODE_EXPERIMENTAL=1 bun dev

Acceptance Criteria

  • All three phases implemented with tests passing
  • hashline_read and hashline_edit appear in tool list when flag is active
  • When flag is active, edit is suppressed; write remains available
  • Existing read/edit tools are completely unmodified behaviourally
  • No @ts-ignore, as any, or type suppressions
  • bun run typecheck passes with 0 errors
  • bun test passes with 0 failures

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions