jig
Health Warn
- No license — Repository has no license file
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Pass
- Code scan — Scanned 11 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Agent-based coding harness built on Rust with a bunch of experimental features for better quality, speed, and savings.
⚡ jig
An ideal coding harness, built from scratch in Rust.
One binary, five crates. No agent frameworks, no SDKs.
Designed from a survey of 281 coding harnesses and ~360 addons, plus deep-dives
into Cursor, Claude Code, Codex CLI, Manus, SWE-agent, Agentless, CodeAct, and
Aider. Every decision is measured, every mechanism justified.
🔭 0. Philosophy
The three axes
Every decision in JIG is subordinate to three targets:
- Flexibility — one loop for any task type (quick-fix to multi-day project)
through profiles, hooks, and MCP, without complicating the core. - Quality above any other harness — squeeze more out of the model than any
existing harness can, using the same weights. - Interactive speed above Cursor — measured, not declared.
"The harness is part of the model's effective parameters."
The same weights in a good vs. bad harness are two different models — measured,
not asserted. On Terminal Bench, Opus scores 77% in Claude Code → 93% in
Cursor's harness — 16pp difference, same weights. 39 harness×model pairs beat
Claude Code. Theo reproduced this manually: GPT-5.6 Sol in someone else's Claude
Code produces better code and UI than in its native Codex, using 4× fewer tokens.
The SWE-agent paper (ACI, arXiv:2405.15793) confirmed it: the "agent–computer"
interface triples results without changing the model.
The system prompt is infrastructure
The main lesson from Theo's teardown of Codex CLI's system prompt: global
prescriptions ("cards with border-radius ≤8px", "use Lucide icons", "update every
30 seconds", "don't stop at suggesting — just do it") silently corrupted outputs
for all users for months and burned millions of tokens. The prompt looked
AI-generated for a weak GPT-4.1-era model and was never revisited.
JIG's rules:
- The prompt is handwritten, reviewed like code, linted in CI.
- Every change is measured by a benchmark.
- With every model upgrade, stale content is pruned — what the model no longer
needs is thrown out. - No dates, no timestamps, no random ids in the system prompt — those live in
the env manifest where they belong (§3). - Scope guides (frontend conventions, stack, style) don't live in the system
prompt — that's what skills are for (§3): otherwise a quarter of the prompt
rides along with backend tasks where it's harmful.
Speed is metric #1, and it barely depends on the model
Agent traffic is ~100:1 input:output (Manus data), meaning latency is dominated
by prefill, not generation. The #1 speed metric for a harness is therefore
KV-cache hit rate, not tok/s of the model.
Minimalism wins
Agentless (arXiv:2407.01489) proved a simple 3-phase pipeline beats complex
multi-agent systems. mini-swe-agent solves SWE-bench with a single bash tool.
A small set of ideal tools beats a zoo of modules.
Every agent error is a harness bug
When the agent slips, we engineer the slip out of existence — guardrails, gates,
output formats. Never a prompt patch. Always a mechanism. The mode of operation:
agent makes a mistake → engineer so that mistake becomes impossible.
📚 1. What We Took From Whom
JIG is built from scratch, but not in a vacuum. Here is the research basis —
281 harnesses studied, specific features borrowed, and the reasoning behind them.
| Source | What we take |
|---|---|
| Cursor | Speculative edits (file as draft for speculative decoding, ~1000 tok/s apply); shadow workspace (hidden copy of the environment for linting before showing the user); dynamic context discovery (chat history as a file after compaction); Fusion/next-action prediction as UX inspiration |
| Claude Code | Reference tool set (Read/Edit/Write/Grep/Glob/Bash/Task/Todo); hooks; "reversibility & blast radius" in the system prompt; 4 types of memory; parallel speculative reads |
| Codex CLI (Rust) | The only "big" Rust precedent: tokio architecture, apply_patch, OS sandbox (Seatbelt on macOS / Landlock on Linux), read-only / workspace-write / full-access modes |
| Aider | Repo map: tree-sitter → symbol graph → personalized PageRank → repo slice under token budget; git-native workflow (every edit = commit) |
| Manus | The KV-cache bible: stable prefix, append-only context, masking (not deleting) tools, recitation via todo, leave errors in context |
| SWE-agent | ACI: concise tool output, lint-rejection on broken edits, feedback formats, "empty output is also a signal — say so explicitly" |
| Agentless | Phases: localization → repair → validation; multiple patch candidates + reranking by tests (for Quality mode) |
| CodeAct | Code as action: composing operations in one script instead of a chain of JSON calls (in JIG: bash + mini-scripts) |
| Anthropic engineering | Writing effective tools (fewer tools, search-oriented instead of "read everything", namespacing, answers "teach" the model); context engineering; long-running agents: compaction is insufficient — structural artifacts are needed |
| DeepSeek Reasonix | Prefix-cache-stable sessions as an architectural principle |
| Plandex / Archon / Flow-next | Plan-first: durable specs, validation gates, fresh-context workers |
| Ante / Continuum (Rust) | Daemon architecture: long-lived process on workspace holds a warm code index |
| Cordy (Rust) | Hot-swap model/provider mid-dialogue without losing context |
| zot / Smelt / QQCode (Rust) | Structured tools + guardrails; granular permissions; parallel subagents; speed as a feature |
| Theo (t3.gg) / T3 Code | Prompt = infrastructure: handwritten, no global prescriptions, constant pruning; workflows-as-code instead of recursive subagent spawn (~1/4 tokens at same quality); Terminal Bench as harness arbiter |
| Addons: Headroom, Serena, agent-lsp, Wit, Greywall | Tool output compression; semantic code search; speculative edits via LSP; symbolic locks for parallel agents; deny-by-default sandbox |
Database stats: of 281 harnesses — TypeScript ~35%, Python ~30%, Rust ~15% and growing
(Codex CLI 99.9k★, Warp 63.4k★, Goose 51.3k★, ZeroClaw 32k★). Almost all "fast"
and single-binary harnesses are Rust. The language choice is market-validated.
🧱 2. Architecture
jig/
├── jig-core ← agent loop, providers, context, tools (~everything is here)
├── jig-index ← tree-sitter symbol graph → personalized PageRank repo map
├── jig-sandbox ← permission engine + Landlock / seccomp / Seatbelt bindings
├── jig-tui ← ratatui interface
└── jig ← CLI binary, provider registry, meta commands
New crates appear only when the harness is impossible without them — never "for cleanliness."
Process model (experimental, from Ante/Continuum): lightweight CLI + optional
warm daemon per workspace. The daemon holds: tree-sitter index, file watcher
(notify), warm bash session, pre-heated HTTP/2 connection to the provider. Cold
start of the agent = 0ms instead of seconds. The CLI works without the daemon
(degradation, not dependency).
2.1 Everything from scratch — what that means
- Own HTTP layer:
reqwest(rustls) + hand-written SSE parser (~100 lines:
parsingdata:lines per spec, buffering incomplete events). No openai-sdk /
anthropic-sdk / rig / langchain. - Own tool-calling: We describe JSON Schema for tools ourselves, parse
streamingtool_calldeltas from SSE ourselves, assemble arguments
incrementally. This gives a superpower SDKs can't provide: starting tool
execution before the stream ends (see §6.2). - Own provider layer:
Providertrait with two format implementations
(OpenAI-compatible + Anthropic Messages). Covers ~95% of models. Cordy-style
feature: model swap mid-session without context loss (transcript stored in
neutral internal format, provider serialization on-the-fly, deterministic).
trait Provider {
fn stream(&self, req: ChatRequest) -> impl Stream<Item = Event>;
// Event: TextDelta | ThinkingDelta | ToolCallStart | ToolCallArgsDelta
// | ToolCallEnd | Usage | CacheReport | Done
}
Transcript — append-only log (internal format, sqlite + jsonl). From it, we
deterministically render context for any provider. This is the foundation for
KV-cache (§6), resume/branch sessions, and hot-swap models.
🚀 Quickstart
📥 Download binary (Windows)
- Grab
jig.exefrom the latest release - Put it anywhere — it's a single self-contained binary (~17 MB), no install, no runtime
- Open PowerShell (or Windows Terminal) in your project folder:
# Set your API key (one-time, or add to your PowerShell profile)
$env:ANTHROPIC_API_KEY = "sk-ant-..."
# Run it — headless mode
.\jig.exe -p "add a health-check endpoint"
# Or interactive TUI
.\jig.exe
# First time in a project — let jig learn your codebase
.\jig.exe init
.\jig.exe onboard --apply
💡 Tip: add the folder with
jig.exeto yourPATHso you can just typejigfrom anywhere.
In PowerShell:$env:Path += ";C:\tools\jig"(permanently via System Properties → Environment Variables).
🦀 Build from source (all platforms)
cargo build --release # single binary → target/release/jig
export ANTHROPIC_API_KEY=… # or OPENAI_API_KEY (or $env:OPENAI_API_KEY on Windows)
jig -p "add a health-check endpoint" # headless, one prompt
jig # interactive TUI
jig init # write jig.toml + AGENTS.md
jig onboard --apply # analyze repo, propose guides + gates
🔄 3. The Loop
One loop, two policies — ⚡ Fast and 🎯 Quality — sharing one code path.
No two code paths. Just different parameterizations.
| ⚡ Fast | 🎯 Quality | |
|---|---|---|
| Goal | Answer / edit in seconds | Maximum code quality |
| Planning | Implicit (todo at model's discretion) | Mandatory Plan phase: localization (Agentless-style) → PLAN.md → gate (user confirmation or self-check) |
| Subagents | Disabled | Model-delegated tasks with explicit read-only or workspace access |
| Repo map | 2k tokens | 8k tokens + explore results |
| Verification | Syntax-check on edit + async cargo check/tsc (doesn't block the answer, result appended) |
Full gates: fmt → lint → build → tests; auto-repair cycle (up to 3 iterations), then blind review |
| Candidates | 1 patch | N parallel candidates on test failure + reranking by tests (Agentless) |
| Thinking | Minimal / off | Extended on Plan and Review phases |
| Model | Routing: utility tasks (compaction, summary, headers) → small model | Main model everywhere, utility → small model |
Gates are project config (jig.toml), not hardcode:
[gates]
check = "cargo check --message-format=json"
test = "cargo test -q"
lint = "cargo clippy -q -- -D warnings"
🧠 Streaming executor
The model streams — we pipeline: read-only tools launch concurrently the moment
their arguments close, while the model is still decoding. A supervisor aborts
doomed calls mid-stream:
- ✋
editwithout a priorread→ killed before touching the file - 🔁 Exact repeat of a command that just failed → killed, strategy change injected
- 💀 Two identical failures → doom-loop breaker fires, forced plan update
🧩 Subagents
In Quality mode, agent delegates scoped tasks to depth-1 subagents
(no recursive spawn). The delegated task describes the job in ordinary
language; the harness does not classify it into a fixed persona. It only
enforces an explicit capability boundary: read-only or workspace access.
Context of a subagent = fork of parent's prefix at the branch point: prompt
cache is shared up to the fork, you only pay for diverging tokens (trick from
the leaked Claude Code source).
| Access | Tools |
|---|---|
read_only |
Read, grep, and glob |
workspace |
Read, edit, write, grep, glob, shell, and todo |
Returns a compact 1–2k tok structured report; full logs go to file — results are inspectable and survive interruptions (Lilian Weng). Main context is not polluted with navigation junk.
🎭 Shadow-check & repair context
Shadow-check (simplified Cursor shadow workspace): in Quality, edits are
first applied in a shadow copy (overlay via copy-on-write / git worktree), gates
are run there; the user sees an already-verified diff. In Fast — we edit the
working copy immediately, verify asynchronously (speed > safety).
Repair-context — a gate failure costs one turn, not three: the harness
doesn't dump a raw compiler log. It parses errors (--message-format=json),
extracts code around each error with hashline anchors, signatures of involved
types, and nearest call-sites — and delivers it in one package. The model
doesn't need 2–3 turns to navigate to the error: everything needed for the fix
is already in front of it, and the repair cycle converges on the first iteration.
Same principle for failing tests: test body + code under test + assertion diff
in one package.
📋 Long session protocol (Quality)
orient (PROGRESS.md, todo, recent commits) → verify baseline (the previous
session might have broken something — first make sure it's alive) → one task per
session → check with real execution (browser/CLI run, not just unit tests: agents
close features without verifying them) → commit + write to PROGRESS.md → clean
exit. Compounding cross-session bugs are the most frequent failure mode of long
agents; baseline verification eliminates this class of error.
🧠 3.5 Physics of Models → What the Harness Fixes
The harness can't change weights. But almost every systemic transformer defect
is fixable by how the harness lays out input and runs the loop. Rule: for
every measured model bug — a specific mechanism, not advice to "write a better
prompt."
| 🐞 Model Bug (source) | 🔧 Harness Fix in JIG |
|---|---|
| Lost in the Middle — U-shaped attention: tokens at the beginning and end get more attention regardless of relevance; the middle of a long context can perform worse than no context at all (Liu et al.; Found-in-the-middle) | Layout under the attention curve: critical at edges, reference in files. Rules and repo map at the start; task, todo, and fresh tool results at the end. Recitation (§4.7) isn't decoration: every turn rewrites the plan into the hot recency zone. Big blocks (history, full logs) never sit in the dead middle — they're in files, accessed via grep (retrieved content lands in the fresh end of context) |
| Attention sink — disproportionately high attention to the first tokens of a sequence (StreamingLLM; "Why do LLMs attend to the first token?") | The first lines of the system prompt are the most expensive attention slots in the model: that's where the main behavioral rules go, not "You are a helpful assistant." The header is byte-unchanged — it matches KV-cache discipline (§6.1): attention physics and cache economics demand the same thing |
| Context rot — degradation with length even on trivial tasks, across all 18 tested models; million-token windows rot by 50k (Chroma) | JIG default: minimal-sufficient context, not maximal-available. Hard budgets per section (repo map 2k/8k, subagent reports 1–2k, truncation invariants §9.5), pre-emptive compaction, fresh restart with handoff (§3). jig calibrate (§5.6) measures the largest tested context length that remains reliable; it does not pretend to distinguish endpoint rejection from attention degradation. |
| Distractors > emptiness — similar but irrelevant information drops accuracy more than its absence (Chroma) | Nothing "just in case." Auto-context carries only PageRank-top slices. Context P&L (§6.5) reports exact downstream attribution and labels missing evidence as unknown, never automatically as waste. Subagents (§4.8) keep navigation junk out of main context |
| Instruction-density ceiling — even frontier models hold 68% at 500 instructions, with bias toward early ones (IFScale) | The main trick: an instruction turned into a mechanical guardrail costs zero attention. "Edit requires read", sandbox, masked tools are enforced by code, not prompt. For the rest — token budgets via prompt-lint (§9.5) and scoped skills instead of global rules (§3) |
| Order bias in choice — models systematically prefer an option by position, not quality (Fragile Preferences) | Reranking of patch candidates is done by tests, not the model (§5). Where the model judges (review), candidates are presented in shuffled order and compared pairwise in both directions |
| Doom loop — autoregression: the model, seeing its repeating pattern in the transcript, continues it — a failed command is repeated byte-for-byte over and over | The streaming supervisor (§6.2) kills exact repeats before execution. Two identical failures in a row → harness injects escalation: "this approach isn't working — here's what was tried, change strategy" + forced plan update. Errors stay in context (Manus) — don't erase them, push off them |
| Blindness to exact reproduction — tokenization makes exact line numbers, whitespace, and long verbatim quotes unreliable → the eternal "String to replace not found" across all harnesses | Hashline (§4.2): anchor = short content-hash the model must recall, not reproduce. Blast radius and repair-context deliver exact code slices mechanically |
Half of JIG's mechanisms were in this table before it was written — now it's clear
why they work: they were designed not by taste, but under the measured attention
physics of the transformer.
🛡️ 3.7 Active Rules — Instructions That Defend Themselves
A classic .md instruction is passive: the model must remember it, comply, and
violations are noticed at best by the user on review. JIG makes rules active:
rule = statement + machine detector + reaction. While the rule isn't violated,
it costs 0 attention tokens (the answer to the instruction-density ceiling,
§3.5); at the moment of violation, the harness injects one corrective line —
right into the hot recency zone where attention is maximal.
[[rule]]
id = "no-unwrap-in-lib"
statement = "in library code, don't use unwrap(); return Result"
detect = { kind = "diff-pattern", pattern = "\\.unwrap\\(\\)", paths = ["src/lib/**"] }
action = "remind" # remind | escalate | block
Three detector types:
- Pattern on diff (regex or tree-sitter query)
- Pattern on bash command
- Pattern on loop events ("edit without read", "commit without test run", "many turns without todo update")
Stream-detectors are executed by the supervisor (§6.2) — violations are caught
before tool execution.
Who writes rules:
- User, by hand
onboard(§5.7) extracts from code conventions- The model itself — after noticing its own repeating mistake or receiving
a review comment, it proposes a rule to itself. Every rule enters via a diff
for user confirmation.
Disciplined injection: one line with a rule id, as a regular append-only turn —
the system prompt is untouched, KV-cache lives. First violation → reminder;
repeat in the same session → escalation; block — only for irreversible actions.
Rule lifecycle: triggers often → candidate for a hard guardrail in code or a
prompt line; hasn't triggered in N sessions → automatically marked stale and
removed. Unlike .md docs, rules don't rot: each has a trigger counter injig stats (§9.5).
Built-in mechanisms — "edit requires read", lint cat→read, doom-loop breaker —
are rules of the same engine, shipped out of the box: one mechanism instead of
three crutches.
🔧 4. Tools — Nine, Designed to the Last Detail
Exactly 9 tools. Each designed by ACI + Anthropic writing-tools principles:
concise output, errors-as-teachers, no duplicate tools. A search_web doesn't
live as a separate tool — it comes through the MCP client optionally. list_dir
is covered by glob. run_tests is bash + gates.
4.1 read
- Input:
path(absolute),offset?,limit?(lines). - Output: hashline format — each line as
N:hh:content, wherehhis a
2–3 char content hash of the line (anchor foredit, see 4.2);grepincontentmode returns lines in the same format. Default max 2000 lines /
40k chars; long lines truncated to 500 chars with a… +N charsnote. - Details: empty file → explicit
<warning: file is empty>(empty output
disorients the model — SWE-agent lesson). Binary/image → metadata (+image in
content for multimodal models). File not found → nearest-name candidates:did you mean src/foo.rs?. - Guardrail:
editrequires a priorreadof that file in the session
(protection against blind edits).
4.2 edit — the hashline format
No patch-diffs (worst format for almost all models), no bare str_replace. The
main format is hashline ("The Harness Problem", oh-my-pi): edits are
addressed by content-hashes of lines from read/grep output — the model
doesn't reproduce old text:
- Input:
path,ops[]; op =replace {anchor, end?, lines}|insert_after {anchor, lines}|replace_text {old_text, new_text}(fallback point fix;old_textmust be unique, otherwise error with match count + fuzzy diagnostics by normalized whitespace). - Key idea: the model doesn't need to perfectly reproduce content and whitespace — if it can recall a pseudo-random tag
2:f1, it knows what it's editing. An entire class of "String to replace not found" errors disappears. - Optimistic concurrency for free: file changed after read → hashes don't match → edit rejected BEFORE corrupting the file. No silent corruption, including with parallel workers.
- All ops in one call are validated against one snapshot and applied bottom-up — line numbers don't drift.
- Output: hash-aware diff with freshly recomputed hashes (±5 lines of context) — chain of sequential edits without re-
reading.
After edit: auto-reindex by tree-sitter; file stopped parsing → ⚠ syntax error at line N immediately (SWE-agent guardrail: don't let the model proceed with a broken file). Blast radius in the response: the diff is appended with call-sites of the changed symbol (tree-sitter) and the first diagnostics from background cargo check — the model sees what it broke in the same turn and fixes it immediately, not three turns later when tests fail.
Edit format — per-model config (JetBrains Diff-XYZ finding: no single winning format): hashline by default; str_replace for models deeply trained on it; apply_patch style for the Codex family. Chosen not by taste but by measurement: the format is selected by jig calibrate (§5.6) automatically for each specific model. All edits go through a transactional journal → undo at any depth, git not required.
4.3 write
- Input:
path,content. Create new or full overwrite. - Guardrail: overwrite of existing requires a prior
read(actual version — we track mtime; file changed externally → error "file changed on disk, re-read"). - Same syntax-check + undo journal.
4.4 grep
- Own wrapper over
regex+ignorecrates (ripgrep engine): respects .gitignore, native speed. - Input:
pattern,path?,glob?,mode=files(file list, default) |content(lines with-Ccontext),limit?. - Output ranked: first matches in "hot" files (by PageRank repo map) — relevance, not alphabet. Truncation with explicit
…+N more matches, narrow your pattern. - Semantic layer is NOT a separate tool:
grepwithmode:"symbols"searches the tree-sitter index (symbol definitions/references) — one namespace, less choice for the model.
4.5 glob
- Input:
pattern(src/**/*.rs), output sorted by mtime (recently changed at the top — almost always what you need).
4.6 shell
- Ordinary commands use one persistent PTY (portable-pty): cwd/env/venv live between calls. Every result ends with
exit 0 · cwd ~/proj/src · 3.2s, so the model never spends a turn onpwdorecho $?. - Long and interactive commands use isolated full PTYs: pass
yield_time_ms, then continue withsession_id+chars(emptycharspolls). The same call can resize the terminal, close stdin, interrupt, or hard-kill it. Legacybackground: truemaps to this lifecycle. - PTY output is streamed byte-for-byte, including partial lines. Each response is a cursor-addressed delta; a caller can replay from an earlier cursor instead of losing output to a destructive poll.
- RAM is bounded per terminal while the complete raw transcript is spooled to
.jig/artifacts. The model receives ANSI/progress-normalized head+tail output with error blocks preserved and recallable. - The single tool accepts familiar
exec_command/write_stdinaliases rather than adding duplicate tools. Commands still pass the permission engine (§7), dialect guard, secret scan, and output filters.
4.7 todo
- Input: full list
[{text, status}](atomic replacement). Stored as JSON, not Markdown: JSON is more resilient to model corruption. Invariant: items cannot be deleted or reordered, only status changes — otherwise the list degrades on long sessions. - This is not a UI decoration — it's attention manipulation (Manus recitation): rewriting the plan at the end of context keeps the global goal in the fresh attention window on long tasks and stabilizes behavior after compactions.
- The harness highlights drift: >10 turns without todo update → system reminder.
4.8 agent (subagents) — bounded, no recursion
- Input:
task, optionalaccess=read_only|workspace, andcontext_files?. Depth strictly 1: a subagent does not spawn subagents. The task defines the work;accessis only a capability boundary. - Subagent context = fork of parent's prefix at the branch point: prompt cache is shared up to the fork, only diverging tokens are paid for (trick from the leaked Claude Code source).
- Return: structured 1–2k tok report in context + full logs to file — results are inspectable and survive interruptions, main context is not polluted with navigation junk.
4.9 recall — what was deliberately NOT made a separate tool
Restores evicted tool outputs verbatim from the journal — a lookup, not a re-run.
Same shape as hashline: replaced reproduction with recall, applied to history
itself. The model passes a hashline-style tag, gets the full output back.
🏷️ 5. Hashline — The Highest-ROI Trick
read/grep output: N:hh:content ← "hh" = short content hash (FNV-1a, base36)
edit input: replace {anchor: "12:f1", lines: "…"} ← recall the tag, don't retype code
The model recalls a pseudo-random tag instead of reproducing exact text and
whitespace. When it can't reproduce the line — it never could. When it can
recall the tag — it knows what it's editing. Implementation: ~200 lines of code.
📊 Measured effect
| Model | Without hashline | With hashline | Boost |
|---|---|---|---|
| Grok Code Fast 1 | 6.7% | 68.3% | ×10 |
| GPT-5.1 Codex Mini | 60% | 77.5% | +17.5pp |
| Gemini | — | — | +8% (more than the avg model upgrade) |
| Grok 4 Fast | — | — | −61% output tokens (retry cycles disappear) |
🎁 Optimistic concurrency is free. File changed after read → tag no longer
resolves → edit rejected before corrupting anything. Reply includes freshly
recomputed tags → chain edits without re-reading.
🗺️ 6. Repo Map
tree-sitter grammars symbol graph personalized PageRank
(Rust, Python, JS, TS, ───────────────→ seeded by files/idents
TSX, Go) defs↔refs in the conversation
↓
top-N file signatures
under token budget
⚡ 2k / 🎯 8k
- 📡 Incremental via file watcher — reindexes changed files only (<10ms per file)
- 🎯 Auto-context on turn 1 — from the task text, identifiers and paths are
extracted, PageRank is seeded with them, and relevant slices of top-files (with
hashline anchors, ready for editing) are placed directly in the first request —
the agent starts editing on the first turn, not the fifth. 3–5 fewer tool calls. - 💡 The model knows where things live from the first turn.
🖼️ 7. Context Engineering
Everything the model sees is laid out in a fixed, append-only order under
the attention curve. Every unstable byte kills KV-cache.
┌─────────────────────────────────────────────────┐
│ STABLE PREFIX (never mutates) │
│ 1. System prompt — static, short, handwritten │
│ 2. Tool schemas — static for the whole session │
│ (inaccessible tools MASKED, not removed) │
│ 3. Project context — AGENTS.md, skills, │
│ repo map, env manifest │
├─────────────────────────────────────────────────┤
│ TRANSCRIPT (strictly append-only) │
│ user messages · assistant blocks · tool results │
│ errors and failed attempts are NOT cleaned up │
│ (model that sees its own failure won't repeat) │
├─────────────────────────────────────────────────┤
│ EPHEMERAL TAIL (after cache breakpoint) │
│ live todo anchor · session line · notifications │
└─────────────────────────────────────────────────┘
Tool masking, not removal: inaccessible tools are blocked at the
decode/validation level + soft reminder at the end, but not removed from the
schema (Manus lesson: removal = cache invalidation + hallucinations on
orphaned tool results).Env manifest: OS & version, shell, cwd, git status & branch, toolchain
versions (rustc/cargo, node, python — what's actually found in the project),
available utilities (rg, fd, jq, docker…), date. Rendered as a terminal
transcript ($ pwd,$ git status -sb,$ ls) — the model reads it as work
it already did. Models typically burn the first turns onpwd,which,ls,
and guessing flags for the wrong OS — the manifest closes this entire class of
reconnaissance in zero turns.Project context (
AGENTS.md): a map, not an encyclopedia — ~100 lines
with progressive disclosure pointing where to dig deeper. Generated and kept
current byjig onboard(§5.7). Skills — scope guides (frontend
conventions, stack), loaded only for relevant tasks — the frontend section
lesson from Codex.Compaction (per Cursor dynamic context discovery): when the window fills
to ~85% — structured summary (goal, decisions, changed files, current todo,
open questions) + full history saved to file, agent given a link: forgot
a detail —grepthe history file. Compaction runs on a small model (cheap,
parallel, pre-emptively — "pre-emptive compaction" in the background before
the threshold).Long tasks (Anthropic long-running agents): compaction isn't enough — the
harness supports structural artifacts:PLAN.md(durable spec) andPROGRESS.md(journal) that survive any compaction. Against "context anxiety"
(model finishes early, feeling the end of the window) in Quality we prefer
a fresh session restart with handoff artifacts over infinite compaction:
PROGRESS.md + JSON-todo + fresh commits orient the new session in one turn.
General principle (Lilian Weng): durable state lives in files, context is
ephemeral.
💾 8. Context MMU — Swapping Instead of Forgetting
Sacred assumption: history is append-only → carry everything (cost + rot)
or summarize (irreversible loss).
Radical move: treat the model's window as RAM and the harness as disk.
Old tool results are paged out, replaced by a hashline-style stub:
[pg:a3f4] evicted read src/engine/mod.rs (9,412 tok, turn 12)
— recall {"tag":"pg:a3f4"} restores it verbatim.
📥 recall brings any page back verbatim — a journal lookup, not a re-run.
Same shape as hashline: replaced reproduction with recall, applied to
history itself.
🧮 Three design rules
| # | Rule | Why |
|---|---|---|
| 1 | Epochs, not reflexes | Evictions queue up in batches. Between epochs the prefix is byte-stable and eligible for provider cache reuse. |
| 2 | Every epoch is arithmetic | Applies iff evicted × horizon × cache_read_cost > tail × rewrite_cost. Token counts are measured; provider cost ratios and horizon are explicit config, not universal constants. |
| 3 | Free epochs ride cache deaths | Compaction kills the cache regardless → MMU sweeps first unconditionally. Same cache cost, zero information loss |
Eviction is deterministic (harness rules, never the model): superseded reads,
consumed grep/glob results, stale passed-check outputs. Recent tail, todo paths,
recalled pages, tiny pages → never touched.
🆚 MMU vs. Summary — the honest comparison
| Criterion | 💾 Context MMU | 🗜️ Summary |
|---|---|---|
| Exact old bytes | Restored verbatim through recall |
Not guaranteed |
| Recovery after compaction | Archived pages remain addressable by tag | Only facts retained by the summary |
| Distortion risk | Deterministic stub + original bytes | Generative rewrite |
| Cache cost | Voluntary epochs pass the configured P&L gate | One cache break per compaction |
| Extreme sessions | Still needs compaction for non-tool history | Produces a bounded continuity summary |
💡 Why: tool outputs are often the largest recoverable part of a coding
transcript — exactly what the MMU relocates without rewriting. A summary is generative text and can lose exact bytes.
A stub is deterministic: "read X, 312 lines, evicted" — nothing to hallucinate.
⚡ 9. KV-Cache Discipline
The #1 speed metric. Agent traffic is ~100:1 input:output → latency is
prefill, governed by cache hit rate. Effect: TTFT and cost ~10× (Manus/Anthropic data).
| Rule | Mechanism |
|---|---|
| 🔒 Byte-deterministic serialization | Own context serializer with fixed key order (serde_json preserve_order). Same transcript = byte-identical request. |
| 📍 Cache breakpoints | Ephemeral tail lands after the breakpoint; stable prefix never mutates. Breakpoints auto-placed for Anthropic API. |
| 🚫 No dynamic bytes in prefix | Date, session clock, notifications → all outside the stable prefix |
| 📊 Observable | jig trace — per-turn latency waterfall + cache_read_tokens / total per turn, visible in TUI |
| 🔁 Replayable | jig replay --fork — counterfactual continuations from recorded sessions. Append-only transcript makes this trivial. |
| ✅ Regression-tested | "4 hellos" test — byte-identical system prompt + tool schemas across 4 turns |
🔮 10. Speculation — Branch Prediction for the Agent Loop
Sacred assumption: the agent loop is sequential. Model thinks → harness waits.
Tool runs → model waits.
Radical move: predict the next tool calls and execute them while the model
is still decoding. CPU branch prediction, literally.
📊 Three categories
A. 📥 Prefetch reads & greps
grep returns 5 hits → speculatively read top-2 files
glob returns paths → same
The predictor extracts real workspace files from grep/glob output and warms a
bounded read cache. It never crosses the workspace boundary, never warms files
above the configured byte limit, and records fire/hit rates per rule. Hit → the
read is served from memory; stale and capacity-evicted entries are discarded.
B. 🧪 Debounced check
The slowest tool in the cycle (cargo check/test, 5–30s) is also the most
predictable: after an edit, a configured gate check often follows. JIG does not
assume a universal probability; the per-repo predictor records it and disables
rules below the configured floor. Running check after every edit wastes resources when the agent
lays down 3–5 edits in a burst.
The fix: debounce + cancel-on-edit.
edit → timer reset ⏳
edit → timer reset ⏳
edit → timer reset ⏳
model starts writing prose → check fires 🚀 (burst over)
check running + new edit → process killed 💀, timer reset
Result: check starts when the stream moves from edits to prose/a non-edit call,
and at explicit priority points (todo transition and end-of-turn). Every result
is keyed to an edit generation; a new mutation cancels or invalidates it.
The configured check is trusted project configuration and may consume CPU even
when its result is not used. /spec exposes fires, hits, cancellations and
wasted completed runtime instead of calling misses free.
C. ⚡ In-decode speculation
We own the streaming tool-call parser → we see arguments seconds before the
call completes:
stream: {"path": "src/engine/mod.rs", ...} ← path visible in first tokens
└── file bytes are warmed before the call closes
stream: {"path": "src/lib.rs", "ops": [{"anchor": "12:f1", ...}]}
└── only the target read is warmed; edit validation still runs normally
This is an accelerator for reads, not speculative mutation. The edit is never
applied before its complete validated tool call arrives.
🚫 Hard allowlist
Only file reads and the one explicitly configured project check are speculated.
Arbitrary model-generated shell, git and network calls are never predicted.
📈 Predictor table
Per-rule hit rates tracked per project (persisted). Rule below threshold →
auto-disables. Falsifiable, inspectable, no neural nets.
🎨 11. Output Filters
Declarative, deterministic. Every bash result passes through filters BEFORE
hitting context — the best pruning is the one that wasn't needed:
| Filter | Effect |
|---|---|
| 🧹 ANSI/progress spam | Stripped |
| 🔁 Repeated lines | Collapsed to line [×N] |
| ✂️ Over-budget output | Head + tail with [… snipped K lines — recall {"tag":"sh:N"}] |
| 🛑 Compiler errors, panics, tracebacks | Never truncated |
[[filter]]
command = "*cargo test*"
keep = "summary"
📊 ≥40% fewer tool-result tokens on noisy sessions, stack traces intact.
The snip idea (declarative shell-output filters):cargo testthat passed →
one summary line,git log→ one-liners, builds → result without progress
bars. Filters are deterministic and never "retell" — unlike LLM compression,
they can't introduce errors.
DCP-style pruning — only atomically with compaction: deduplication of
repeated reads, cleanup of failed-attempt payloads, stub-links instead of stale
outputs — are executed in a batch at compaction time (one cache-break, §5.5)
and never mid-session.
🛡️ 12. Permission Engine
Always-on — even when the OS sandbox is unavailable. Every command passes through
here before it runs:
allow: cargo * → ✅ instant
ask: git push* → ❓ confirms every time (high blast radius)
deny: rm -rf / → 🚫 blocked unconditionally
- 💥 Blast-radius aware —
git push(high) re-asks every time;cargo build(low) approved for session - 🔐 Approve once ≠ approve always — irreversible actions never silently re-approved from cache
permission = [
"allow: cargo *",
"ask: git push*",
]
🏖️ 13. OS Sandbox
Best-effort hardening on top of the permission engine — degradation, not dependency:
| 🖥️ Platform | 🛡️ Backend |
|---|---|
| 🐧 Linux | Landlock + optional seccomp |
| 🍎 macOS | Seatbelt (sandbox-exec) |
| 🪟 Windows | none (permission engine is the backstop) |
[sandbox]
fs_mode = "workspace-write" # read-only | workspace-write | full
net = "denied" # denied | full | ["api.github.com", …]
🏔️ 14. Quality Ceiling — Squeeze More Than the Model Knows
Theoretical basis: over one sample, a model outputs average code from its
distribution — but the distribution also contains much better code (pass@k > pass@1).
"Better than it knows" = five operations on the distribution. All harness
mechanics, not prompt pleas. But selection and extra passes have an honest price —
tokens, latency, new failure points — so the discipline is: escalation by
necessity. Free levers work always; paid ones activate only on trigger. Fast
mode doesn't see this section at all.
| # | Operation | Mechanism | 💸 Cost |
|---|---|---|---|
| 1 | Narrow: types as rails | Interface-first: signatures, newtypes, typestate first. Compiler = per-turn verifier. Wrong code → no compilable form. Plus decomposition: the plan is sliced to units fitting the model's confident generation zone (boundary measured by jig calibrate). |
Free — a skeleton is the first lines of the same files, and every error class closed by the compiler is minus repair cycles (net token savings) |
| 2 | Shift: anchor with quality | Exemplary repo fragments selected by onboard serve as style anchors. Legacy code in context is always marked ("old style, new style — here"). Cost = 0 extra tokens: exemplars replace less useful slices in the same budget. |
Free |
| 3 | Select: adversarial test-pair | A separate task can write tests before implementation when the workflow explicitly asks for it. The harness does not guess API importance from source-language keywords. | Explicit — paid only when requested by the workflow |
| 4 | Push: second pass "make it right" | Fresh-context refactor after green gates when requested by the workflow or justified by measured gate output. Review prose is not keyword-classified. | Explicit — no semantic trigger hidden in code |
| 5 | Ratchet | Coverage never drops, warnings never grow, complexity never grows. Each merge can only raise the bar. The repo statistically drifts upward — and through anchoring (op #2) feeds tomorrow's sessions: a compounding quality cycle. | Free — lives in CI gates, model doesn't even know about it |
Budget discipline: every paid lever's ROI is measured by jig stats (§9.5):
didn't yield measurable improvement in accepted diffs on this project → disabled
by config.
💰 14.5 Cheap Techniques with Disproportionately Large Boosts
Before making the system more complex (multi-agent, fine-tuning, new models),
we exhaust every "low-hanging" technique — each costs hours-days of work but
gives the effect of a model upgrade.
| Technique | Cost | Measured effect |
|---|---|---|
| Hashline edit (§4.2) | ~200 lines of code | Grok Code Fast 1: 6.7% → 68.3% (×10); GPT-5.1 Codex Mini: 60% → 77.5%; Gemini: +8% (more than the avg model upgrade); −20% output tokens; Grok 4 Fast: −61% tokens from vanishing retry cycles |
| Per-model edit-format routing | config | Aider: format switch moved GPT-4 Turbo from 26% to 59% |
| Architect/Editor split (Aider) | one extra prompt | reasoning model solves the task, cheap fast model formats the edits; model doesn't split attention between "solve" and "follow format" — quality and speed rise simultaneously |
| Tool Use Examples | 2–3 call examples in tool schema | noticeable accuracy boost on complex tool calls (Anthropic advanced tool use) |
| Tool result clearing | — | stale tool outputs replaced with stub-link to file artifact (Anthropic context editing): context lives longer, information isn't lost. But: clearing breaks append-only, hence KV-cache. So clearing is only done atomically with compaction — one cache-break instead of constant ones |
| Self-verification + tracing | gate scripts + trace parsing | LangChain deepagents: 52.8 → 66.5 on Terminal Bench (+13.7) without model change |
| Strict schemas / constrained decoding | strict: true in API, llguidance/xgrammar locally |
zero syntactically broken tool calls |
| Recitation via todo (§4.7) | already in core | stabilization of long tasks (Manus) |
📐 15. Calibration — The Harness Configures Itself to the Model
The weak spot of all leaders: behavior is hardcoded for 2–3 flagship models, and
when a new one drops, the harness lags by months (Codex prompt was written for
the GPT-4.1 era and never revisited — result known). JIG makes per-model config
a measured artifact:
- New model → calibration run of ~15 minutes on a mini-bench: best edit format
(hashline / str_replace / apply_patch), reliability of parallel tool calls,
reliable-context-derived repo map budget, instruction-density tolerance,
whether explicit thinking improves the probe, and break-points (max ops pereditcall, actual JSON nesting depth in tool arguments). - Result — TOML model profile: committed, diffed, reviewed. No "I feel like this
model likes patches." - Model upgrade is a recalibration event: rerun the command and review the
profile diff. Calibration never silently changes a saved profile.
jig calibrate --save # writes .jig/profiles/<model>.toml
jig calibrate --thorough # more trials, wider sweeps
The day a new model drops is the day of its full support — not the start of a
month of manual tuning.
🧭 15.7 jig onboard — The Harness Configures Itself to the Project
Paired with calibrate: that one tunes the harness to the model, this one — to
the codebase. Runs in the project root, works as a one-shot Quality session with
parallel explore-subagents, and outputs everything as a single diff for review:
onboard proposes, user approves.
- Studies the project: repo map + explore-subagents across zones
(architecture, conventions, tests, entry points) + git history (what changes
most often, where bugs are most often fixed). - Writes/updates
AGENTS.md: a ~100 line map (§3), not an encyclopedia.
Existing AGENTS.md / CLAUDE.md / .cursorrules are not overwritten but
checked against the code: every claim is verified against the actual state
of the repo; stale claims are flagged and proposed for removal. The rot of
agent docs is fixed by no one today — and a stale instruction is worse than
none (the model trusts it). - Generates skills: scope guides by zone (frontend conventions, tests,
migrations) — from real patterns in this code, not general advice. Loaded only
for relevant tasks (§3) — the frontend-section lesson from Codex. - Configures
jig.toml: finds build/test/lint commands (Cargo.toml,
package.json scripts, CI configs) and proposes ready gates (§5). - Extracts principles from code, not invents them: a rule enters docs only
with evidence — a link to an example file ("errors — via thiserror, see
src/error.rs"). Empty mantras ("write clean code") are cut: generated docs
pass the sameprompt-lintand the same token budgets (§9.5) as handwritten ones. jig onboard --refresh: periodic revision — checks every claim in
AGENTS.md against the current code and highlights stale content without
rewriting everything. Cheap: facts are verified, not text regenerated.
Result: every future session starts with an accurate map, current conventions,
and ready gates — faster (fewer explore turns), more accurate (fewer
hallucinations about the project), higher quality (project conventions followed
from the first commit).
🌐 16. Providers
Two wire formats cover ~95% of models:
| Format | Models |
|---|---|
| 🔷 Anthropic Messages | Claude family |
| 🟢 OpenAI-compatible | GPT, Groq, OpenRouter, Together, Ollama, LM Studio, … |
The transcript is stored in a neutral format and serialized per-provider
deterministically on the fly → enables mid-session model hot-swap
without losing context (Cordy-style).
jig provider add groq \
--base-url https://api.groq.com/openai/v1 \
--api-key-env GROQ_API_KEY \
--model llama-3.3-70b-versatile
jig --provider groq # use it
jig provider list # built-ins + custom, with key status
jig provider test groq # one-shot connectivity check
🔑 Per-provider keys live in ~/.config/jig/auth.toml (0600), never in the
committed jig.toml. Precedence: JIG_API_KEY_<PROVIDER> env → auth store →
canonical env vars → legacy config.
🧠 17. Reasoning
Extended thinking is user-sovereign. Nothing else touches it — not Fast mode,
not Quality mode, not calibration, not the model profile.
jig --reasoning high -p "…" # off | low | medium | high
In the TUI: /reasoning high or Ctrl+R to cycle.
| Level | Thinking budget |
|---|---|
| 🔇 off | — (default) |
| 🔹 low | 2k tokens |
| 🔸 medium | 8k tokens |
| 🔺 high | 24k tokens |
Calibration measures and reports whether thinking helps — but never turns it on.
🤖 18. Local Sidecar — Small Models on the User's Machine (Experimental)
Principle: local models are accelerators, not solvers. None of them make
decisions that end up in the final code, and the loop is fully functional
without them (degradation, like with the daemon). One inference layer:
llama.cpp bindings (or candle) behind the same Provider trait — a local model
is indistinguishable from a cloud one to the core. Lives in the daemon; weights
are lazily downloaded, only for enabled roles.
| Role | Candidate | What it gives |
|---|---|---|
| Embedding index | EmbeddingGemma-300M / Qwen3-Embedding-0.6B (both strong in code retrieval) | Semantic layer where grep is blind: session history, compact summaries, docstrings, commits. NOT a replacement for repo map and NOT part of the core |
| Prefetch / next-action predictor | 1–4B tool-caller: 4B class holds ~97.5% on tool-calling tests; specialized Hammer/xLAM — comparable at 1.5B | Feeds speculations §10: guesses next read/grep while the big model thinks; miss is free (read-only) |
| Utility model | same 1–4B model | Compaction, summaries, session headers, command classification for permission engine — offline, free, doesn't burn the main provider's rate limit |
| Draft for speculative decoding | 0.5–1B from the main model family | 1.5–3× tok/s, but ONLY when the main model is also local (logit access needed). For cloud models, this role is already covered by predicted outputs |
What we deliberately DON'T do: a "model that codes terribly but calls tools
perfectly" as the main dispatcher. In a coding agent, tool arguments ARE code
(edit.lines, bash commands): such a model calls tools perfectly in form and
incorrectly in content — the worst class of errors (valid, but wrong). Hard
boundary: small models touch only read-only and reversible paths.
🖥️ 19. Interactive TUI
Streams the model token-by-token. The agent runs on a background task; the
async UI renders events live:
┌──────────────────────────────────────────────────┐
│ jig · provider/model-id · ⚡ fast · 🧠 off │ ← status bar
│ ctx 41k/200k · evicted 18% (epoch 3) · cache 87% │
├──────────────────────────────────────────────────┤
│ │
│ ▐█ ▌ You │ ← heavy left accent bar
│ ▐█ ▌ add a health check endpoint │
│ │
│ Let me look at the current routes first. │ ← borderless prose
│ │
│ $ → read src/main.rs 12ms │ ← tool card (collapsed)
│ ✱ → edit src/main.rs 340ms │
│ + pub fn health() -> impl Responder { │ ← diff tinted +/−
│ + HttpResponse::Ok().json(…) │
│ ⚙ → cargo check 2.3s ✓ │
│ │
├──────────────────────────────────────────────────┤
│ > _ │ ← composer
│ jig · ⚡ · Ctrl+R reasoning · / commands │ ← footer
└──────────────────────────────────────────────────┘
- 🎨 Heavy left accent bar for things that matter (your messages, tools, errors)
over a three-step background ladder; borderless indented prose for the assistant - 🎯 Color reserved for state — role, running, warning, failure
- 🃏 Tool cards — icon gutter
$ → ± ✱ ⚙, duration, collapsed preview; click to expand - ⌨️ Everything is a keystroke —
/autocomplete,^Ppalette,@file mentions,Ctrl+Rreasoning - 🖱️ Mouse — scroll, click to expand/collapse
- 🗣️ Steer while it works — type during a turn; injects at next safe boundary (never orphans tool pairs)
- 🛑 Esc interrupts the stream immediately
⚙️ 20. Config — jig.toml
Data, not hardcode. Every dial has a default; tune what matters.
[model]
provider = "anthropic"
name = "<model-id-from-jig-models>"
[gates]
check = "cargo check --message-format=json"
test = "cargo test -q"
lint = "cargo clippy --workspace --all-targets -- -D warnings"
fmt = "cargo fmt --all --check"
[sandbox]
fs_mode = "workspace-write"
net = "denied"
# 🛡️ Active rule: 0 attention until violated → one corrective line
[[rule]]
id = "no-unwrap-in-lib"
statement = "in library code, don't unwrap(); return a Result"
action = "remind" # remind | escalate | block
[rule.detect]
kind = "diff-pattern"
pattern = "\\.unwrap\\(\\)"
paths = ["src/lib/**"]
# 🎨 Declarative output filter
[[filter]]
command = "*cargo test*"
keep = "summary"
🔧 21. Meta Commands
jig prompt-lint # 🧹 static-lint prompt + schemas (fails CI)
jig calibrate --save # 🎯 deep calibration → per-model profile
jig trace # 📊 latency waterfall + cache accounting
jig replay <id> --fork 4 --prompt "try X" # 🔁 counterfactual replay
jig autopsy # 🔬 classify failures → prompt/tool/context/model
jig stats # 📈 recorded token totals + input-weighted cache hit rate
jig doctor # 🩺 environment & config health check
jig sandbox-probe # 🛡️ sandbox + permission posture
jig sessions # 📋 list recorded sessions
jig resume <id> # ▶️ resume a session interactively
jig models # 📋 fetch models from the selected provider
jig tools # 🔧 list the agent's tools
jig config # ⚙️ print effective configuration
Inside the TUI, /mmu, /spec, and /pnl expose page-store state,
speculation outcomes, and evidence-backed context attribution respectively.
🚫 22. Anti-Overengineering — What We DON'T Do
- No graph workflow engines, YAML orchestrators, "cognitive architectures" — loop + 9 tools + 2 policies cover everything.
- No vector DB and RAG pipeline — repo map + grep + tree-sitter are faster, more accurate, and require no embedding indexing (consensus: Claude Code, Codex, OpenCode all work without vectors). The only exception — optional embedding layer in the daemon-sidecar (§18): semantics on top of history and docs; the core is fully functional without it.
- No own plugin runtime — extensibility through MCP and hooks (shell commands on events: pre-edit, post-turn), like Claude Code.
- No multi-agent "zoo of roles" (34 specialists etc.) — one generic subagent tool; the task defines the job and the harness only enforces read-only or workspace access.
- No own LSP server at launch — tree-sitter covers 80% of the value; LSP client is a late optional stage.
- Own codebase hygiene — per anti-lessons from the leaked Claude Code (390k lines of TS, god-files of 5000+ lines, 1000+ scattered feature flags) and Codex CLI (515k lines of Rust): hard size budget, invariants enforced by lints, not docs.
- Every model upgrade = harness revision: every crutch encodes an assumption "the model can't do this," and that assumption rots. We remove components one by one, measure with a benchmark, and throw out what doesn't carry its weight.
🧪 23. Automated Harness Tests
The harness is infrastructure (§0), so it has its own CI. Three layers:
deterministic core tests → replay tests on recorded traces → prompt linting.
Everything fast runs on every PR.
| Test | What it proves |
|---|---|
| 🔒 Context serialization snapshot | Same transcript renders into byte-identical request on re-run; adding a turn changes only the tail. KV-cache regression test: any "floating" byte in the prefix fails the test before the token bill arrives |
✏️ Property tests (edit) (proptest) |
On random files and op sequences: apply→undo returns the original byte-for-byte; edit by stale hashes always rejected; ops in one call don't intersect or shift each other's anchors |
| 📡 Fuzz (SSE/tool-call parser) | Valid stream sliced into random chunks gives identical result; broken events → error, not panic. Own parser (§2.1) = own guarantees |
| 🔁 Replay tests | Recorded sessions (jsonl) run against a mock provider: tool execution order, read-only concurrency, truncation, compaction — asserted on real traces, not synthetics |
| 🛡️ Sandbox matrix | Catalog of forbidden actions (rm -rf /, writes outside workspace, network outside allowlist) must be blocked on every OS; after every incident the catalog grows — "every agent error is a harness bug" (§0) |
| ✂️ Truncation invariants | Every tool output after truncation: ≤ token budget, contains head and tail, artifact link exists and is readable |
jig prompt-lint — automated prompt fitness test
Prompts and tool schemas pass static linting with the same rights as clippy on
code: fails → merge blocked. Checks directly from the Codex CLI teardown: almost
everything Theo ripped their prompt for is caught by regexes and token counters —
no need to wait for users to notice.
| Check | Threshold | Why |
|---|---|---|
| Token budget | system prompt ≤ 2k tokens, tool description ≤ 300, AGENTS.md template ≤ 1.5k; counted with the target model's tokenizer, not words | Bloated prompt = tax on every request and diluted model attention |
| Word repeats | content word appears > 5 times → warn, > 10 → fail (stop-words ignored) | Symptom of "card ×12" from the Codex prompt: repetition means the author didn't notice they already said it — or it wasn't written by a human |
| Instruction duplicates | two instructions with matching 6-grams → fail | Same instruction in different words — the model treats them as two different ones and "averages" |
| Unstable bytes | timestamps, dates, random ids, counters in system prompt → fail | Every floating byte in the prefix kills KV-cache (§9) |
| Forbidden patterns | regexes: rituals ("every/каждые N seconds"), style prescriptions (px values, icon library names), ALWAYS/NEVER density > 3 per 100 lines | Hyper-prescriptions the model executes literally — "update every 30 seconds" burned millions of tokens |
| Dead links | every mentioned tool/file/section must exist in the registry, otherwise fail | Stale mentions = hallucinations on non-existent tools |
| Contradictions (optional, nightly CI) | cheap model searches for pairs of conflicting instructions; output — report, not blocker | "Don't stop at suggesting — just do it" vs "don't implement while the user is brainstorming" — typical Codex conflict |
Lint catches form, the bench measures effect: a prompt-diff without greenprompt-lint and a mini-bench run does not merge.
jig autopsy — the harness that fixes itself
Closing the loop. The self-filling benchmark (table above) collects failures —
autopsy dissects their causes and turns them into harness fixes:
- Post-mortem of every failure: after an unsuccessful session, a cheap model
reads the trace and classifies the root cause into four buckets: prompt / tool
/ context / model. One failure is an opinion, failure statistics are a
diagnosis: "31% of monthly failures — edit by stale hashes after background
bash commands" — that's a report line, not a feeling. - Draft-PR into the harness: for a repeating pattern, autopsy proposes a
concrete fix (prompt, tool schema, context policy — or a new active rule,
§3.7) as a draft-PR: diff + links to N failure traces + predicted effect.
Merged like any PR — throughprompt-lintand mini-bench. - Protection from reward hacking (Lilian Weng): autopsy is strictly
read-only and never measures itself — it proposes and predicts, the bench
delivers the verdict. Otherwise the system would start optimizing reports
instead of the harness. jig stats— harness efficiency over time: tokens per accepted diff line,
undo rate, average repair iterations, cache hit rate — by week. The harness
must improve measurably; if the graph stalls — stale crutches have accumulated
(§22, item 7) and it's time for a revision.
🗺️ 24. Build Order (Roadmap)
Why this is feasible solo: v0.1 is ~5–7k lines of Rust without a single complex
subsystem. Leaders lose not because they have few people, but because they have
390–515k lines of legacy they can't rewrite and thousands of feature flags they
can't touch. A small measured system evolves faster than a big one — that's JIG's
main bet.
- v0.0: mini-bench (20 tasks on real code) + skeleton of
jig trace+jig prompt-lint
(§23). First the measurement stand, then the agent: every next decision is
made by the bench, not taste. - v0.1: Provider trait + SSE + loop + read/edit/write/bash/grep/glob + TUI-minimum.
Already a working agent. - v0.2: transcript log, KV-cache discipline, permission engine + sandbox,
undo journal, todo. - v0.3: tree-sitter index + repo map + syntax-gates, streaming executor,
parallel tools. - v0.4: Fast/Quality policies, gates + repair cycle, compaction with
history-file, long session protocol, explore/review subagents,jig calibrate. - v0.5: daemon, speculative prefetch/gates, speculative rewrite, workers +
worktrees + workflow-plans, MCP, hooks,jig onboard. - Beyond — only by data from
jig trace, mini-bench, and Terminal Bench
runs (comparison with leaders on equal models). Every harness fix — per AHE:
tied to a specific failure trace, with predicted effect, verified by the
next run; fix candidates supplied byjig autopsy. System prompt change =
PR with mandatory bench run.
🪟 25. Windows
Fully supported (10 1809+ / 11, Windows Terminal recommended):
| Layer | Detail |
|---|---|
| 🐚 Shell | ConPTY → Git-Bash → PowerShell → cmd.exe. Dialect-aware backgrounding, markers, line endings |
| 🔒 TLS | rustls + bundled webpki — MSVC-clean (no schannel) |
| 🖥️ TUI | crossterm/ratatui — native, mouse included |
| 🛡️ Sandbox | OS layer reports none; permission engine is the backstop |
🧱 26. Rust Stack
| Layer | Crates | Note |
|---|---|---|
| Async runtime | tokio | de-facto standard (Codex CLI, Goose) |
| HTTP | reqwest + rustls | SSE parser is own (~100 lines) |
| Serialization | serde, serde_json (preserve_order) | determinism for KV-cache |
| TUI | ratatui + crossterm | like Codex CLI/Crush |
| Code parsing | tree-sitter + grammars for top-15 languages | repo map, syntax-gates, symbols |
| Search | regex, ignore, grep-searcher | ripgrep engine as a library |
| PTY | portable-pty | persistent bash |
| Watcher | notify | incremental index |
| Storage | rusqlite + jsonl | sessions, undo journal, replay |
| Sandbox | own Seatbelt/Landlock bindings | modeled after Codex CLI |
Forbidden: langchain-like frameworks, ready agent-SDKs, heavy DI/plugin systems.
The MCP client is the only external integration surface.
🧱 27. Built Without Frameworks
| Layer | Implementation |
|---|---|
| 📡 SSE parser | ~100 lines, hand-written (jig-core/src/provider/sse.rs) |
| 🔧 Tool-calling | Own JSON Schema → own streaming assembly → own incremental parser |
| 🗺️ Repo map | tree-sitter → symbol graph → personalized PageRank |
| 🛡️ Sandbox | Raw Landlock / seccomp / Seatbelt bindings via libc |
| 🔍 Search | ripgrep engine as a library |
| 💻 Terminal | portable-pty for persistent bash |
| 🖥️ TUI | ratatui + crossterm |
📚 28. Sources
- Cursor: Shadow Workspace, Instant Apply / speculative edits, Dynamic context discovery, Fusion Tab
- Manus: Context Engineering for AI Agents
- Papers: SWE-agent / ACI (2405.15793), Agentless (2407.01489), CodeAct (2402.01030), Speculative Actions (2510.04371), Speculative Tool Calls (2512.15834)
- Model physics (§3.5): Lost in the Middle (2307.03172), Found in the Middle, StreamingLLM / attention sinks (2309.17453), Why do LLMs attend to the first token? (2504.02732), Chroma — Context Rot, IFScale (2507.11538), Fragile Preferences (2506.14092)
- Anthropic: Writing effective tools, Effective context engineering, Long-running agents, Building effective agents, Advanced tool use, Context editing
- Aider: Repo map with tree-sitter, Architect/Editor split, Edit formats
- OpenAI: Harness engineering; Codex Agentic Patterns
- Can Bölük: The Harness Problem / hashline — benchmark of 16 models × 3 edit formats
- LangChain: Improving Deep Agents with harness engineering
- Theo (t3.gg): Codex system prompt teardown, AgentRiot "the harness is part of the model", Claude Code leaked source analysis + Terminal Bench, T3 Code
- Lilian Weng: Harness Engineering for Self-Improvement — ACE, MCE, Self-Harness, AHE
- Best Practices for Building an Agent Harness — consolidated Anthropic+OpenAI practices
- JetBrains: Diff-XYZ — edit format benchmark
- Local models: Hammer — on-device function calling (2410.04587), Qwen3-Embedding-0.6B, EmbeddingGemma-300M, Local LLM tool calling test, LM Studio — speculative decoding
- Context economy (§6.5): snip — declarative shell output filters, opencode-dynamic-context-pruning, pi-dcp
- Foundation catalog: Coding Harness Atlas — harnesses (281) and addons (~360)
📜 License
MIT.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found