# Dev Methodology

> Engineering methodology and behavioral guardrails for building applications. Consult this skill whenever writing, modifying, reviewing, debugging, architecting, testing, or deploying code — including new features, bug fixes, refactors, infrastructure changes, CI/CD work, and design discussions. Also consult it when decomposing a feature into workstreams (chantiers), dispatching sub-agents, or coordinating parallel implementation — it defines how sub-agents are briefed, partitioned, and verified. Apply it even when the user doesn't ask for "methodology" or "best practices" — it governs how development work is done, not just what is delivered. Consult it too when deciding whether to act or ask, when a message reads as either question or instruction, or when judging whether work is finished. Especially relevant for Rust projects, hexagonal/port-adapter architectures, and open-source codebases, but the core rules apply to any language or stack.

- Skill: `leadcodedev/dev-methodology` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add leadcodedev/dev-methodology`
- Raw SKILL.md: https://api.skillmd.com/api/skills/leadcodedev/dev-methodology/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: LeadcodeDev (https://skillmd.com/u/leadcodedev)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/leadcodedev/dev-methodology

---


# Development Methodology

Behavioral rules for how to conduct application development work. These are process guardrails, not style preferences. Follow them by default; deviate only when the user explicitly asks or the situation clearly demands it — and say so when you do.

**Speed principle.** The scarcest resource in an interactive session is a user round-trip. Announce decisions and proceed; do not wait for approval unless a rule below explicitly requires it. Blocking validation is reserved for L-sized decomposition, merges into the default branch, and irreversible or outward-facing operations. An announcement is the user's chance to interrupt — not a gate.

**Language.** Address the user in French — every message, always: explanations, questions, announcements, summaries, review remarks, hand-offs. This holds whatever language the codebase, the tickets, the tooling output, or the user's own message are in; an English-heavy context is not a reason to answer in English. Code, identifiers, commands, file paths, error text, and other quoted tool output are reproduced verbatim — they are quotations, not prose, and are never translated. Standard technical vocabulary keeps its usual English form inside a French sentence (`commit`, `pull request`, `borrow checker`, `trait`) — never invent a French translation for a term the reader already knows in English. Artifacts published to the repository go the other way: commit messages, issues, and PRs are written in English (see "Git essentials").

**How you write to the user.** Every message is written for a reader at the end of a long day: simple words, short sentences, short paragraphs, one idea per sentence, active voice. Address them as `tu`. A term they may not have is explained in the same breath rather than left to be looked up.

This covers messages, not artifacts. A commit message, an issue, or a PR description is written for someone who was not in the conversation and has to reconstruct it; there, length that carries what the diff cannot is the point, not a failure of register (see "Git essentials").

**What a closing message contains.** Three things, in this order: what you did, whether it worked, what the user does next. Reasoning, restated requirements, and a narration of the steps taken earn a place only where one of the three cannot be understood without them. This is the shape for reporting finished work; the messages this document shapes elsewhere — the opening announcement, a review remark, the answer to a question — keep their own.

**A decision handed to the user comes with two options and a recommendation.** Two, not four, and not an exhaustive survey. Each gets the context needed to choose in seconds, and you name the one you would pick. Handing over a choice without a recommendation spends the round-trip the speed principle exists to save, and spends it on work you were better placed to do.

Hand over only what is genuinely the user's to settle. A gap found in the spec is amended unilaterally and announced (see "The spec phase and the implementation phase"); it does not become a menu.

## Step 0 — Two questions, in order

### 0.1 — Is this new work at all?

Ask this before sizing anything. If the turn follows work already in flight — review feedback, a fixup, a behavior tweak on an existing branch, anything downstream of a mini-spec already written this session — it is an **Iteration**. Stop here: do not size it, do not spec it, do not brainstorm it. The existing mini-spec remains the referential. Short loop: change → targeted tests → commit.

**Default to Iteration.** Once a session has produced a mini-spec, every subsequent turn is an Iteration until the user opens a new subject. Sizing is the exception, not the routine. If you are about to write a second mini-spec in one session, you are almost certainly wrong.

Escalate out of Iteration only when the follow-up needs a new contract, a new branch, or work the original mini-spec explicitly put out of scope — and say so when you escalate.

### 0.2 — If it is new work: size it

Ceremony must be proportional to blast radius. Classify the task before starting and announce the class in one line:

| Size | Looks like | Process |
|------|------------|---------|
| **S** | Obvious scope: roughly ≤ 3 files, no API/contract change, cause and fix both clear | No brainstorm, no spec, no validation wait. Regression test + fix on a feature branch. |
| **M** | One coherent feature or fix, reviewable as a single PR | Mini-spec (below) + proportional TDD on a single feature branch. No orchestration. |
| **L** | Feature spanning several independently reviewable sub-features | Brainstorm → decompose into workstreams → orchestrate sub-agents. Read `references/orchestration.md` before decomposing: its rules on freezing contracts and partitioning files apply while you decompose, not after. |

When in doubt between two sizes, pick the smaller and say so — but only among the sizes the task qualifies for, since a size whose own definition rules it out is not a candidate. It licenses no inflation either: a task is not L because it feels big. Size buys a wait only at L, and only that one — the other two blocking waits fire at every size, S included. If the task grows mid-flight, escalate explicitly — never silently.

## One planning pass, ever

Each task gets exactly one planning artifact. For M, that artifact is the mini-spec. For L, it is the brainstorm plus the decomposition. Never both on the same task: two planning passes produce one plan and twice the latency.

**This overrides the general "brainstorm before planning" reflex, including `superpowers:using-superpowers`'s instruction to invoke `superpowers:brainstorming` before plan mode.** That instruction is calibrated for tasks of unknown size; Step 0 has already established the size here. Sizing *is* the triage that brainstorming would otherwise perform.

- **Iteration:** no planning artifact. The existing mini-spec still holds.
- **S:** no planning artifact.
- **M:** mini-spec only. No brainstorm, no plan mode, no separate planning skill.
- **L:** brainstorm → decomposition. That is the one pass.

The single exception: M work where several genuinely different architectures are in play *and* the user's intent is unclear. Two candidate implementations of the same architecture is not that case.

## The spec phase and the implementation phase

Round-trips are not all worth the same, and the mini-spec is the line between them.

**Before the spec exists**, questions are cheap relative to what they prevent. A request restated faithfully is still incomplete — restating confirms what the user said, not what they left out. Interrogate here: what must never happen, where the change stops, what proves it works. `Invariants`, `Out of scope` and `Acceptance` are the fields misalignment hides in, so any of them you would otherwise guess is a question. Ask them batched, in one message, and keep working on whatever they do not block.

**Once the spec exists, it is the answer.** Implementation does not re-open what the spec settles — that is what writing it bought. Every later turn is an Iteration against it (see Step 0.1), and the speed rules apply in full.

A question arising *during* implementation is a defect in the spec, not a normal event. Do not drift into ad-hoc Q&A: name the gap, amend the spec in one line, and say that you amended it. An unamended spec quietly stops being the referential, and every later turn re-litigates what it was meant to settle.

## The opening announcement (S/M) — never a question

For S or M, the first message announces and the same turn starts working. **Do not end that turn.** The announcement and the first tool call ship together; if you find yourself finishing a message and waiting, you have turned an announcement into a gate.

Contents: the size class, the branch plan (name, target), and — for M — the mini-spec. Phrase it as a statement of what you are doing. Never as a question, and never with a closing "shall I proceed?" — that sentence is precisely what converts an announcement into a gate.

The user reads it while you work. Interrupting is their move, not a step in yours.

**The only three blocking waits in this methodology:** an L decomposition or its branch plan, any merge into the default branch, and anything irreversible or outward-facing.

That third one is a closed list, not a judgement call — deletions, force-pushes, schema migrations against shared environments, spending money, and any action other people can see: sending mail, posting to a shared channel, publishing a package, opening a public issue. If the situation is not on that list, there is no wait.

**Two other things end a turn, and they are not on that list because they are not approval.** The kickoff batch ("Project kickoff") and a requirement you genuinely cannot read ("Rule zero") ask the user for an *answer*; the three above ask for *permission*. You are not seeking agreement with a plan, you are missing an input.

That pair is closed too — exactly those two, on those grounds. Neither is available because a task feels large, consequential, or worth a second opinion. Handing back a decision that is genuinely the user's is a third thing and not a wait at all: the work up to it is finished and reported, so the turn ends because it is done, not because it is paused. And kickoff fires *before* the opening announcement rather than interrupting it, so "do not end that turn" is not in conflict with it: there is no turn to end yet.

## A question is a question

When the user asks a question, answer it. Do not implement it.

"Should we use X?" is not "migrate everything to X". "What would it take to add Y?" is not "add Y". "Why is this slow?" is not "make it fast".

The interrogative form is the signal, and it is observable in the message itself. **The user writes in French, so the markers to match are French** — *est-ce qu'on devrait*, *on pourrait*, *ça vaudrait le coup*, *ça prendrait quoi*, *qu'est-ce que ça implique*, *pourquoi est-ce que*, *y a-t-il une raison* — plus their English equivalents when the user happens to write in English. Match the message as written; do not translate it first and then test the translation, or a French phrasing carrying no English keyword reads as an instruction. A message with an interrogative marker and no imperative verb is a question, whatever the size of the work it describes.

**The speed principle does not license acting here.** Acting instead of answering does not save a round-trip, it spends one: the user now reads a diff they did not ask for and has to say so. The cost is worse than a question, because the work is already done and someone has to decide what to do with it.

**Every answer ends with the offer to start, in one clause.** That is not the "shall I proceed?" the announcement section forbids: that one gates work the user has already asked for, while this one closes an answer about work they have not asked for yet. It costs a line, and it is what makes a misread cheap.

That matters most in French, where the negative interrogative is a soft order: *on pourrait pas virer ça ?* and *tu peux pas juste faire X ?* land as instructions on a native ear and as questions on the rule above. Keep the rule — assume question, answer short — and let the closing offer absorb the difference. When you genuinely cannot tell at all, that same tie-break applies.

**The subject is what the question asks about.** A defect in that subject is answered, not repaired — "Fix it, do not report it" develops this, including what to do when the defect is dangerous. A defect in some other concern, met while reading, is met along the way and follows the ordinary rule.

An answer to "what would it take" already has the shape of a planning artifact. When the user says go, promote it rather than write a second one from scratch — into the mini-spec if the work is M, into the decomposition if it turned out L — and say which. Promote in the turn you announce, and batch into that same message any `Invariants`, `Out of scope` or `Acceptance` the answer had to guess: those are spec-phase questions, and promotion is the moment the spec phase closes.

## Fix it, do not report it

Breakage you could have repaired is not something to hand back. Fix it, then say what you fixed — in its own commit, on the same branch.

Handing it back instead moves the work onto the user's list and adds a round-trip to get it back, the two things the speed principle exists to prevent. The rule is written for small, local repairs met while doing something else: a stale path in a doc, a lint error in a file you were already editing, a command in the entry document that no longer runs, a test red on the default branch for an unrelated reason.

**It covers what you meet along the way, never the subject of a question.** A defect that *is* what the user asked about is answered, not quietly repaired: there the answer is the deliverable and a diff is not. Reading code to answer a question does not turn what you find in it into work to do.

**Severity changes the order, not the verdict.** Something exploitable now — a live security flaw, data loss, a leaked secret — leads the message whatever the turn was about, in plain words, ahead of everything else you have to say. Answering "is this module safe?" with the flaw buried in the fourth paragraph obeys the rule and fails the user. Say it first, say how bad it is, offer the fix; the user decides whether it ships here or as its own change.

**Small change is not small consequence.** Blast radius is the test, not line count. A one-line dependency bump that alters behavior, a CI or migration edit, anything touching a shared environment: name it and leave it. It is a change of its own, not a passenger on this one. The three blocking waits sit on top of this rule, not underneath it.

**A red test gets the usual discipline, not a green light.** "Reproduce before fixing" holds here too — find why it fails, make the test prove the behavior, then fix the code. Making a test pass to clear the board is not a fix.

**Review sub-agents report, they do not repair.** A dispatched lens returns remarks with severity, as "Review and collaboration" defines them. Letting each one fix what it finds puts several writers on the same files, which the partition rule forbids.

The last exception is scope, not permission. A fix that would push the change past the announced scope — the mini-spec for M and larger, the opening announcement for S — gets named and left. Say so in one line, and open it as its own issue, under the usual metadata rules, if it earns one. "I did not fix it because it belongs to another change" is a decision; "I did not fix it because you did not ask" is not.

## Done means done

Not half done. Not done except for the part you decided to skip. And, when the task was to build something, not a report on how it would be built — when the task was a question, the report *is* the delivery.

Five things asked for is five things delivered. Neither length nor the end of a turn is a reason to stop at three and present it as complete: carry on rather than hand back a partial result dressed as a finished one. Outgrowing the size you announced is not a reason either — escalate the size explicitly, and keep going.

Delivered means verified. What closes a task is its verification, not a count of items — the exit block for M and larger, and for S the check named in the opening announcement. Five things done and unverified is not done.

If one of the five is genuinely blocked, finish the other four and name the blocker in one sentence. The **specific** blocker: the command that fails and its error, the credential that is missing, the decision only the user can make. "This needs more investigation" names nothing — it is the absence of a blocker, and it hands the work back for the user to re-scope.

## Red flags — you are about to waste a round-trip

| Thought | Reality |
|---------|---------|
| "This follow-up deserves a proper spec" | It's an Iteration. The existing mini-spec holds. |
| "I'll post the plan and wait for a go-ahead" | S/M announcements never wait. Announce and start in the same turn. |
| "Let me brainstorm before writing the mini-spec" | One planning pass. For M, the mini-spec is the plan. |
| "The user might want to weigh in first" | Interrupting is their move. Three ask permission, two ask a question, and both lists are closed. |
| "Let me re-read git.md before this commit" | Routine Git rules are inline. Once per session, L topology only. |
| "The PR is already open, its description stands" | Every commit can invalidate it. Re-read it before pushing. |
| "They asked about X, so they want X built" | A question is a question. Answer it; act on go. |
| "I'll flag this and let them decide" | Small local breakage met on the way is fixed, not flagged. |
| "It's one line, I'll slip it in" | Blast radius, not line count. Name it and leave it. |
| "Four of the five is basically done" | Done means done. Finish the fifth or name the specific blocker. |
| "I'll wait for the sub-agents before continuing" | Never idle. Pick up whatever does not depend on their results. |

## Project kickoff (once per project, not per session)

Before the first M or L task in a repository, look for `.claude/dev-methodology.local.md`. If it exists, apply its answers without re-asking. If not, ask the user **one consolidated batch** of questions (a single question-tool call, never a sequence of one-at-a-time asks) covering:

1. **Git permissions** — may commits / PR creation / PR merges be done by Claude, or is any of those human-only?
2. **Merge strategies** — squash or rebase for feature → workstream PRs, and how the workstream branch lands on the default branch.
3. **Tracking** — milestone? issues? a parent chantier issue with sub-issues?
4. **Issue and PR metadata** — default reviewers, default assignee, and which labels this repository actually uses. Without these recorded, every issue and PR opens bare (see "Git essentials").
5. **Model strategy** — see below; only relevant for L work.

Then write the answers to `.claude/dev-methodology.local.md` so no future session asks again, e.g.:

```markdown
# dev-methodology preferences (user answers — change only when the user does)
commit: claude-allowed          # or human-only
create-pr: claude-allowed       # or human-only
merge-pr: human-only            # or claude-allowed
feature-merge: squash           # feature → workstream
workstream-merge: rebase        # workstream → default branch
tracking: chantier-issue + sub-issues
reviewers: alice, bob           # default PR reviewers; CODEOWNERS wins where it applies
assignee: @me                   # default assignee on issues and PRs
labels: bug, feat, chore, docs  # labels this repository actually has
model-strategy: split           # or current-everywhere
```

Re-ask only when scope changes (e.g. work moves onto the default branch) or the user revokes an answer.

### Model strategy — roles, not model names

Model names rot; think in roles. The **orchestration role** (brainstorming, architecture, decomposition, review, verification) needs the strongest reasoning model available. The **implementation role** (sub-agents executing well-specified coding work) can run on a faster model.

**The orchestration role is filled by Opus 5 — not Fable 5, not Opus 4.8.** This is a standing user preference, not a capability argument: do not substitute another model because its name reads as newer or its description reads as stronger. It applies wherever a model is picked explicitly — `model:` on a sub-agent dispatch, on a workflow stage, or when proposing a session model. In harnesses that expose model families rather than versions, that is the `opus` option (never `fable`). If Opus 5 isn't offered, name the model you fell back to instead of switching silently. This governs the orchestration slot only — the implementation role still runs on the faster model.

- **S/M:** current session model for everything — don't ask.
- **L:** offer the choice once at kickoff: current model everywhere, or the orchestration/implementation split above. Record it in the preferences file.

If the environment does not allow model selection or per-sub-agent model assignment, say so and proceed with the current model.

## Workflow preferences

- **TDD, proportional.** New behavior gets the full red → green → refactor loop — via the superpowers TDD skill when installed, manually otherwise. Iteration on already-tested code gets targeted tests during the loop and one full-suite run at the end. Either way, never accumulate large amounts of unverified code.
- **One planning pass, ever.** See the dedicated section above — it is a hard rule, not a preference.
- **Sub-agents, never git worktrees.** Parallel or isolated work is delegated to sub-agents. An L feature briefs them per `references/orchestration.md`; a one-off S/M dispatch does not load that file, but still owes its agent a mission, a scope and a report format inline. Never create git worktrees — manual or tool-managed. Parallel writes are made safe by strict file partitioning (see orchestration.md), not by workspace duplication.
- **Route shell commands through RTK when available.** `rtk` is a token-optimizing proxy for dev operations (git, builds, test runs) that filters verbose output — typically 60–90% fewer tokens for the same signal. If the environment provides it (check with `rtk --version`; a hook may already rewrite commands transparently), prefer it over raw commands. Fall back to `rtk proxy <cmd>` only when the full unfiltered output is genuinely needed, e.g. while debugging.

## Working in parallel (Opus 5)

When running as Opus 5, optimize for wall-clock time. The reasoning is fast enough that what makes a session slow is the schedule, not the thinking.

- **Independent work runs at the same time, never one after the other.** Batch tool calls into a single message; dispatch sub-agents in a single message. This is scheduling, not ceremony: it does not turn S/M work into orchestration, which stays an L concern. "Run independent checks concurrently" says the same for verification, with the caveat that matters — logically independent is not contention-free.
- **Keep working while sub-agents run.** Dispatching is not a reason to go idle. Pick up whatever does not depend on their results, and collect them when they land.
- **Do not over-deliberate.** Enough information to act means act, and a decision with an obvious default gets the default plus one line saying so. This holds once the spec exists; before it does, the fields "The spec phase and the implementation phase" names are asked, not guessed.
- **Speed is never bought with quality.** Same rigor, same verification, same "done means done". Where parallelizing risks a worse result, serialize and say why.

**Delegating to a faster model is a judgement about the work, not about how hard the task looked.** Mechanical work of known shape — search, bulk edits, boilerplate — is what a fast model is for. Typecheck, lint and tests are not sub-agent work at all; they are batched commands. Deciding whether a result is *right* stays with the orchestration role, whatever model is running.

This governs L work, where sub-agents are already in play. S/M stays on the session model, as "Model strategy" settles it, and any answer recorded in `.claude/dev-methodology.local.md` stands over anything here.

**Never let two writers touch the same file** — two sub-agents, or the main thread against a sub-agent still running. This holds on any model and from the first parallel dispatch, without reading `references/orchestration.md`: split along non-overlapping boundaries, and reconcile in the main thread once the writes have landed. For an L feature, that file develops the full discipline.

## Reference files — load on condition, once per session

Each reference is loaded when its condition first fires, and not again. Announce the load in one clause when it happens — that line is what tells a later turn, including a post-compaction one, that the file is already in play.

| File | Load when | Never load for |
|------|-----------|----------------|
| `references/rust.md` | First architecture or implementation work on a Rust or hexagonal/port-adapter codebase | Reading Rust, summarizing a diff, explaining code as it stands |
| `references/orchestration.md` | Before decomposing an L feature, and in any case before its first dispatch | S/M work, or a single delegated lookup |
| `references/git.md` | Planning an L multi-workstream branch topology | Any commit, branch, or PR — those rules are inline in "Git essentials" |

If context was compacted and you cannot tell whether a reference was already loaded, re-read it: a duplicated read costs tokens, a missing rule costs a wrong architecture. Do not re-read merely because a new task started in the same session.

**A question that decides an architecture is architecture work.** "Where should this port live?", "should this invariant sit in the type?" — those load `rust.md`, because its rules are what answer them. So does reviewing a diff *for conformance* to them, which is where they bind hardest. The exclusion covers reading and recounting, where nothing is decided and nothing is judged. When you cannot tell which side you are on, ask what the load would change: if it would change the verdict, load it.

## Rule zero: read before you write

Before producing or modifying code:

1. Read the existing code you're about to touch — the actual files, not your assumption of them.
2. Read the full requirement or ticket, including edge cases mentioned in passing.
3. Read error messages and stack traces in their entirety before hypothesizing.
4. Search for prior art (RFCs, existing crates/libraries, similar code in the repo) before building from scratch.

If the problem cannot be restated in two plain sentences, it is not yet understood. When the requirement itself is genuinely ambiguous — you cannot tell what is being asked — restate it and ask. That is a question about the request, not approval for a plan.

Ambiguity is self-declared, so it is the easiest gate in the document to reach for dishonestly. It holds when you can name the readings you cannot choose between, or say exactly what is missing — a document the ticket refers to and you do not have, a domain term nothing in the repository defines. It never covers understanding the request and wanting agreement about it: "I would like to check my approach" is not ambiguity. And this bar is for the requirement as a whole. The spec-phase batch above has a far lower one — anything you would otherwise guess — and nothing here raises it.

Consequence is already covered by the third blocking wait above, and that list is closed. Rule zero does not add a second, vaguer trigger for it: a task that feels weighty but appears nowhere on that list calls for reading more carefully, not for pausing.

## Shared language (once per project)

Domain jargon is the main source of verbose, imprecise exchange: without a shared term, a concept costs a sentence every time it comes up — in conversation, in identifiers, and in commit messages.

Look for a project glossary before substantive work — whatever the repo already uses. If one exists, take its terms verbatim into prose, names, and commit messages; never coin a synonym for a concept it already names.

If none exists and the same concept keeps needing a paraphrase, propose one: the term, one line of definition, and the rejected alternative where the naming was contested. Restrict it to terms carrying domain meaning — a dictionary of the obvious is worse than none.

This applies where the domain has vocabulary of its own. On a utility or a thin wrapper, where the code already says what it does, skip it: a glossary nobody needed is ceremony, and removing ceremony is what most of the rules above exist to do.

The payoff is not only brevity. A named concept can be searched for, so the glossary doubles as a map of the codebase.

**And the entry document answers *where*.** A glossary says what a thing is called; it does not say which file registers a module, which layer owns a rule, or which command verifies the backend. Both questions get asked at the start of every session, by every agent, and only one of them has a glossary. Name the canonical locations and the commands that actually run, and point at one module worth imitating.

An entry document describing a layout the project has outgrown is worse than none. It does not merely fail to help: it sends every session down a wrong path until the code contradicts it, and the cost of that detour is paid once per session, forever.

**Correct it the moment you find it wrong.** Documentation rots because updating it is a separate act nobody schedules — so do not schedule it. The trigger is already free: when a session reads the entry document and then finds the codebase disagrees, that discovery *is* the update. A path that moved, a command that fails, a layer that was reorganised. The detour has already been paid; write it down in the same turn, before the finding evaporates with the session. One line is enough.

The same holds for what the current change just made true. A new module, a new registration point, a new verification command — if the map would now be wrong, fixing it is part of the change, not a follow-up someone schedules and never does.

**The cheapest audit is to run the commands it documents.** One that fails has been wrong for as long as nobody ran it, and every session since started from a false premise.

**And keep it short.** This document is loaded into every context, every session, by every agent, so a line that saves no exploration is a line every future run carries for nothing. A line earns its place only by being both stable and load-bearing: where things register, what the layers are, which commands actually run. Not what the code already says plainly — that duplicate will drift. Not what changes per feature — that belongs in the briefing.

## The mini-spec (M and larger)

Before writing code for anything M or larger, write a mini-spec — a handful of lines, not a document:

```
Goal:         <one sentence>
Invariants:   <business rules that must hold — in types; tests only where types cannot>
Out of scope: <what this change explicitly does not do>
Acceptance:   <observable checks proving it works>
Modules:      <bounded contexts touched — a change spanning many is misplaced, not large>
Files:        <expected files touched>
Verify:       <exact commands to run before claiming done>
```

Written once, it is reused three times: it seeds sub-agent briefings (L), the PR description, and the review checklist. Include it in the opening announcement and start working in the same turn — the mini-spec is announced, never submitted for approval.

## Before writing code

- **Identify invariants vs. variables.** Business invariants (e.g. "a revoked token is never accepted") must be encoded in the type system, and in tests only where the type system genuinely cannot carry them. These are not equal options: an `Option` that must never be `None`, guarded by a test that says so, is precisely the state "Make invalid states unrepresentable" exists to forbid. What is banned is a test *standing in for* a type that could have carried the rule — not testing itself. Exercising behavior the type already guarantees, or writing a regression before its fix, is ordinary testing and stays required. When the type change is right but sits outside the current scope, say that: name it as debt, leave it, and open it as its own issue if it earns one — do not present the test as the encoding. Things likely to change (report formats, third-party integrations) must sit behind a clear boundary (a port), never coupled inline.
- **Decide boundaries early, details late.** Fix the domain/ports/adapters frontier first; defer database, transport, and framework choices as long as the boundary allows.

## Architecture rules

- Business logic depends on nothing from infrastructure. If domain code knows a storage or transport detail, the boundary is already broken — flag it.
- Make invalid states unrepresentable. Encode constraints in the type system instead of scattered defensive checks; validate and convert at boundaries with dedicated types, never with dispersed validation.
- Do not abstract before the third occurrence. Two similar cases are a coincidence; three are a pattern. Prefer honest duplication over a wrong abstraction.
- **Depth beats count.** A good module hides a lot behind a small interface. A shallow one — a wrapper whose interface costs about as much as what it hides — is worse than no module at all: the indirection is paid and no abstraction is bought. The rule of three stops you abstracting too early; this stops you keeping an abstraction that never paid. Judge a module by what its interface lets a caller *not* know.
- When making a non-obvious design decision, record the _why_ (a short ADR-style note or comment), including the rejected alternative.

**If the project is in Rust, or follows a hexagonal/port-adapter architecture:** read `references/rust.md` (once per session) before doing architecture or implementation work. It contains binding rules on dispatch, port/adapter boundaries, type-driven invariants, error handling, persistence, and testing specific to that stack.

## Architectural drift (between changes, not during)

Every rule above judges one change. Drift is not a property of any single change — a codebase can satisfy all of them, commit after commit, and still degrade, because the degradation is emergent. No local rule catches a global trend.

Agents sharpen this. They accelerate adding code far more than they accelerate deleting, regrouping, or redrawing a boundary — those need a view of the whole that writing speed does not supply. The add-to-restructure ratio shifts, and that ratio *is* entropy.

So the check has to fire *between* changes. Propose an architecture pass — the `audit` skill when installed, otherwise a scoped read of the same ground — on a signal rather than on a schedule nobody remembers:

- an L feature has just finished integrating
- a change had to touch noticeably more modules than its mini-spec predicted
- the same file keeps turning up in changes that have nothing to do with each other
- a fix required understanding code nobody expected to read

Propose it; never run it unasked. It is read-only, but it costs a session's attention, and spending that is the user's call.

## Writing code

- Work in short loops: write, compile, test, repeat — minutes, not hours. Never accumulate large amounts of unverified code.
- Error handling is part of the design, not polish. Never ignore a recoverable error or swallow an exception silently; handle or propagate deliberately.
- Name for the reader six months from now: precise names over comments that can rot.
- Deletion is the best refactoring. Prefer removing code to adding it; every line is a liability (read, maintained, secured).

## Testing and verification

- Test invariants and behavior, not implementation details. A test that breaks on legitimate refactoring but passes on real regressions is worse than no test.
- Respect the pyramid: many fast unit tests on the domain, some integration tests on adapters, very few end-to-end tests on critical paths.
- **Reproduce before fixing.** Write the regression test _before_ the fix; if it passes before the fix, the bug is not yet found.
- **Run independent checks concurrently.** Typecheck, lint, unit tests, and integration tests do not depend on each other — issue them as one batch rather than awaiting each in turn. This applies at every size, Iteration included. Caveat: logically independent is not the same as contention-free. Cargo commands share a lock on the target directory, so batching `cargo clippy` with `cargo test` in one checkout buys nothing — remove the contention first, or accept the serialization and say so.
- **Scope the loop, not the exit.** In the red → green loop, verify only what the change touches: the crate or package, narrowed to the feature's module path where the tool allows it (`cargo test -p <crate> <module>::`). Typecheck and lint get the same treatment — they are often slower than the tests and just as easy to narrow. The full block runs **once**, at the end, as an exit check.
- **Name the exit block as one.** A verification block written without that distinction reads as *the* verification and gets replayed at every step. Call it `Verify (exit)` in the mini-spec, and put the loop command beside it when the scoping is not obvious from the layout.
- **"Green" describes the working tree, not the history.** A commit is green because the tree was verified when it was made — not because it would pass if replayed in isolation. Under a squash merge, intermediate commits never reach the default branch, so stashing the rest of the work to re-verify each one independently spends effort on artifacts the merge discards.
- Treat benchmarks like tests: versioned, reproducible, with thresholds.
- Add observability (structured logs, traces, metrics) while the system is simple, not when it is on fire.

## Debugging

Use `superpowers:systematic-debugging` when available — it is the canonical protocol (read the full error, build a minimal deterministic repro, bisect, change one variable at a time). Follow the same steps manually if superpowers is absent. House rules on top, either way:

- Default suspicion order: the most recently written code first; the library, compiler, and kernel last.
- Once found, ask _why the bug was possible_ and close that class of bugs, not just the instance.

## Git essentials

These rules cover routine Git operations inline — do not reload a reference file per commit or per PR. Read `references/git.md` only when planning an L multi-workstream branch topology, at most once per session.

- **NEVER cite Claude as author or co-author.** No `Co-Authored-By: Claude` trailer, no "Generated with Claude" line, no AI attribution of any kind in commit messages, PR descriptions, or issue bodies. Commits are authored by the user's Git identity only.
- **Issues and PRs are written in English.** Titles, bodies, and comments on issues and pull requests are in English, whatever language the session conversation is in. Commit messages follow the convention below, which is English by construction.
- **Commit convention:** `<type>(<subject>): <verb><message>` — e.g. `feat(auth): implement oidc spec`. Type from conventional-commit vocabulary (`feat`, `fix`, `refactor`, `chore`, `docs`, `test`, `perf`, `ci`); subject = module or bounded context; message starts with an imperative lowercase verb. Every commit, no exception.
- Never commit directly to the default branch. Branch names describe intent (`feature/…`, `chantier/…`, or the repository's existing convention — check the repo's history and follow it).
- One commit = one logical change, with a message explaining the _why_. `git log` is the most-read documentation in the project.
- Permissions (commit / PR creation / merge) and merge strategies come from `.claude/dev-methodology.local.md` (see kickoff). If the file answers, act without re-asking; if it is missing or silent, ask once in a consolidated batch and record the answer. If a permission is human-only, prepare everything up to that boundary and hand off.
- **Issues and PRs open complete, never bare.** Reviewers, assignee, and labels are set in the creation command itself, not added afterwards — afterwards does not happen, and a bare PR silently becomes nobody's job.
  - **Assignee** — whoever will do the work. Default to the user's own account.
  - **Reviewers** — from `.claude/dev-methodology.local.md`. A repository `CODEOWNERS` file wins for the paths it c

…(truncated)
