Protocol Report · For Review

File Ops Protocol

A deterministic, token-lean contract between a developer and an AI model for producing and applying code changes — where every rule exists for a specific reason, and nothing is included that does not earn its token.

PurposeSafe, minimal, reviewable code changes
PrincipleCorrectness over verbosity
OutputPatches, not prose

At a glance

The protocol governs three things: what the model is allowed to output, in which form it delivers changes, and how the reviewer verifies them. It replaces free-form chat with a machine-parsable patch stream.

Lean output

No tools, no prose, no echo. The model writes only file edits and console blocks, then a footer.

Why: Every non-patch token is a token not spent on correctness. Prose corrupts the file stream and confuses reviewers.

Cheapest-safe form

Structural moves use one-line commands. Content changes use search/replace until cost crosses 70% of the file.

Why: Sending unchanged lines is waste; sending too few lines causes wrong matches. The threshold keeps both errors out.

Reversible and audited

Every structural change is a single named command with an :undo escape hatch and a machine-readable footer.

Why: Reviewers need the diff in plain English before they trust it. Silent structural edits are the number-one source of accidental breakage.

Core principles

These rules define what the model is allowed to do. Each exists because omitting it introduced a concrete failure mode.

No tools, no Python, no terminal

The model never runs, tests, or executes code. It writes the change directly.

Why: Execution is non-deterministic and unreviewable. The protocol is a patch format, not a runtime. Anything the model executes produces output a reviewer cannot diff or undo.

Only file edits before the footer

Before ### Changed files:, the model outputs only console blocks and file-edit blocks. No prose, labels, notes, or recap.

Why: The stream before the footer is parsed as a patch. Stray text corrupts file boundaries and produces broken or misapplied changes.

No preamble, no echo, no recap

The model skips introductory sentences, restating the request, and summarizing what it just did.

Why: These lines carry zero information for the patch applier. They cost tokens, add latency, and risk being misparsed as file content.

Short reasoning, write once

The model plans once, writes once, and stops. It does not re-check, re-derive, or repeat a block.

Why: Re-derivation cannot improve an already-correct patch. It only spends tokens and introduces drift between what the model reasoned and what it wrote.

Cover every part of the request

If a name, signature, or path changes, all its usages and imports are updated in the files the model has.

Why: Partial patches compile but fail at runtime or at import time. The reviewer should never have to finish the model's rename.

Ambiguous → take the most likely reading

When the request is unclear, the model proceeds with the most probable interpretation instead of stalling.

Why: A stalling turn produces nothing. A wrong turn produces something the reviewer can correct in one follow-up. Something beats nothing.

Missing file → never guess

If file content is missing, the model edits what it can and lists the missing paths under Need: after the footer.

Why: A guessed file looks plausible and then fails at review. A named missing path lets the developer fix the input in one step.

Do not move, rename, or delete unless required

Structural changes happen only when the user asks, or when the request cannot be fulfilled without them.

Why: Structural changes break imports, CI paths, and review context. They must be explicit and auditable, never incidental to a content fix.

Format choice — cheapest safe

Every change has more than one valid representation. The protocol picks the one with the lowest total cost — measured in tokens, diff surface, and risk — that still produces a correct patch.

Delete

:rm <path|glob>

Why: One line, atomic, reversible. Writing a delete as a file edit would require sending the whole file just to prove it is empty.

Move

:mv <src> <dest>

Why: A move is a path change, not a content change. Encoding it as a rename inside a diff is verbose and loses git's rename detection.

Rename

:rn <old> <new>

Why: Same reason as move, plus one rename command can update an extension or substring across many files at once.

Undo structural change

:undo / :unrm <path>

Why: Structural mistakes are the most expensive to correct by hand. An explicit undo turns a destructive action into a reversible one.

New file

Full file

Why: There is no prior content to anchor a search against. The full file is the only correct representation.

Existing file

Search / Replace

Why: S/R sends only the changed lines. Full file sends everything. S/R wins unless the cost rule says otherwise.

Cost threshold

S/R ≥ 70% of file

Why: Below 70%, S/R is cheaper than the full file. Above 70%, the patch is essentially the whole file, so sending it whole is simpler and less error-prone.

No unique anchor

Full file

Why: A search string that matches more than once either fails or silently edits the wrong location. Without a unique anchor, the full file is the only safe format.

File too large

Always S/R

Why: A file that does not fit in one reply cannot be sent whole. S/R is the only viable format regardless of cost.

Remove code

S/R · empty replace

Why: Deleting lines is a content change, not a structural change. S/R with an empty replace is the smallest correct form.

Console commands

Structural operations are single, named, logged commands. They appear in one fenced block before the footer, and they are the only place where moves, renames, and deletes live.

CommandEffectWhy it is a command
:rmRemove path or globRemoving a file must be explicit, auditable, and reversible.
:mvMove file or directoryPreserves git rename detection and keeps imports traceable.
:rnRename file, extension, or substringOne command can update a symbol across many files without touching content.
:undoUndo last structural opStructural mistakes are the costliest; undo makes them cheap.
:unrmRestore deleted fileRecovery for the only operation that destroys content.
:mv -f / :rn -fForce overwriteOverwriting must be explicit; the flag keeps it intentional.

Order matters. Structural commands run first (:mv/:rn), then file edits use the new paths, then :rm runs last.

Why: Edits must reference paths that will exist after the move, and removals must not race with edits that still need the old file.

File edits

Content changes follow one of two formats: a full file, or a numbered search/replace under a single file header.

One header per file

--- File: <path> ---

Why: The header is the parser's only signal that a new file begins. Multiple headers for one file would split its edits across unrelated blocks.

Relative POSIX paths, full extension

src/app.js, src/App.vue

Why: Absolute paths break on other machines. Missing extensions make file type ambiguous and defeat language-specific tooling.

Search is the shortest exact unique match

The search block should match exactly once — usually 1–3 lines, max 12.

Why: Longer searches are safer but cost more. Below 12 lines we can afford context; beyond that, the search itself becomes the bulk of the patch.

Copy character-for-character

Spaces, tabs, and blank lines are preserved exactly. Never use ellipses or placeholders.

Why: A single wrong space makes a search fail silently. The model must reproduce the file, not paraphrase it.

No unchanged lines between changes

If two changes are separated by two or more unchanged lines, they get separate edits.

Why: Unchanged lines in the middle increase cost and make the anchor non-unique. Separate edits are cheaper and clearer.

Adjacent edits merge

Edits that touch or sit within one line of each other are one block.

Why: Two adjacent edits would overlap or fight over the same anchor. One block applies both deterministically.

Number edits top to bottom, restart per file

Edit 1, 2, 3, each applied to the file after the previous one.

Why: The reviewer reads top-to-bottom and can undo to any numbered checkpoint. Global numbering would be meaningless because each file is independent.

Multi-turn behaviour

Each turn edits what the previous turns produced. The final commit is all turns applied in order — later turns override earlier ones on the same lines, and untouched lines stay exactly as they were.

Later turns override earlier lines

If a line changed in Turn 1 changes again in Turn 3, the Turn 3 value wins.

Why: This mirrors how a human iterates: each edit supersedes the last.

Use new paths after structural changes

After :mv, :rn, or :rm, all subsequent edits reference the new paths.

Why: Editing the old path after a move targets a file that no longer exists.

Mistakes get a minimal S/R

If a prior turn was wrong, the next turn sends the smallest search that isolates the wrong lines.

Why: Re-sending unchanged code costs the same as the original patch and obscures what actually changed between turns.