Files
khosra/.claude/skills/feature-loop/SKILL.md
T
bdeshiandClaude Opus 5 16fde5ee68 lift the skill's question cap, which CLAUDE.md lifted and it did not
SKILL.md said "maximum three" clarifying questions while CLAUDE.md §4 said
"there is no cap" — lifted in 25d7045 and never propagated. The constitution is
always loaded and wins, so the skill was simply wrong, and an agent reading only
the skill would have split a six-fork request across turns to stay under a cap
that no longer exists.

The redundancy between the two files is deliberate: the constitution is always in
context and the skill is not, so both state the loop. HARNESS.md now records that
this deliberate copy has drifted twice — this, and the skill's conflict table
still deferring to `[spec]` markers after they were deleted. No gate can catch
it, because both files are prose and each is internally consistent, so the only
mechanism is grepping the other whenever a rule changes in either.

3 files. No rule changed — one restated correctly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 23:16:11 +06:00

15 KiB

The feature loop

Seven steps, in order: Clarify → Plan → Implement → Verify → Document → Commit → Report. CLAUDE.md holds the rules; this file holds the procedure.

Scale, do not skip. A one-sentence request gets one line of plan and a short report — and still gets the conflict check, propagation, verification, the state.md touch and the stale-reference sweep. Casual phrasing is not a smaller change: "default-language files do not need the .en part" is one unambiguous sentence that rewrites filenames on disk, makes a suffix optional in code, and falsifies every doc calling it required.

Propagation — a change is not done until every surface agrees

One request can touch four surfaces. Name each as done or n/a before reporting; the ones people miss are the first and the last.

Surface What it means
the site root Out of reach: it lives in its own repo (ADR-0011). A disk-contract change ships a written migration step the author runs, plus a note on whether any bundle key or URL moves. Never claim to have migrated files you cannot see.
code The engine, plus a test for the new behaviour.
the theme Out of reach, like the site root (ADR-0023). This repo ships a contract extension in
harness/theme-contract.md plus a written note of what a theme must do — never the theme itself, and never
a layout or markup decision dressed as an engine feature.
fixtures and emitted output Fixture sites in testdata/, the embedded default templates (a reference implementation of the contract), and anything reading a field or path shape you altered.
harness docs Every doc, ADR, marker or example that assumes the old form. An ADR that mandates what you just made optional is not stale, it is contradicted — supersede it, do not quietly reword it.

1. Clarify

Read harness/state.md, then harness/README.md — the map, always, before deciding what else to open.

A handoff is not evidence. If .scratch/continue.md says something is pending, waiting, or unfinished, run git log --oneline -- <the path it names> before planning from it. The file is uncommitted, so no gate compares it to anything, and it is written during a change whose commit can still move underneath it. The log is the only record of what landed. Offering the human work that is already in HEAD wastes the turn they asked the question in. For code, read the compressed form before the source: harness/surface.md locates every declaration, go doc gives a package's surface, a test states its contract. Disciplines: harness/context-economy.md. Use its topic-ownership table to list the docs owning anything this change asserts a rule about, and read those. That list is a floor: skipping it is how a rule gets written twice and the second copy contradicts the first.

Conflict check — every request

Before anything else, ask whether the request contradicts something already decided. Classify and act; do not average two positions into a compromise nobody chose.

Kind Examples What to do
Hard — reverses a deliberate decision an ADR, an architecture invariant, the permalink shape, the untrusted boundary, a _MAX budget, a frozen contract, the dependency policy Stop. Quote the line, name the file, give both paths: comply, or change the decision (new ADR, or supersede the old one). Wait. Never pick for them.
Soft — exceeds a convention a _WARN threshold, a style-floor preference, a latent item's trigger, a shape sketched in ideas/ State it in one line, proceed with the request, record the deviation where the convention lives. Conventions are a floor, not a decision.
Stale — a doc contradicts the code state.md inventory, a STATUS line, an out-of-date example Auto-resolve: the code wins. Fix the doc in Document, mention it in the report. No question needed.
None — the request refines or extends what is written asking for more of something a doc already describes as built Proceed. Say nothing about it.

Resolve intelligently where the answer is genuinely unambiguous — a stale doc, or a request that is a superset of what is written. Surface anything where a reasonable person could have meant either thing, and everything in the Hard row without exception. One conflict is worth one message; a silently reversed decision costs the trust in every other decision.

Then decide what you genuinely do not know. Ask only questions whose answer changes the code or the bytes on disk — but ask every one of those. Batched, one message, up front, each with a bold default so silence answers. There is no cap (CLAUDE.md §4): a request carrying six real forks gets six questions, and splitting them across turns to look brisk wastes more of the human's time than asking once. This file said "maximum three" until the cap was lifted in CLAUDE.md and not here.

Never ask about: anything conventions.md, content-model.md, or an ADR already decides; naming, formatting, file placement, test style; permission to follow the constitution; "would you like me to also…" — that is scope creep wearing a question mark.

Do ask when: the feature has two plausible disk formats, URL shapes, or authoring ergonomics; it touches an open question in state.md; success criteria are not observable from the request; it appears to need a dependency, a new package, or a frozen-contract change.

No such questions? One line — "No questions — assuming tag pages live at the section root and reuse the post list template." — and continue.

2. Plan

Post this before writing code. Under fifteen lines.

Goal:              one sentence, observable from outside the program
Reduces to:        Bundle | Stage | Query | View | Interaction | Effect | bundle-as-program
Success criteria:  the checks that will prove it works (commands, URLs, expected output)
Files:             exact paths, marked new / edit, with an estimated ±LOC each
New deps:          none  (anything else needs an ADR first — stop and ask)
Earn-it check:     which counter this increments, and whether an extraction is due this change
Trust check:       does any untrusted input reach this code? ("n/a" if not)
Docs read:         the owning docs you opened ("none" only if the change asserts no rules)
Not doing:         the two or three adjacent temptations you are declining

Wait for a go-ahead unless the user said "just do it" or the change is under ten lines in one file. If the plan reveals a trunk, say so instead of planning and offer the leaf.

3. Implement

  • Smallest code satisfying the success criteria. Nothing for the version after next.
  • Only the planned files. No renames, reordering, reformatting beyond gofmt, or tidying of neighbouring code.
  • Something wrong outside the plan? One line on the Latent list in harness/state.md, keep moving. That is the whole mechanism; use it instead of a drive-by fix.
  • Reuse the existing shape before inventing one. Copying eight lines twice beats an abstraction at use two; the third use is when it becomes a design.
  • Halt and ask if any CLAUDE.md §6 stop condition fires. Halting costs one message.

4. Verify

Run ./scripts/verify.sh --quiet — doc coupling, format, vet, build, test, dependency allowlist, budgets. Quiet prints only failures, warnings and the verdict; use the loud form when a gate fails and you want its neighbours, or when auditing the gates themselves.

Then at least one piece of feature-specific evidence you actually executed: the new test name and its output, curl -s localhost:PORT/path | head, a golden-file diff, the before/after fragment of real generated markup, or benchmark numbers.

Budget failure: shrink the change, or stop and propose an ADR raising it. Never raise it silently.

A standing advisory is a defect. If a warning fires on correct code and will keep firing, either the check is wrong or the code is — resolve which, in that change. Tolerated warnings are how the whole channel stops being read, and the count creeping from one to two is the only signal you get.

Never write "should work", "this will now…", or your own diff summarised as a result. If you could not run something, say which and why.

5. Document

Same change, not later. Triggers: harness/README.md. Walk the propagation table above — every surface done or explicitly n/a. Minimum:

  • harness/state.md: inventory rows, counters, latent items added/removed — in this commit, never a trailing one, which is what verify.sh compares (ADR-0057).
  • harness/decisions.md: an ADR if the choice is expensive to reverse. Six lines. First open the doc that owns the topic — if it already carries the rule, amend it there; an ADR restating an existing doc is a duplicate, not a decision.
  • Any other doc only if the change made it wrong. Never state one fact in two docs.

Doc edits are surgical too. Prefer deleting a stale line to appending a corrected one.

Then sweep for what you replaced. Any rule, value, name or path you changed may be described elsewhere in the old terms:

grep -rn '<old form>' harness CLAUDE.md HARNESS.md ideas reference .claude scripts

verify.sh fails on dangling file paths, ADR numbers and CLAUDE.md section refs. It cannot detect a sentence that is merely now untrue, or a concept renamed in one place — that sweep is the author's, and skipping it is how a doc ends up contradicting the file it points at.

6. Commit

Not optional and not asked for: the work is committed before the report, every time. CLAUDE.md §4 holds the rule; this is how.

The notice, if this session did not ask for automatic commits. Before the first commit only, on its own line, visible:

**Committing this to git** — and every later batch of changes in this session. Say "stop committing"
and I will leave the tree dirty instead.

Once said, it is not repeated. If the human asked for automatic commits — in this session or by having put the rule here — skip the notice entirely; repeating a notice for something already agreed is noise that trains them to skim the next one.

What goes in one commit. The changes that would be reverted together (conventions.md "Git"). Bundle freely inside one unit: code with its test, its state.md row and its ADR are one commit because reverting the code without the doc leaves a lie. Split when a second pass is independently revertible — a fix to something you noticed afterwards, or a doc correction that was already wrong before this change. "Which of these would I want to undo alone?" answers it faster than any rule.

How.

  • git status first: an unexpected file is a stop condition, not something to sweep into git add -A.
  • Stage what the change touched, then check git diff --cached --stat against the plan's file list.
  • The pre-commit hook runs verify.sh, so a red gate blocks the commit. That is the design — fix the gate or the code, do not reach for --no-verify.
  • Subject imperative, under 72 characters. Body says why, plus the numbers the change earned (LOC, counters, a benchmark) since git log is the map when no agent is available.
  • Never git push. Publishing is the human's; committing is bookkeeping.
  • Scratch files, ideas/, reference/ and anything under the scratchpad stay out.

Then reconcile the handoff — once the commit exists, never before. If .scratch/continue.md carried a planned item this change completed, delete that item now and note anything learned that changes a later one. Doing it during Document records the plan's version of events instead of the result: an amend, a squash or a late fix moves the commit under a handoff that already claims what it contains.

7. Report

Short — one line each, no prose unless a conflict or a stop condition needs explaining. Drop the lines that are genuinely n/a rather than padding them.

Did:        what now works, in the user's terms
Evidence:   the command you ran and its result
Diff:       files touched, ±LOC
Propagated: content / code / templates / docs — each done or n/a, with content files named
Committed:  the commit subjects this produced, one line each
Earned:     counters after this change; anything now due for extraction
Skipped:    what you deliberately did not do, and the latent items you logged
Conflicts:  hard ones surfaced and how they were settled, soft ones deviated from, or "none"
Swept:      the old form you grepped for after a rename, or "n/a"

Failure modes to catch in yourself

Symptom What it means Do instead
Interface with one implementation Anticipating, not designing Concrete type; wait for the second
A parameter no caller passes Speculative flexibility Delete it
Reaching for a dependency Usually 40 lines of stdlib Write the 40 lines, or stop and ask
Wanting a switch on post type in the core A View or Query is the seam Move it out
Needing a mock to test Wrong seam fstest.MapFS or a testdata dir
Renaming "for clarity" mid-feature Drive-by refactor Latent list
Explaining why the boundary can bend here It cannot (ADR-0003) Stop and ask
Plan grew while implementing Scope drift Stop, re-plan, continue
Writing a rule without reading its owning doc Guessing where you could look harness/README.md topic table, then amend that doc
Quietly doing what was asked against a recorded decision The conflict was real and you hid it Quote the line, give both paths, wait
Changed the rule in code, left the docs describing the old one Propagation stopped at the first surface Walk all four surfaces
Made a field optional, left the ADR mandating it Contradicted, not stale Supersede the ADR
Treated a one-sentence request as a narrow change Articulation size is not blast radius Walk the propagation table
Splitting the difference between request and policy A compromise nobody chose Surface it; the human picks
Ending a turn with the work only in the working tree The one copy is the one that gets lost Commit before reporting
Offering work a handoff calls pending The file states intent; only the log states what landed git log -- <path> before trusting it
Ticking the handoff during Document The commit can still move under it Reconcile it after the commit exists
Opening a file to find out what is in it The generated surface already answers it harness/surface.md, then read the range
Grepping for callers before changing a signature The compiler enumerates them exactly Change it, then go build ./...
A shorter report that dropped a caveat, case or number Truncation wearing compression's clothes Restore it; cut words, never findings
One commit per file, or per doc touched Shredding a single revertible unit Bundle what would be undone together
Committing without ever saying you would Consent assumed, not given The notice, once, before the first one