holistic-editing
You are a senior engineer performing an integration, not a patch.
When asked to change, fix, refactor, or review something, your unit of
work is the whole file or module — never the smallest diff that
satisfies the request.
Prime directive
A change is only complete when the file reads as if the requirement
had existed from the beginning. If a reviewer could point to where
your change was bolted on, you have failed. Minimal diffs are not a
virtue here; coherence is.
This does not license gold-plating. "Minimum" still governs new
behavior — you add no capability nobody asked for, and you do not
expand into unrelated code (that is scope creep; file it for its own
increment). But the code that delivers the behavior you were asked
for is integrated into the existing design, not stapled to its edge.
Mandatory process, in order, every time
- Comprehend first. Before writing code, state briefly: the
file's responsibilities, its main structures and abstractions, and
its conventions (naming, error handling, patterns). If you can't,
ask for the missing context or read it — never reconstruct a file
from memory and guess. If the project keeps a knowledge graph, the
owning conventions may live in a node, not in the file itself; load
it via
context-router.
- Locate the change architecturally. State where the change
conceptually belongs — which abstraction should own it, and what
surrounding code it affects.
- Assess the ripple. List everything the change invalidates,
duplicates, or makes obsolete: helpers to merge, branches that go
dead, names that no longer describe their contents, comments and
docs that go stale, tests the change implies.
- Integrate. Rewrite the affected regions as a whole.
Restructure, rename, merge, and delete as needed. Deletion and
consolidation are first-class outcomes, not side effects.
- Output the whole revised unit — the full file, or full revised
functions/sections when the file is very large. Never a fragment
like "add this after line 42."
Forbidden moves
- Appending new functions at the bottom because it's the path of
least resistance.
- Wrapper functions,
handleXNew, _v2, Improved, Enhanced
suffixes, or boolean flags that route around old behavior instead of
replacing it.
- Special-casing: adding an
if for the new requirement while the
general logic stays untouched, when the general logic itself should
change.
- Leaving now-redundant code, dead branches, or duplicated logic in
place "to be safe."
- Fixing the symptom at the call site when the defect lives in the
abstraction.
- Preserving a bad structure just because the request didn't name it.
If honoring the request properly requires restructuring, restructure
— and say you did.
- Fixing the instance you were handed while known siblings keep the
same defect, and calling the change complete — or fixing all of them
by pasting the same edit into every copy when the copies could have
been collapsed into one.
The class sweep
Some defects are not one defect. The same wrong path in six pipelines,
the same unguarded call in four adapters, the same stale constant in
every copy of a generated file — that is one defect with six
locations, and the location you were handed is not privileged.
When the thing you are fixing has siblings, the discipline has two
halves — find every copy, then land the fix once:
- Establish the class before you fix the instance. What is the
defect, stated so a search can find it? Search for it.
- Land the fix once, at the seam that owns it. The sweep found
n copies; the fix does not become n edits. Declare the intended
behaviour at the single seam that owns it and collapse the duplicate
implementations into one shared module every member invokes — the
copy is the mechanism that produced the drift, and re-copying is the
same mechanism run once more. A placeholder whose side effect happens
to suppress the symptom is not a fix; nor is a reimplementation of
logic that already exists elsewhere — a second, weaker source of
truth. Where the copies genuinely cannot be collapsed in this
increment — a generated file per consumer, a shared library whose
source sits outside the audit — apply the fix at each site,
integrated into that member's local conventions (a sweep is not a
find-and-replace, and a mechanical substitution that breaks a
member's conventions is not a fix), then verify uniformity by
diff rather than assuming it, and file the collapse as its own
increment.
- Report the sweep: which members were searched, which were
affected, which were already clean, and where the fix now lives. A
sweep you cannot enumerate is a claim, not a result.
If the class is too large for this increment, fix the instance, name
the remaining members explicitly, and file them — but never leave the
sweep implicit, because a silent partial fix reads as a complete one.
This is not a licence to roam, and it does not compete with the scope
rule below. Scope restraint is about other problems: a defect you
noticed that has nothing to do with the request is filed, not fixed.
The class sweep is about this problem, in another file. A sibling
carrying the defect you were sent to fix is not unrelated code — it is
the same work, and the file boundary is not the shape of the bug. The
sweep is bounded by the defect's identity; the scope rule is bounded by
the defect's relevance. Both bound; neither licenses the other's
territory. The seam that should own the fix may live in a file the
request did not name; that file is the sweep's territory too — and, like
any other file outside the one you were given, it is listed before it is
touched (scope rule below).
Scope rule
Holistic is not unbounded. Stay within the file or module you were
given and the direct consequences of the request. Do not redesign
unrelated subsystems, swap libraries, or change public interfaces
other code depends on without flagging it first. "Integrate the code
you touch" and "do not chase unrelated code" are the same discipline
seen from two sides: coherence inside the unit of work, scope
restraint outside it. Unrelated issues you notice get filed as their
own increment, not silently fixed in this one — unrelated being the
operative word: another instance of the defect you were sent to fix is
the same issue, and belongs to the class sweep above, not here.
If proper integration requires touching other files, say so
explicitly and list them before doing it.
The append-only exception
Some artifacts are deliberately append-only, and holistic rewriting
would destroy their reason to exist. Do not apply this skill to:
- the plan-of-record's history/changelog (see
grill-planner — stale
claims are struck through, not deleted),
- Architecture Decision Records (see
adr-writer — superseded, never
edited in place),
- any changelog or audit log.
Those follow supersede-don't-delete. This skill governs code and
single-current-truth knowledge pages, where two copies of a fact is a
defect. Know which kind of file you are in before you start.
Self-check, run before you answer
- Did I read and account for the entire file, or only the region
near my edit?
- Is my diff purely additive? If yes, justify why nothing needed to
change or die — additive-only is a red flag, not a default.
- Does anything now exist in two places?
- Does the defect I just fixed exist in another place? If I did not
look, I do not know.
- Is any symbol still imported for a definition that has been commented
out or deleted? A dangling import is often the only trace of a
half-removed feature — when auditing for dead code, check type, enum,
and import references separately from executable call sites, because
the call sites can all be gone while the import quietly survives.
- Do all names, comments, and docs still tell the truth?
- Could a reader tell where the patch was stitched in? (Goal: no.)
Output format
When you deliver a change under this discipline:
- Read — 2–4 sentences: the file's purpose and relevant
structure.
- Integration plan — what changes, what moves, what dies, and
why.
- Full revised code — the whole unit, not a fragment.
- Changelog — a bullet list that includes anything you removed
or restructured beyond the literal request, so it can be vetoed.
The changelog is not decoration. Deletion and restructuring are the
parts most likely to surprise, so they are the parts you surface
loudest.
When this does NOT apply
Genuinely trivial changes — a typo, a comment, a lint fix, a
single-line config value — take the trivial-change shortcut. Do not
stage a four-part integration report for a one-character fix. The test
is the unit of work, not the size of the request: "fix this typo"
is trivial; "fix this bug" almost never is, because the bug usually
lives in an abstraction, not at the call site.
A rename is the sharp exception, and it fails the trivial test the
moment the identifier crosses a serialization, wire, or process
boundary — a persisted entity or DTO field, an enum constant an
external party reads, an auth-token claim name, an RPC or HTTP path, a
message-queue routing key, a service-discovery name. Each of those is
an unversioned contract: the diff looks like a one-line rename, but
some other process, stored record, or in-flight message still speaks
the old name, and nothing fails at compile time. "Looks like a
one-liner" is exactly the failure mode that silently breaks contracts
in service-oriented or serialized-data systems — so treat such a rename
as a contract change (versioned, migrated, or dual-read), never as a
trivial edit.
Reference files
- the kernel (
AGENTS.md) — the boundary that makes this binding.
docs/graph/agents/02-implementer.md — writes code under this rule.
docs/graph/agents/03-reviewer.md — audits for the forbidden moves.
docs/graph/skills/context-router.md — how to comprehend a file's owning
conventions before editing.
docs/graph/protocols/test-first.md — the characterization test that makes
restructuring existing code safe.
1---2name: holistic-editing3description: The discipline for any change, fix, refactor, or review whose unit of work is larger than a trivial one-liner. Use whenever you edit existing code or a knowledge page — the unit of work is the whole file or module, never the smallest diff that satisfies the request. A change is complete only when the file reads as if the requirement had existed from the beginning: no bolted-on functions, no _v2 wrappers, no special-case branches around logic that should itself change, no now-dead code left "to be safe." Coherence outranks minimal diffs. Load this before editing a file of any substance; it governs how implementer, reviewer, and every specialist touch existing files.4---56# holistic-editing78You are a senior engineer performing an **integration, not a patch**.9When asked to change, fix, refactor, or review something, your unit of10work is the whole file or module — never the smallest diff that11satisfies the request.1213## Prime directive1415A change is only complete when the file reads as if the requirement16had existed from the beginning. If a reviewer could point to where17your change was bolted on, you have failed. Minimal diffs are not a18virtue here; **coherence is**.1920This does not license gold-plating. "Minimum" still governs *new21behavior* — you add no capability nobody asked for, and you do not22expand into unrelated code (that is scope creep; file it for its own23increment). But the code that delivers the behavior you *were* asked24for is integrated into the existing design, not stapled to its edge.2526## Mandatory process, in order, every time27281. **Comprehend first.** Before writing code, state briefly: the29 file's responsibilities, its main structures and abstractions, and30 its conventions (naming, error handling, patterns). If you can't,31 ask for the missing context or read it — never reconstruct a file32 from memory and guess. If the project keeps a knowledge graph, the33 owning conventions may live in a node, not in the file itself; load34 it via `context-router`.352. **Locate the change architecturally.** State where the change36 conceptually belongs — which abstraction should own it, and what37 surrounding code it affects.383. **Assess the ripple.** List everything the change invalidates,39 duplicates, or makes obsolete: helpers to merge, branches that go40 dead, names that no longer describe their contents, comments and41 docs that go stale, tests the change implies.424. **Integrate.** Rewrite the affected regions as a whole.43 Restructure, rename, merge, and delete as needed. **Deletion and44 consolidation are first-class outcomes, not side effects.**455. **Output the whole revised unit** — the full file, or full revised46 functions/sections when the file is very large. Never a fragment47 like "add this after line 42."4849## Forbidden moves5051- Appending new functions at the bottom because it's the path of52 least resistance.53- Wrapper functions, `handleXNew`, `_v2`, `Improved`, `Enhanced`54 suffixes, or boolean flags that route around old behavior instead of55 replacing it.56- Special-casing: adding an `if` for the new requirement while the57 general logic stays untouched, when the general logic itself should58 change.59- Leaving now-redundant code, dead branches, or duplicated logic in60 place "to be safe."61- Fixing the symptom at the call site when the defect lives in the62 abstraction.63- Preserving a bad structure just because the request didn't name it.64 If honoring the request properly requires restructuring, restructure65 — and say you did.66- Fixing the instance you were handed while known siblings keep the67 same defect, and calling the change complete — or fixing all of them68 by pasting the same edit into every copy when the copies could have69 been collapsed into one.7071## The class sweep7273Some defects are not one defect. The same wrong path in six pipelines,74the same unguarded call in four adapters, the same stale constant in75every copy of a generated file — that is **one defect with six76locations**, and the location you were handed is not privileged.7778When the thing you are fixing has siblings, the discipline has two79halves — *find every copy*, then *land the fix once*:80811. **Establish the class before you fix the instance.** What is the82 defect, stated so a search can find it? Search for it.832. **Land the fix once, at the seam that owns it.** The sweep found84 *n* copies; the fix does not become *n* edits. Declare the intended85 behaviour at the single seam that owns it and collapse the duplicate86 implementations into one shared module every member invokes — the87 copy is the mechanism that produced the drift, and re-copying is the88 same mechanism run once more. A placeholder whose side effect happens89 to suppress the symptom is not a fix; nor is a reimplementation of90 logic that already exists elsewhere — a second, weaker source of91 truth. Where the copies genuinely cannot be collapsed in this92 increment — a generated file per consumer, a shared library whose93 source sits outside the audit — apply the fix at each site,94 integrated into that member's local conventions (a sweep is not a95 find-and-replace, and a mechanical substitution that breaks a96 member's conventions is not a fix), then **verify uniformity by97 diff** rather than assuming it, and file the collapse as its own98 increment.993. **Report the sweep**: which members were searched, which were100 affected, which were already clean, and where the fix now lives. A101 sweep you cannot enumerate is a claim, not a result.102103If the class is too large for this increment, fix the instance, name104the remaining members explicitly, and file them — but never leave the105sweep *implicit*, because a silent partial fix reads as a complete one.106107**This is not a licence to roam, and it does not compete with the scope108rule below.** Scope restraint is about *other problems*: a defect you109noticed that has nothing to do with the request is filed, not fixed.110The class sweep is about *this problem, in another file*. A sibling111carrying the defect you were sent to fix is not unrelated code — it is112the same work, and the file boundary is not the shape of the bug. The113sweep is bounded by the defect's identity; the scope rule is bounded by114the defect's relevance. Both bound; neither licenses the other's115territory. The seam that should own the fix may live in a file the116request did not name; that file is the sweep's territory too — and, like117any other file outside the one you were given, it is listed before it is118touched (scope rule below).119120## Scope rule121122Holistic is not unbounded. Stay within the file or module you were123given and the **direct consequences** of the request. Do not redesign124unrelated subsystems, swap libraries, or change public interfaces125other code depends on without flagging it first. "Integrate the code126you touch" and "do not chase unrelated code" are the same discipline127seen from two sides: coherence *inside* the unit of work, scope128restraint *outside* it. Unrelated issues you notice get filed as their129own increment, not silently fixed in this one — *unrelated* being the130operative word: another instance of the defect you were sent to fix is131the same issue, and belongs to the class sweep above, not here.132133If proper integration requires touching other files, **say so134explicitly and list them** before doing it.135136## The append-only exception137138Some artifacts are *deliberately* append-only, and holistic rewriting139would destroy their reason to exist. Do **not** apply this skill to:140141- the plan-of-record's history/changelog (see `grill-planner` — stale142 claims are struck through, not deleted),143- Architecture Decision Records (see `adr-writer` — superseded, never144 edited in place),145- any changelog or audit log.146147Those follow supersede-don't-delete. This skill governs code and148single-current-truth knowledge pages, where two copies of a fact is a149defect. Know which kind of file you are in before you start.150151## Self-check, run before you answer152153- Did I read and account for the **entire** file, or only the region154 near my edit?155- Is my diff purely additive? If yes, justify why nothing needed to156 change or die — additive-only is a red flag, not a default.157- Does anything now exist in **two places**?158- Does the defect I just fixed exist in **another place**? If I did not159 look, I do not know.160- Is any symbol still imported for a definition that has been commented161 out or deleted? A dangling import is often the only trace of a162 half-removed feature — when auditing for dead code, check type, enum,163 and import references separately from executable call sites, because164 the call sites can all be gone while the import quietly survives.165- Do all names, comments, and docs still tell the truth?166- Could a reader tell where the patch was stitched in? (Goal: no.)167168## Output format169170When you deliver a change under this discipline:1711721. **Read** — 2–4 sentences: the file's purpose and relevant173 structure.1742. **Integration plan** — what changes, what moves, what dies, and175 why.1763. **Full revised code** — the whole unit, not a fragment.1774. **Changelog** — a bullet list that *includes anything you removed178 or restructured beyond the literal request*, so it can be vetoed.179180The changelog is not decoration. Deletion and restructuring are the181parts most likely to surprise, so they are the parts you surface182loudest.183184## When this does NOT apply185186Genuinely trivial changes — a typo, a comment, a lint fix, a187single-line config value — take the trivial-change shortcut. Do not188stage a four-part integration report for a one-character fix. The test189is the *unit of work*, not the *size of the request*: "fix this typo"190is trivial; "fix this bug" almost never is, because the bug usually191lives in an abstraction, not at the call site.192193A rename is the sharp exception, and it fails the trivial test the194moment the identifier crosses a **serialization, wire, or process195boundary** — a persisted entity or DTO field, an enum constant an196external party reads, an auth-token claim name, an RPC or HTTP path, a197message-queue routing key, a service-discovery name. Each of those is198an **unversioned contract**: the diff looks like a one-line rename, but199some other process, stored record, or in-flight message still speaks200the old name, and nothing fails at compile time. "Looks like a201one-liner" is exactly the failure mode that silently breaks contracts202in service-oriented or serialized-data systems — so treat such a rename203as a contract change (versioned, migrated, or dual-read), never as a204trivial edit.205206## Reference files207208- the kernel (`AGENTS.md`) — the boundary that makes this binding.209- `docs/graph/agents/02-implementer.md` — writes code under this rule.210- `docs/graph/agents/03-reviewer.md` — audits for the forbidden moves.211- `docs/graph/skills/context-router.md` — how to comprehend a file's owning212 conventions before editing.213- `docs/graph/protocols/test-first.md` — the characterization test that makes214 restructuring existing code safe.