shared/edit-format
Source:
shared/edit-format.md
shared/edit-format.md — canonical edit format for /adk-implement
Inspired by Aider’s
diffedit-format. Used byadk-agent-implementerfor every file mutation. Prevents whole-file rewrites and silent regressions.
Format
Every code edit is a SEARCH/REPLACE block anchored to a specific file path.
path/to/file.py<<<<<<< SEARCHdef existing_function(x): return x + 1=======def existing_function(x: int) -> int: """Return x incremented by one.""" return x + 1>>>>>>> REPLACERules:
- File path is the first non-empty line of the block. Repository-relative; no leading
./. Absolute paths are forbidden. SEARCHsection contains the EXACT current text, byte-for-byte, including whitespace and surrounding context. Minimum 1 line, maximum ~40 lines. No paraphrase, no ”…” elision, no comments-explaining-edits.REPLACEsection contains the EXACT new text. Empty replace = deletion. Empty search = insertion at file start (requires the file to be new or the location to be unambiguous; otherwise use an anchored search).- Markers are literal:
<<<<<<< SEARCH,=======,>>>>>>> REPLACE. Exactly 7 angle brackets, 7 equals signs. - One block per logical change. If you’re touching three regions in one file, emit three blocks (same path repeated). Don’t merge.
- Anchor enough context so the search is unique within the file. If the file has two
def existing_functiondefinitions, include the class name or surrounding line that disambiguates. The implementer must verify uniqueness BEFORE writing the block.
New files
For a new file, use an empty SEARCH:
path/to/new-file.py<<<<<<< SEARCH======="""new module."""def hello(): return "world">>>>>>> REPLACEThe implementer’s tool then creates the file with the REPLACE content.
Deletions
For a deletion, use an empty REPLACE:
path/to/file.py<<<<<<< SEARCHdef deprecated_function(): return None=======>>>>>>> REPLACEIf you’re removing an entire file, the implementer uses rm <path> (logged separately, not via block).
Renames
Two operations: a delete-block on the old path + a create-block on the new path. Don’t try to express a rename as a single block.
Anti-patterns
- Whole-file replace when one block changed. Forbidden — defeats the discipline.
- Paraphrased search (“change the function to add types”). Forbidden — the implementer can’t apply a vague search.
- Search with
...elision. Forbidden — must be byte-exact. - Multiple files in one block. Forbidden — one block, one file.
- Search that matches multiple locations. Forbidden — disambiguate with context.
- Trailing newline mismatch. The implementer’s apply step must respect the file’s trailing-newline convention; the model is expected to preserve it.
Pre-edit verification (the implementer does this, NOT the human)
Before emitting a block, the implementer must:
- Have Read the file in this session. Constitution §V; non-negotiable.
- Verify the SEARCH is exactly current. If a previous edit in this session changed the relevant region, re-Read first.
- Verify SEARCH is unique in the file. If not, add surrounding context until unique.
If any verification fails, the implementer stops and reports — not silently retries.
Output channel
SEARCH/REPLACE blocks are emitted by the implementer agent. The skill’s runtime apply step:
- Validates each block against the actual file content.
- On any mismatch: stop, surface the diff, ask user to confirm or abort.
- On match: apply via
Edittool. - Each applied block produces one entry in
<task-slug>/diffs/applied.jsonlfor traceability.
Why this format
- Smaller diffs than whole-file. Same edit, 1/20th the tokens for a 200-line file.
- Catches drift. If the model “remembers” file content differently from disk, validation fails immediately rather than overwriting the user’s work.
- Reviewable. A human can read the block in seconds and know exactly what changed.
- Composable. Multiple blocks in one report are independently apply-able and individually revertible.