Coding
Discover before assuming. Verify before shipping. Every coding failure traces to acting on assumptions instead of
evidence, declaring done without verification, or burning context on noise.
Core Loop
Discover → Plan → Implement → Verify
- Discover — read the code, trace dependencies, understand what exists.
- Plan — define success criteria, scope the change, order the work.
- Implement — write minimal code, one change at a time, commit each.
- Verify — run the checks, confirm the requirement, show the evidence.
The threshold: if you can describe the diff in one sentence, skip planning. Otherwise, plan first.
The Assumption Interrupt
Apply this silently — don't narrate the check.
- Using a method, type, or interface without having read its definition
- Recalling an API from memory instead of reading current source
- Planning changes to code you haven't read in this session
- Assuming a method signature, type structure, or interface shape
When you catch one, read the source before continuing.
Discovery Protocol
Read the Scars
Intent leaves no trace in code, but change does. Each shape below is a question, never a conclusion — the provenance
rule binds here, so nothing read off code shape is a fact until a witness outside the code confirms it.
- An exported symbol nobody calls — possibly a fossil of whatever replaced it. Look for the successor.
- Armor bolted on — a guard, a retry, a sanitizing pass, a defensive copy around code that would not obviously need
one. Something may have failed here.
- A hack where a clean path exists — serializing an object to text and parsing it back where a copy would do. Ask
what blocked the clean path.
- Redundant repairs — two layers fixing the same thing. One of them may be dead.
Chase each with git history, the tracker, or the person who owns the code. A confirmed answer narrows what your change
must not break; an unconfirmed one stays a question and constrains nothing.
Planning Discipline
Success criteria — restate the task as something you can check. "Add validation" becomes "invalid inputs have
tests, and those tests pass." "Fix the bug" becomes "a test reproduces it, and that test passes." A criterion you
cannot run is not a criterion, and it is what forces you back to the user mid-task.
Scope — what files change, what stays untouched. Bound it explicitly. Don't "helpfully improve" adjacent code.
Decomposition — before writing any code, write the ordered list of changes you will make, one commit each. This
is where the work gets split. Splitting at commit time means untangling a diff that was never built to come apart,
and that rarely succeeds — the pieces are already interleaved across the same files and lines.
Name each entry by its kind of work, because a change carries only one kind: refactor, new code, integration, fix,
formatting, docs. Two sources feed the list:
- The task statement, before you read any code. "Implement and integrate X" is already two entries: the new code
standalone, then the wiring that puts it to use.
- Discovery, once you have read the code. If the existing code needs a refactor to make room, that refactor is
entry one, committed on its own, before any new code exists.
A dummy layer that integrates end to end and does nothing is usually entry one or two; after it, one capability at a
time in dependency order — storage standalone and verified, then the handler that calls it, then the surface above.
Then work the list one entry at a time. Never write code for the next entry until the current one is committed. That
rule is what makes the plan binding — without it the list is a note you abandon at the first opportunity.
Risks — what could break. If modifying shared code, trace all callers first.
Implementation Discipline
One Change at a Time
An uncommitted working tree is the unit nobody can review and nobody can bisect. Commit at each verified step during the
work, not once at the end. A large change is not one hard review — it is a review nobody does, and the defect that
survives it costs more than the whole change saved.
Atomic is a code boundary, not a task boundary. An atomic change is the smallest change that leaves the tree
working: it builds, its tests pass, nothing is half-wired. It is not the smallest change that satisfies the request. One
task is normally many atomic changes.
The second test catches what the first cannot see. A refactor and the feature it enables build and pass together, so
deliverability alone keeps them in one diff — and the reviewer then separates "what moved" from "what is new" by hand,
which is the review that silently does not happen. At equal correctness, take the finer split.
Both tests belong to planning. They decide the change list before code exists; here they only confirm the list held.
Applying them for the first time to a finished diff is already the failure. If it happens, say so, salvage what
separates cleanly, and start the next task from a change list.
Three things trigger a commit. Any one is enough, and they are not a sequence — most commits fire on the first.
Four rules qualify all three:
The git-commit plugin's commit skill carries the pipeline for each of these — unit boundaries, message, validation.
Stay Inside the Change
- Every changed line traces to the request. Run that test over the finished diff; a line that traces to nothing is
scope creep that got in while you were somewhere else.
- Clean up the orphans your change created — an import, a variable, a helper that your edit made unused.
Pre-existing dead code is not yours to delete: mention it and leave it.
- Wear one hat at a time. Refactor first with behavior identical, verify, then change behavior. A diff that does
both can't be reviewed or bisected.
Write Simple Code
Agents overcomplicate by default. Before writing new code, climb the reuse ladder and stop at the first rung that holds.
The ladder runs after you understand the problem — read the code the change touches and trace the real flow first,
then climb. It shortens the solution, never the reading: a small diff you don't understand is laziness dressed up as
efficiency.
Two rungs hold → take the higher one and move on.
Follow Existing Patterns
Search the codebase for an existing pattern before inventing one. Match the error handling, the testing patterns in
adjacent tests, and the naming conventions. Read CLAUDE.md and the lint config for project-specific rules. When two
patterns contradict, pick one (more recent, more tested), explain the choice, and flag the other for cleanup — never
blend them into an average. If you think an existing convention is harmful, surface it; don't fork it silently.
Read references/patterns.md when writing error paths, introducing a seam for testability, reshaping a function whose
body reads worse than it works, or improving existing code rather than changing its behavior.
Naming
A name is read at every use site; a comment is read once — so a vague name is the defect that forces the explanation
above it. Read references/naming.md when naming a new symbol, when writing a diagnostic a user will read, when a
comment's payload would fit in an identifier, or when a name in code you touch reads two ways. It carries the naming
rules and the limits on renaming what already exists.
Comments
The default is zero comments, and it is not a judgment call. Five kinds may exist and nothing outside the list does: a
doc comment on a public symbol, the justification on an escape hatch, shortcut:, constraint:, why?:. There is no
sixth kind and no "this one is genuinely useful" exception.
Before writing a comment, climb the routing ladder and stop at the first rung that holds: a better name, a test, a doc
comment, a rule in the project's rule document, a design note in the architecture doc, one of the five markers, or drown
it. Drown is the default verdict and it is silent.
Read references/comments.md before writing, repairing, or reviewing any comment or doc comment. It carries the full
closed set, the marker grammar, the provenance rule for a WHY, the doc-comment contract, and the repair scope.
Debugging
Read references/debugging.md when a test fails, a bug is reported, or behavior surprises you — before proposing a fix.
It carries the ordered protocol, the multi-component localization step, and the counter that turns a third failed fix
into a design question rather than a fourth fix.
Verification Discipline
Verification is the highest-leverage activity in the loop. Code that "looks right" but hasn't been run is unverified.
Never Silence the Signal
A failing check is information about the code, not an obstacle to green. Make checks pass by fixing the code — never by
weakening the check.
Before Declaring Done
- Run the tests. If none cover the code you changed, write them. If you modified existing behavior, run the full
relevant suite, not just the new tests.
- Check both axes. Spec: does it do what was asked — the success criteria from planning — not merely something
plausible? Standards: does it follow this repo's conventions (CLAUDE.md, lint and type config, ADRs)? Check them
separately: code can satisfy every convention and implement the wrong thing, and a correct result can still violate
the conventions.
- Review your own diff as if it were someone else's. Leftover debug code, comments outside the five kinds, docs
describing the old contract, missing error handling, hardcoded values, uncovered edge cases, dead code from earlier
attempts.
- Type-check and lint. Don't ship with known warnings.
- Disclose gaps. "Done" means fully verified. "Done, but I didn't verify X" beats a silent gap.
Evidence, Not Assertion
Every claim below needs its own proof. The third column is what people submit instead.
| Claim |
Proof |
Not proof |
| Tests pass |
this run's summary line and exit code |
a previous run, "should pass" |
| Lint or types clean |
the tool's own exit code |
the tests passing |
| Build succeeds |
the build's exit code |
lint passing |
| Bug fixed |
a test of the original symptom, passing |
the code changed |
| Regression test works |
it went red with the fix reverted, green with it in |
it passes once |
| Nothing calls this |
the search command and its output, over the whole tree |
a grep in one directory |
| A subagent finished |
the diff it left behind |
its report |
The last two are the ones most often asserted and least often checked. An absence you cannot show a sweep for is an
absence you do not claim, and an agent's success report is a claim like any other.
Self-Verification Patterns
- Write a failing test first, then implement until it passes.
- A test must be able to fail. One that cannot fail when the business logic changes is testing nothing.
- Use subagents for fresh-context review — they catch mistakes you'll miss in the context where you wrote the code.
- For UI changes, exercise the feature in a browser. Type checks and test suites verify code correctness, not feature
correctness; if you can't test it, say so rather than claiming success.
- For refactors, verify identical behavior before and after.
Context Management
- Don't pre-load files "just in case" — retrieve context when the need for it arrives, not before.
- After two failed corrections on the same issue, start fresh rather than accumulating failed approaches in context.
- If you're losing track of what you've tried, say so. Silent degradation wastes more time than admitting it.
Application
Apply these disciplines silently. Don't narrate the protocol, announce which step you are on, or report a check that
found nothing.
When writing code: run discovery, work the change list, commit as each trigger fires, verify before reporting.
When reviewing code: cite the specific violation with file:line and show the fix. Don't lecture.
If the codebase contradicts a rule here, follow the codebase and flag the divergence once. The comment default is the
exception: a commented-out-everything codebase is a convention you neither match nor sweep. Match the code's patterns,
not its prose.
Integration
This skill runs before the language skill and again before you declare done. It governs the shape of the work — what to
read, how to split it, when to commit, what may be a comment, what proves completion. The language skill governs syntax,
idiom, toolchain, and which symbols need a doc, and it wins on anything language-specific.
Return to the verification protocol above before declaring any task complete.
1---2name: coding3description: Language-agnostic coding workflow: discovery, change decomposition, commit sizing, comment and naming policy, and completion evidence.4---56# Coding78**Discover before assuming. Verify before shipping.** Every coding failure traces to acting on assumptions instead of9evidence, declaring done without verification, or burning context on noise.1011## Core Loop1213```14Discover → Plan → Implement → Verify15```1617- **Discover** — read the code, trace dependencies, understand what exists.18- **Plan** — define success criteria, scope the change, order the work.19- **Implement** — write minimal code, one change at a time, commit each.20- **Verify** — run the checks, confirm the requirement, show the evidence.2122**The threshold**: if you can describe the diff in one sentence, skip planning. Otherwise, plan first.2324## The Assumption Interrupt2526Apply this silently — don't narrate the check.2728<assumption-rule>29Never build on a contract you haven't read in this session. Each of these is an unverified30assumption, and every unverified assumption is a potential compile failure, runtime bug, or31behavioral regression:3233- Using a method, type, or interface without having read its definition34- Recalling an API from memory instead of reading current source35- Planning changes to code you haven't read in this session36- Assuming a method signature, type structure, or interface shape3738When you catch one, read the source before continuing. </assumption-rule>3940<assumption-markers>41These words in your reasoning are the tell:42- "probably" → you don't know. Read it.43- "likely" → you're guessing. Check it.44- "should have" → assumption. Verify it.45- "typically" → general knowledge, not this codebase. Read it.46- "I remember" → memory is unreliable. Read it now.47- "usually" → this codebase may differ. Check it.48</assumption-markers>4950## Discovery Protocol5152<discovery-protocol>53- **Map the area** — read the files in the target directory and learn the module structure.54- **Trace dependencies** — what this code imports, what imports it. Grep the function and type names for usages.55- **Verify contracts** — read the actual signatures, interfaces, and type definitions. For third-party code, read the56 vendored source or fetch the docs. Confirm the function exists, the signature matches, the types line up.57- **Assess impact** — who calls what you are modifying, what tests cover it, what breaks.58</discovery-protocol>5960### Read the Scars6162Intent leaves no trace in code, but change does. Each shape below is a question, never a conclusion — the provenance63rule binds here, so nothing read off code shape is a fact until a witness outside the code confirms it.6465- **An exported symbol nobody calls** — possibly a fossil of whatever replaced it. Look for the successor.66- **Armor bolted on** — a guard, a retry, a sanitizing pass, a defensive copy around code that would not obviously need67 one. Something may have failed here.68- **A hack where a clean path exists** — serializing an object to text and parsing it back where a copy would do. Ask69 what blocked the clean path.70- **Redundant repairs** — two layers fixing the same thing. One of them may be dead.7172Chase each with git history, the tracker, or the person who owns the code. A confirmed answer narrows what your change73must not break; an unconfirmed one stays a question and constrains nothing.7475## Planning Discipline7677<planning-checklist>7879- **Success criteria** — restate the task as something you can check. "Add validation" becomes "invalid inputs have80 tests, and those tests pass." "Fix the bug" becomes "a test reproduces it, and that test passes." A criterion you81 cannot run is not a criterion, and it is what forces you back to the user mid-task.8283- **Scope** — what files change, what stays untouched. Bound it explicitly. Don't "helpfully improve" adjacent code.8485- **Decomposition** — before writing any code, write the ordered list of changes you will make, one commit each. **This86 is where the work gets split.** Splitting at commit time means untangling a diff that was never built to come apart,87 and that rarely succeeds — the pieces are already interleaved across the same files and lines.8889 Name each entry by its kind of work, because a change carries only one kind: refactor, new code, integration, fix,90 formatting, docs. Two sources feed the list:91 - **The task statement**, before you read any code. "Implement and integrate X" is already two entries: the new code92 standalone, then the wiring that puts it to use.93 - **Discovery**, once you have read the code. If the existing code needs a refactor to make room, that refactor is94 entry one, committed on its own, before any new code exists.9596 A dummy layer that integrates end to end and does nothing is usually entry one or two; after it, one capability at a97 time in dependency order — storage standalone and verified, then the handler that calls it, then the surface above.9899 Then work the list one entry at a time. Never write code for the next entry until the current one is committed. That100 rule is what makes the plan binding — without it the list is a note you abandon at the first opportunity.101102- **Risks** — what could break. If modifying shared code, trace all callers first.103104</planning-checklist>105106## Implementation Discipline107108### One Change at a Time109110An uncommitted working tree is the unit nobody can review and nobody can bisect. Commit at each verified step during the111work, not once at the end. A large change is not one hard review — it is a review nobody does, and the defect that112survives it costs more than the whole change saved.113114**Atomic is a code boundary, not a task boundary.** An atomic change is the smallest change that leaves the tree115working: it builds, its tests pass, nothing is half-wired. It is not the smallest change that satisfies the request. One116task is normally many atomic changes.117118<atomicity-tests>119- **Deliverable alone** — cut it in two: does each piece build and pass? If yes, it was not one change. Cut it, commit120 the first piece, and repeat until the answer is no. "The whole task is one logical change" is not the test — it is the121 reasoning that produces a 3000-line diff.122- **One kind of work** — does the diff carry implementation and refactoring? Formatting and logic? New code and the123 wiring that integrates it? If yes, it was not one change, even when every line serves the same feature.124</atomicity-tests>125126The second test catches what the first cannot see. A refactor and the feature it enables build and pass together, so127deliverability alone keeps them in one diff — and the reviewer then separates "what moved" from "what is new" by hand,128which is the review that silently does not happen. At equal correctness, take the finer split.129130**Both tests belong to planning.** They decide the change list before code exists; here they only confirm the list held.131Applying them for the first time to a finished diff is already the failure. If it happens, say so, salvage what132separates cleanly, and start the next task from a change list.133134Three things trigger a commit. Any one is enough, and they are not a sequence — most commits fire on the first.135136<commit-triggers>137- **The entry is finished.** Whatever the change-list entry set out to do now works and its tests pass. Commit it at138 whatever size that turned out to be: completion is the trigger, not volume, and a 30-line commit is finished work139 rather than an under-delivery. The next entry is its own commit even when it touches the same files — the140 implementation lands, then the integration that wires it up lands separately.141- **The next step would cross the size checkpoint** of ~400-500 lines of production code. Commit before you start it,142 not after you cross it. A boundary chosen in advance is one you control; a boundary found afterwards leaves a143 finished diff to take apart, and that split is always worse than the one you would have planned.144- **You crossed the size checkpoint anyway.** Stop, commit what is coherent, and continue on top of it.145</commit-triggers>146147Four rules qualify all three:148149<commit-constraints>150- **Tests don't count toward the size checkpoint.** They ship in the commit with the code they cover, however long they151 run. A small feature with a wide test surface is one legitimate commit of a few thousand lines.152- **Atomicity outranks the size checkpoint only after the split test fails.** A mechanical rename across 60 files, or a153 signature change with all its callers, cannot be cut without leaving a broken tree, so it ships whole at any size. Run154 the test before invoking this.155- **Never start a second change on an uncommitted first.** Both then land as one blob, and neither can be reverted156 alone.157- **A phase is not a commit.** A planned phase usually lands as one to three commits. Size the commit by these triggers,158 never by the phase boundary.159</commit-constraints>160161The `git-commit` plugin's `commit` skill carries the pipeline for each of these — unit boundaries, message, validation.162163### Stay Inside the Change164165- **Every changed line traces to the request.** Run that test over the finished diff; a line that traces to nothing is166 scope creep that got in while you were somewhere else.167- **Clean up the orphans your change created** — an import, a variable, a helper that your edit made unused.168 Pre-existing dead code is not yours to delete: mention it and leave it.169- **Wear one hat at a time.** Refactor first with behavior identical, verify, then change behavior. A diff that does170 both can't be reviewed or bisected.171172### Write Simple Code173174Agents overcomplicate by default. Before writing new code, climb the reuse ladder and stop at the first rung that holds.175The ladder runs _after_ you understand the problem — read the code the change touches and trace the real flow first,176then climb. It shortens the solution, never the reading: a small diff you don't understand is laziness dressed up as177efficiency.178179<reuse-ladder>1801. **Does this need to exist?** Speculative need → skip it, say so in one line. (YAGNI)1812. **Already in this codebase?** A helper, util, type, or pattern that already lives here → reuse it. Re-implementing182 what's a few files over is the most common waste.1833. **Stdlib does it?** Use it.1844. **Native platform feature covers it?** `<input type="date">` over a picker lib, CSS over JS, a DB constraint over185 app-level code.1865. **An already-installed dependency solves it?** Use it. Never add a new dependency for what a few lines or an existing187 one can do.1886. **Can it be one line?** One line.1897. **Only then** write the minimum code that works.190191Two rungs hold → take the higher one and move on. </reuse-ladder>192193<simplicity-rules>194- Prefer functions over classes when either works; avoid inheritance unless the problem demands it195- Prefer explicit over implicit — no magic. Keep permission checks and validation visible at the call196 site, not hidden in middleware the next reader won't find197- Prefer deep modules — a small interface hiding substantial implementation — over shallow ones that198 expose nearly as much as they hide. A wrapper that only forwards calls earns nothing199- Don't introduce a seam (interface, port, strategy) until two concrete implementations need it —200 typically production plus test. One implementation behind an interface is indirection, not abstraction201- Deletion test before adding an abstraction: imagine the module gone. If its complexity reappears in202 every caller, it earns its place. If the complexity merely moves, it was a shallow pass-through203</simplicity-rules>204205### Follow Existing Patterns206207Search the codebase for an existing pattern before inventing one. Match the error handling, the testing patterns in208adjacent tests, and the naming conventions. Read CLAUDE.md and the lint config for project-specific rules. When two209patterns contradict, pick one (more recent, more tested), explain the choice, and flag the other for cleanup — never210blend them into an average. If you think an existing convention is harmful, surface it; don't fork it silently.211212Read `references/patterns.md` when writing error paths, introducing a seam for testability, reshaping a function whose213body reads worse than it works, or improving existing code rather than changing its behavior.214215## Naming216217A name is read at every use site; a comment is read once — so a vague name is the defect that forces the explanation218above it. Read `references/naming.md` when naming a new symbol, when writing a diagnostic a user will read, when a219comment's payload would fit in an identifier, or when a name in code you touch reads two ways. It carries the naming220rules and the limits on renaming what already exists.221222## Comments223224The default is zero comments, and it is not a judgment call. Five kinds may exist and nothing outside the list does: a225doc comment on a public symbol, the justification on an escape hatch, `shortcut:`, `constraint:`, `why?:`. There is no226sixth kind and no "this one is genuinely useful" exception.227228Before writing a comment, climb the routing ladder and stop at the first rung that holds: a better name, a test, a doc229comment, a rule in the project's rule document, a design note in the architecture doc, one of the five markers, or drown230it. Drown is the default verdict and it is silent.231232Read `references/comments.md` before writing, repairing, or reviewing any comment or doc comment. It carries the full233closed set, the marker grammar, the provenance rule for a WHY, the doc-comment contract, and the repair scope.234235## Debugging236237Read `references/debugging.md` when a test fails, a bug is reported, or behavior surprises you — before proposing a fix.238It carries the ordered protocol, the multi-component localization step, and the counter that turns a third failed fix239into a design question rather than a fourth fix.240241## Verification Discipline242243Verification is the highest-leverage activity in the loop. Code that "looks right" but hasn't been run is unverified.244245### Never Silence the Signal246247A failing check is information about the code, not an obstacle to green. Make checks pass by fixing the code — never by248weakening the check.249250<signal-rules>251- Never delete, skip, or comment out a failing test to get green252- Never loosen an assertion until it passes — that asserts the bug, not the behavior253- Never add a lint or type suppression (`eslint-disable`, `# type: ignore`, `as any`) to silence an254 error you haven't understood255- Never wrap failing code in catch-and-ignore or a silent fallback — an error that vanishes is a256 bug that relocated257- The one legitimate case: the check itself is wrong, asserting old behavior the task explicitly258 changes. Prove it, say so, then change the check visibly — never as a side effect259</signal-rules>260261### Before Declaring Done2622631. **Run the tests.** If none cover the code you changed, write them. If you modified existing behavior, run the full264 relevant suite, not just the new tests.2652. **Check both axes.** _Spec:_ does it do what was asked — the success criteria from planning — not merely something266 plausible? _Standards:_ does it follow this repo's conventions (CLAUDE.md, lint and type config, ADRs)? Check them267 separately: code can satisfy every convention and implement the wrong thing, and a correct result can still violate268 the conventions.2693. **Review your own diff** as if it were someone else's. Leftover debug code, comments outside the five kinds, docs270 describing the old contract, missing error handling, hardcoded values, uncovered edge cases, dead code from earlier271 attempts.2724. **Type-check and lint.** Don't ship with known warnings.2735. **Disclose gaps.** "Done" means fully verified. "Done, but I didn't verify X" beats a silent gap.274275### Evidence, Not Assertion276277Every claim below needs its own proof. The third column is what people submit instead.278279| Claim | Proof | Not proof |280| --------------------- | ------------------------------------------------------ | ----------------------------- |281| Tests pass | this run's summary line and exit code | a previous run, "should pass" |282| Lint or types clean | the tool's own exit code | the tests passing |283| Build succeeds | the build's exit code | lint passing |284| Bug fixed | a test of the original symptom, passing | the code changed |285| Regression test works | it went red with the fix reverted, green with it in | it passes once |286| Nothing calls this | the search command and its output, over the whole tree | a grep in one directory |287| A subagent finished | the diff it left behind | its report |288289The last two are the ones most often asserted and least often checked. An absence you cannot show a sweep for is an290absence you do not claim, and an agent's success report is a claim like any other.291292### Self-Verification Patterns293294- Write a failing test first, then implement until it passes.295- A test must be able to fail. One that cannot fail when the business logic changes is testing nothing.296- Use subagents for fresh-context review — they catch mistakes you'll miss in the context where you wrote the code.297- For UI changes, exercise the feature in a browser. Type checks and test suites verify code correctness, not feature298 correctness; if you can't test it, say so rather than claiming success.299- For refactors, verify identical behavior before and after.300301## Context Management302303- Don't pre-load files "just in case" — retrieve context when the need for it arrives, not before.304- After two failed corrections on the same issue, start fresh rather than accumulating failed approaches in context.305- If you're losing track of what you've tried, say so. Silent degradation wastes more time than admitting it.306307## Application308309Apply these disciplines silently. Don't narrate the protocol, announce which step you are on, or report a check that310found nothing.311312When **writing** code: run discovery, work the change list, commit as each trigger fires, verify before reporting.313314When **reviewing** code: cite the specific violation with `file:line` and show the fix. Don't lecture.315316If the codebase contradicts a rule here, follow the codebase and flag the divergence once. **The comment default is the317exception**: a commented-out-everything codebase is a convention you neither match nor sweep. Match the code's patterns,318not its prose.319320## Integration321322This skill runs before the language skill and again before you declare done. It governs the shape of the work — what to323read, how to split it, when to commit, what may be a comment, what proves completion. The language skill governs syntax,324idiom, toolchain, and which symbols need a doc, and it wins on anything language-specific.325326Return to the verification protocol above before declaring any task complete.