On this page #
When Can Bölük forked Mario Zechner's minimalist Pi to create omp, he made a radical bet: harness defects are the real ceiling for AI coding. Here is the architecture behind that bet.
In July, I wrote about Pi Agent’s minimalist philosophy and how its 4-tool harness (<1,000 token prompt) became the connective glue between heavier tools. A week later, I examined omp’s subagent coordination.
For two years, developers focused on model weights. When an agent failed to edit a file, missed an export, or hallucinated a fix, users blamed the model and waited for the next generation.
In my analysis of harness engineering, I argued the opposite: agents fail in production because the environment around the model breaks.
Oh My Pi (omp), built by Can Bölük ( can1357), proves this point.
Can forked Mario Zechner’s Pi (pi-mono) in late 2025. Pi championed minimalism: four core tools (read, write, edit, bash), zero permission prompts, and an auditable shell where you build extensions for extra capabilities.
omp took the opposite path. It turned the harness into an IDE engine built in Rust and Bun. omp embeds ripgrep, AST parsing, hash-anchored editing, Language Server Protocol (LSP), Debug Adapter Protocol (DAP), structured memory, and typed worker pools into the execution process.
On identical model weights, changing the harness lifted benchmark pass rates by 15 percentage points across 16 models. One model saw a 10x jump in task completion.
flowchart TD
Model[Model] --> Harness[Maximalist Harness]
Harness --> Core["Rust Native Core (Bun CLI)"]
subgraph IDE["In-Process IDE Engine"]
direction TB
subgraph Intel["Code Intelligence & Safety"]
direction LR
Hash["Hashline<br/>(Content-Anchored Edits)"]
LSP["LSP Client<br/>(TypeScript, Go, Python)"]
DAP["DAP Debugger<br/>(dlv, debugpy, lldb)"]
end
subgraph Exec["Execution & State"]
direction LR
Workers["Worker Pools<br/>(Isolated Worktrees)"]
Memory["Memory<br/>(Mnemopi SQLite + Snapcompact)"]
end
Core --> Intel
Core --> Exec
end
IDE --> FS[(File System & Codebase)]
The Fork: Minimalism vs Maximalism #
The architectural divergence between Pi and omp comes down to scope.
flowchart TD
Model["Model"] --> Loop["Minimal Core Loop"]
Loop --> Tools["4 Core Tools<br/>(read, write, edit, bash)"]
Tools --> Subprocesses["Shell Subprocesses<br/>(rg, find, sed, git)"]
Subprocesses --> FS[("File System")]
Pi relies on subtraction:
- Keep the core loop small.
- Delegate file and search operations to Unix tools via
bash. - Keep the base prompt under 1,000 tokens to preserve context for user code.
- Let developers write opt-in TypeScript extensions for specialized tasks.
omp rejects reliance on shell scripts and textual diffs. Shell commands introduce process startup latency, string escaping bugs on Windows, and zero semantic awareness of code syntax.
| Dimension | Pi ( pi-mono ) |
omp ( oh-my-pi ) |
|---|---|---|
| Philosophy | Minimalist harness, extension-first | Maximalist harness, baseline-first |
| Core Runtime | TypeScript / Node shell | Bun CLI + Rust native core |
| Tool Surface | 4 core tools ( read ,write ,edit ,bash ) |
31+ in-process tools |
| File Editing | Line replacement and text diffs | Hashline (content-hash anchored) |
| Language Intelligence | Optional user extensions | Built-in LSP client (TypeScript, Go, Python) |
| Debugging | Print statements via bash |
Built-in DAP client ( dlv ,debugpy ,lldb ) |
| Subagents | Spawn separate Pi instances via bash |
Worker pools, typed yields, worktree isolation |
| Memory | Ephemeral or flat text files | Mnemopi (SQLite + vector/graph) + Hindsight |
| Prompt Size | <1,000 tokens | ~4,000-8,000 tokens (rich tool schemas) |
Pi gives users full terminal auditability and low token overhead. omp gives the model execution reliability across multi-file codebases.
The Rust Core: Removing Subprocess Overhead #
Coding agents spend significant time in fork/exec loops. When an agent searches a codebase, lists files, and edits three locations, it spawns multiple separate processes: ripgrep, find, git, and patch scripts.
External process execution creates three bottlenecks:
- Latency: Starting hundreds of subprocesses across a multi-turn task adds seconds of wall-clock delay.
- Platform drift: Shell syntax that works in POSIX environments breaks on Windows or inside locked containers.
- Missing state checks: Shell commands return unformatted text, preventing the harness from verifying file state between search and edit without extra disk reads.
omp pulls file system operations, text search, and syntax analysis into a compiled Rust native core linked to the Bun runtime.
flowchart TD
Agent["Agent Loop"] -->|Direct FFI / In-Process| Rust["Rust Native Core"]
subgraph Operations["In-Memory Rust Engines"]
direction LR
Search["In-Process<br/>Ripgrep Engine"]
Walk["Tree-Walking<br/>& Worktrees"]
Syntax["AST Parsing<br/>& Highlighting"]
Hash["Hashline Signature<br/>Verification"]
end
Rust --> Operations
When omp searches a repository, it calls its internal Rust search engine. When it inspects symbols or tracks file changes across git worktrees, the Rust core resolves queries in memory.
In-process execution removes process startup delay, enforces memory safety, and runs on macOS, Linux, and Windows without platform shims.
Hashline: Content-Anchored Editing #
File editing causes more agent failures than any other tool call.
Standard agents use search-and-replace blocks or unified diffs:
<<<< SEARCH
const count = 2;
>>>>
A single whitespace mismatch, a duplicate line matching earlier in the file, or a drifted line number breaks the patch. The model enters a retry loop, burns tokens, and corrupts surrounding code.
omp replaces string-matching diffs with Hashline, a content-anchored editing protocol.
Hashline Mechanics
When omp reads a file, its Rust engine annotates each line with a short 2-3 character content hash and tags the file with a unique snapshot hash:
[src/server/auth.ts#A1B2]
1:f1 import { verifyToken } from "./jwt";
2:8c
3:3d export function authenticate(req: Request) {
4:e9 const token = req.headers.get("authorization");
5:0a if (!token) return null;
6:d4 return verifyToken(token);
7:7b }
To edit the file, the model sends line-anchored patch commands:
[src/server/auth.ts#A1B2]
PUT 5.=5:
+ if (!token) throw new UnauthorizedError();
The harness enforces three checks before writing to disk:
- File tag check: The file must match snapshot
[auth.ts#A1B2]. If a user or background process edited the file, the tag changes and omp rejects the edit. - Line hash check: Line 5 must have hash
0a. If lines shifted, the harness catches the mismatch. - Targeted write: The harness replaces the target range without modifying surrounding lines.
flowchart TD
Read["Agent reads file<br/>with line hashes"] --> Plan["Model plans change<br/>using hash anchors"]
Plan --> Patch["Model sends Hashline<br/>PUT/CUT command"]
Patch --> Verify{"File tag & line<br/>hashes valid?"}
Verify -->|Yes| Apply["Apply patch<br/>to disk"]
Verify -->|"No (File Drifted)"| Reject["Reject patch<br/>before disk write"]
Reject --> Fresh["Harness returns<br/>fresh snapshot"]
Fresh --> Plan
Benchmark Results
Can Bölük published a 540-task benchmark across 16 models (3 runs per task, fresh sessions) comparing standard string-replace edit formats against Hashline:
| Model | Baseline Pass Rate | Hashline Pass Rate | Net Gain | Token Reduction |
|---|---|---|---|---|
| Grok Code Fast 1 | 6.7% | 68.3% | +61.6% (10x) | - |
| Grok 4 Fast | - | - | - | -61% tokens |
| Gemini 3 Flash | Baseline | +5.0% | +5.0% | - |
| MiniMax M2.1 | Baseline | >2x Baseline | >2x | - |
| 16-Model Average | Baseline | Baseline + 15% | +15.0% | Substantial |
Grok Code Fast 1 improved from 6.7% to 68.3% with identical model weights, prompts, and instructions. The only change was replacing text diffs with Hashline.
On Grok 4 Fast, total token consumption dropped by 61% because the model avoided multi-turn edit retry loops.
A failed edit usually indicates that the model generated valid code but tripped on a brittle string-matching parser. Hashline removes that failure mode.
Semantic Intelligence: Built-In LSP and DAP #
Most coding agents treat code as raw text. To rename a function in a TypeScript monorepo, an agent runs grep, finds 20 occurrences, and issues 20 text edits. It misses barrel file re-exports or alters identical property names on unrelated types.
omp integrates Language Server Protocol (LSP) and Debug Adapter Protocol (DAP) into the tool surface.
LSP for Symbol-Aware Refactors
omp connects to language servers for TypeScript (vtsls/ tsserver), Go ( gopls), Python ( pyright/ ruff), and Rust ( rust-analyzer).
The model uses semantic tools instead of regular expressions:
lsp.rename: Renames a symbol across the workspace, updating imports, definition sites, and re-exports in one step.lsp.references: Finds all true call sites and usages of a function or type.lsp.diagnostics: Reads compiler errors and type warnings from the language server after an edit.lsp.codeActions: Applies compiler quick-fixes and organizes imports.
sequenceDiagram
participant Model as Agent Model
participant Harness as omp Harness
participant LSP as Language Server (vtsls)
participant FS as Codebase Files
Model->>Harness: lsp.rename(file, line, symbol, "newAuthHandler")
Harness->>LSP: textDocument/rename
LSP-->>Harness: WorkspaceEdit (14 files affected)
Harness->>FS: Apply edits via Hashline engine
Harness-->>Model: Success: 14 files updated
DAP for Runtime Debugging
When a standard agent debugs a runtime error, it adds console.log or print() statements, runs the test suite, parses stdout, and repeats. This process wastes tokens and pollutes the context window.
omp includes a DAP client that connects to debuggers like dlv (Go), debugpy (Python), or lldb-dap (C/C++/Rust).
The model can:
- Set conditional breakpoints at target lines.
- Step over and step into execution frames.
- Inspect variable values and call stacks in memory.
- Evaluate expressions in the running process.
Agent Action:
dap.set_breakpoint(file="src/billing/calculator.go", line=84, condition="amount < 0")
dap.continue()
-> Breakpoint hit at line 84
-> Variables in scope: { amount: -50.00, currency: "USD", userTier: "ENTERPRISE" }
The agent inspects memory state directly, identifies the root cause in one turn, and applies the fix.
Orchestration: Worker Pools and Typed Contracts #
In tools like Claude Code, starting a subagent runs a second chat loop that returns a natural language summary to the parent. The parent must read and parse multiple paragraphs of prose.
omp models multi-agent orchestration like an operating system process table:
flowchart TD
Parent["Parent Agent"] -->|"task batch tool"| Pool["Worker Pool"]
subgraph Workers["Isolated Worktree Execution"]
Pool --> W1["Worker 1<br/>(Worktree A)"]
Pool --> W2["Worker 2<br/>(Worktree B)"]
Pool --> W3["Worker 3<br/>(Worktree C)"]
end
W1 <-->|"hub peer bus"| W2
W2 <-->|"hub peer bus"| W3
W1 --> R1["agent://Worker1/files<br/>(Typed JSON)"]
W2 --> R2["agent://Worker2/schema<br/>(Typed JSON)"]
W3 --> R3["agent://Worker3/status<br/>(Typed JSON)"]
R1 --> Parent
R2 --> Parent
R3 --> Parent
Orchestration Components
- The
taskBatch Tool: The parent launches up to 32 parallel workers in one tool call with a shared context header and distinct task assignments. - Worktree Isolation: Every worker executes in an isolated git worktree. Sibling agents cannot overwrite shared files during execution.
- Typed Yield Contracts: Each subagent defines a JSON Schema for its return payload. When the worker completes, it yields a structured JSON object, accessible by URL paths like
agent://<worker_id>/output. - The
hubCommunication Bus: Subagents exchange point-to-point messages across the in-processhubbus without routing through the parent. - Persistent Execution Kernels: omp provides persistent Python and Bun VM kernels (
eval). The kernel retains state across turns, and scripts inside the kernel call agent tools over a loopback bridge via@tooldecorators.
omp also provides intent triggers in natural language:
ultrathink: Allocates maximum reasoning budget and enforces multi-turn plan verification.orchestrate: Spawns parallel subagent teams to implement independent components concurrently.workflowz: Transforms a user prompt into a formal multi-stage contract managed by thetaskengine.
Memory: Mnemopi, Snapcompact, and Local Workers #
Long-running agent sessions face context window exhaustion and rising token costs. omp manages memory and context through three subsystems:
Mnemopi Structured Memory
omp stores cross-session knowledge in Mnemopi, a local SQLite backend with vector embeddings and graph relations:
retain: Stores architectural facts, project conventions, and user preferences.recall: Queries memory by semantic similarity or project tags.reflect: Compresses completed sessions into distilled models for future sessions.
Subagents inherit the parent session memory state, ensuring shared context across parallel runs.
Snapcompact Visual Compression
Standard agents summarize old conversation turns into text when hitting context limits. Text summaries often drop variable names, line numbers, and edge cases.
omp uses snapcompact. The engine renders session snippets into pixel-font PNG images on device:
flowchart TD
History["Old Session Turns<br/>(Approaching Context Limit)"]
Render["Pixel-Font PNG<br/>(Rendered on Device)"]
Model["Main LLM Vision Input<br/>(~1/3 Token Cost)"]
History -->|snapcompact engine| Render
Render -->|Vision API Input| Model
The model reads the rendered history through vision input at roughly one-third the token cost of raw input text.
Local Model Workers
To avoid spending cloud tokens on bookkeeping, omp runs local models on-device using transformers.js (such as Qwen 1.7B, Gemma 1B, or LFM2 1.2B).
These local workers run in background threads to handle:
- Session titling
- Entity and keyword extraction for memory storage
- Formatting diff markers
This offloads routine tasks from the primary frontier model, reducing cost and latency.
Trade-Offs: When Pi Wins vs When omp Wins #
A 30-task evaluation by StandardCompute and Composio tested Pi against omp on identical real-world coding benchmarks:
| Metric | Pi ( pi-mono ) |
omp ( oh-my-pi ) |
|---|---|---|
| Task Success Rate (30 tasks) | 20 / 30 (66.7%) | 17 / 30 (56.7%) |
| Cost Per Success | $0.028 | $0.103 (3.7x higher) |
| Median Duration | 132.2 seconds | 272.4 seconds (2x slower) |
| Average Tokens Per Task | 558,885 | 742,283 (33% more) |
| Harness Architecture | Minimalist 4-tool shell | Maximalist 31-tool IDE engine |
Pi won on benchmark speed and cost for three reasons:
- Tool Overload on Simple Tasks: On single-file edits or simple scripts, omp’s extensive tool schemas (LSP, DAP, task schemas, memory) consume tokens and add decision overhead for the model.
- Setup Latency: Provisioning worktrees, querying language servers, and indexing memory takes time. For a 10-line fix, Pi’s
read+editloop completes in seconds. - Token Usage: Rich schemas and structured yields use more tokens per turn than Pi’s minimal prompt.
omp still ranked second out of eight tested coding harnesses, outperforming Claude Code, Codex, and OpenCode on the same model weights.
Choosing Between Pi and omp #
flowchart TD
Task[Coding Task] --> Scope{"Task Scope &<br/>Complexity"}
Scope -->|"Single file, scripts,<br/>low-context glue"| PiChoice["Choose Pi Agent"]
Scope -->|"Multi-file refactor, monorepo,<br/>deep debugging"| OMPChoice["Choose omp"]
subgraph PiBox["Pi Strengths"]
direction TB
Pi1["• Sub-1,000 token prompt"]
Pi2["• Low latency & cost"]
Pi3["• Terminal auditability"]
end
subgraph OmpBox["omp Strengths"]
direction TB
Omp1["• Hashline safe editing"]
Omp2["• LSP symbol awareness"]
Omp3["• DAP runtime breakpoints"]
Omp4["• Parallel worktree subagents"]
end
PiChoice --> PiBox
OMPChoice --> OmpBox
Choose Pi when you:
- Want a minimal, transparent harness with zero background overhead.
- Work on single-file fixes, scripts, and glue tasks between other tools.
- Prefer writing custom extensions in TypeScript.
- Need the lowest token cost and fastest time-to-first-token.
Choose omp when you:
- Maintain multi-package TypeScript, Go, or Python monorepos where text replacement breaks imports.
- Need parallel subagents working in isolated git worktrees without merge collisions.
- Need debugger integration (breakpoints, stepping, stack inspection) rather than print statements.
- Require cross-session memory and structured project knowledge tracking.
- Build automated agent systems that require typed JSON contracts instead of chat prose.
The Bottom Line #
The harness shapes agent capability as much as the model weights.
Giving a model read, write, and bash works for simple tasks, but fails when edits drift or imports break across packages.
Can Bölük’s work on omp demonstrates that engineering a hardened Rust core, content-anchored hashline editing, native LSP/DAP protocols, and typed worker pools can turn a struggling model into a reliable software engineer.
Minimalist tools like Pi remain ideal for fast, low-overhead tasks. For large codebases and autonomous multi-file workflows, the harness must function as an IDE wired into the execution loop.
Experimenting with coding agent harnesses or building multi-agent workflows? I’d love to hear what architecture patterns are working in your stack. Reach out on LinkedIn.