Purpose
Stage: cross-cutting / setup (one-time). Every downstream skill reads and writes a shared substrate:
the STATE.md board (what's in flight + who owns the next action), the per-feature artifact directories,
the repo-wide design substrate (CONTEXT.md glossary + docs/adr/), the session state
(docs/session-state.md — the snapshot of where the work stands; docs/session-log.md — the append-only log of decisions a resuming
session reads first), the run record
(docs/progress.md — what each slice actually executed, one entry per slice), the lessons record
(docs/lessons.md — what each root-caused defect turned out to be, and the guard that would catch it
coming back), and the rules file that carries the ## Agent skills block, with a pointer to it in
whichever of CLAUDE.md / AGENTS.md does not hold it. If that substrate doesn't exist, each skill has to re-derive "where do issues live / where's
the glossary / where do ADRs go" — which is exactly the scattered-tracker problem this suite consolidates
away (one local board, not mp's several issue queues). project-setup makes the substrate exist once, so the rest of the suite consumes it cold.
This is a prompt-driven skill, not a deterministic script. Explore, present what you found, confirm with
the user, then write. It is distinct from preflight-readiness: preflight-readiness is a per-run environment gate that
re-fires every wave; project-setup is the one-time repo bootstrap that runs before any feature exists.
When to use / when to skip
- Use once per repo, before the first feature — before
interview-me,idea-refine, orspec-grilling. - Skip if
STATE.mdalready exists at the repo root and the substrate is intact; you don't need to re-run per feature (the board is appended to byplan-breakdown, not re-scaffolded). - Re-run to repair when a substrate file has gone missing or stopped resolving — including a
CLAUDE.md/AGENTS.mdpointer naming a file that is no longer there. Nothing watches for that between runs, so the pointer check under "Verification" is what catches it, and it only runs here. - Escape hatch — adopt, don't overwrite: if the repo already has a
CONTEXT.md,docs/session-state.md,docs/session-log.md,docs/progress.md,docs/lessons.md,docs/adr/, or aCLAUDE.md/AGENTS.mdwith prior content, adopt them in place. Re-runningproject-setuprepairs missing pieces; it never clobbers existing user content. The three append-only files matter most here: every entry indocs/session-log.md, indocs/progress.md, and indocs/lessons.mdexists nowhere else, so re-scaffolding over any of them deletes reasoning and evidence nobody can recover.
Inputs
Foundation only — this is the bootstrap; it has no upstream artifact and no refuse-to-run dependency. It reads the repo as-is to decide what already exists:
git remote -v/.git/config— is there a remote? (informational only; the tracker is local regardless.)- root
CLAUDE.mdandAGENTS.md— does either exist? Is there already an## Agent skillssection? - root
CONTEXT.md/CONTEXT-MAP.md— single- or multi-context already? docs/adr/,docs/features/,docs/session-state.md,docs/session-log.md,docs/progress.md,docs/lessons.md,STATE.md— does prior output already exist?.gitignore— does any pattern in it matchdocs/session-state.md,docs/session-log.md,docs/progress.md, ordocs/lessons.md? All three have to be committed.
Process
1. Explore
Look at the current repo to understand its starting state. Read whatever exists; don't assume:
git remote -vand.git/config— is this a remote-backed repo? (Informational — does not change the tracker.)CLAUDE.mdandAGENTS.mdat the repo root — does either exist? Is there already an## Agent skillssection?CONTEXT.mdandCONTEXT-MAP.mdat the repo root — is a single- or multi-context layout already implied?docs/adr/and anysrc/*/docs/adr/directories.docs/features/,docs/session-state.md,docs/session-log.md,docs/progress.md,docs/lessons.md, andSTATE.md— does this skill's prior output already exist?.gitignore— isdocs/session-state.md,docs/session-log.md,docs/progress.md, ordocs/lessons.mdmatched by anything in it? All three exist to survive the session that wrote them, so an ignored copy dies on the next fresh clone.
2. Present findings and ask
Summarise what's present and what's missing. Then walk the user through the decisions one at a time — present a section, get the answer, then move on. Don't dump everything at once.
Assume the user does not know what these terms mean. Each section starts with a short explainer (what it is, why these skills need it, what changes if they pick differently), then the choices and the default.
There is no issue-tracker question. In this suite the tracker is always the local STATE.md board:
a two-level board (features → slices) with a gate column that absorbs mp's
ready-for-agent/ready-for-human triage roles. So the source's GitHub/GitLab/local choice and its
five-label vocabulary are already decided — you scaffold STATE.md, you do not ask where issues live.
Mention this once so the user knows their GitHub Issues (if any) are intentionally not the work surface here.
Section A — Domain doc layout (the one surviving choice).
Explainer: Several skills (
spec-grilling,code-review,quality-verification,incremental-implementation) readCONTEXT.mdfor the project's domain language anddocs/adr/for past architectural decisions. They need to know whether the repo has one global context or several (e.g. a monorepo with separate frontend/backend contexts) so they look in the right place.
- Single-context — one
CONTEXT.md+docs/adr/at the repo root. Most repos are this. (Default.) - Multi-context —
CONTEXT-MAP.mdat the root pointing to per-contextCONTEXT.mdfiles (a monorepo).
If neither CLAUDE.md nor AGENTS.md exists, also ask which one holds the rules — don't pick for
them. Both filenames end up existing either way: the one they pick gets the ## Agent skills wiring, and
the other gets a short pointer to it, so a contributor whose tool reads that other name is not left
with nothing. Note that a fresh CLAUDE.md is seeded with a small set of project-agnostic behavioral
guidelines (the bundled assets/CLAUDE.template.md) above the ## Agent skills wiring; a fresh
AGENTS.md gets the wiring only.
3. Confirm and edit
Show the user a draft of everything before writing, and let them edit:
- The
STATE.mdskeleton (the empty board with the legend; see "STATE.md seed" below). - The
CONTEXT.mdstub (glossary-only). - The
docs/session-state.mdstub (empty fields; see "session-state.md seed" below) and thedocs/session-log.mdstub (the preamble and entry shape, no entries; see "session-log.md seed" below). Say what each is for and why they are two files: the five fields are a snapshot of where the work stands, rewritten each time and therefore always short; the log is an append-only record of decisions — the reason, what was ruled out, what is still open — that only ever grows. Kept together, the half that grows without limit sits under the half every session must read. Both are committed, so they survive the session that wrote them. - The
docs/progress.mdstub (the entry shape, no entries; see "progress.md seed" below). Say what it is for while you show it: one entry per slice recording what that slice actually executed — the commands, their real output, the files that changed, and what was not run. A run's own summary is otherwise the only account of itself, and a summary is written by the party with the most reason to round up. - The
docs/lessons.mdstub (the field template, no entries; see "lessons.md seed" below). Say what it is for while you show it: one entry per root-caused defect, and every entry names the guard that would catch that defect coming back. A slice reads the file before it writes its skeleton, which is the moment the knowledge is worth anything — read afterwards, a lesson is a post-mortem of work already done. - The
## Agent skillsblock to add to whichever ofCLAUDE.md/AGENTS.mdis being edited. - The pointer that goes in the other filename (see "pointer seed" below) — it names the file that holds the rules, and says nothing else.
- If a fresh
CLAUDE.mdis being created, the bundled behavioral template (assets/CLAUDE.template.md) that seeds it — the user can edit it now or later (it's a starting point, not a fixed contract).
4. Write
Pick the file that holds the rules (verbatim file-selection rules):
- If
CLAUDE.mdexists, edit it. - Else if
AGENTS.mdexists, edit it. - If neither exists, create the one the user chose in Section A — never pick for them.
- Never write the rules into both. Exactly one file gets the
## Agent skillsblock — the one already there, or the one the user chose. The other never gets a copy of it: two copies drift apart, and a reader has no way to tell which one is lying. If an## Agent skillsblock already exists, update it in place rather than appending a duplicate; don't overwrite the surrounding sections. - Then create the other filename as a pointer. Once the
## Agent skillsblock has landed, write the pointer (see "pointer seed" below) into whichever ofCLAUDE.md/AGENTS.mddoes not hold it. The pointer names the file that does, and says nothing else. Without it, a contributor whose tool reads only that filename opens an empty file or none at all. If the file already exists with content of its own, do not overwrite it — show the user the pointer line, ask before adding it at the top, and either way say plainly which file holds the rules. If they decline it, say what that leaves: their tool opens a file that does not route anywhere, and finding the rules is now on whoever reads it. That is theirs to accept, and it is the one branch where the pointer does not exist. - Seeding a fresh
CLAUDE.md: only when neither file exists and the user choseCLAUDE.md, write the bundled behavioral template (assets/CLAUDE.template.md) first, then append the## Agent skillsblock below it (see "CLAUDE.md seed" below). A freshAGENTS.mdchosen as the rules file gets the## Agent skillsblock only — no behavioral template. Never seed over aCLAUDE.mdthat already exists; edit it in place.
Then create the substrate (skip anything that already exists; never clobber).
STATE.mdat the repo root — the empty two-level board with the legend (see seed below).CONTEXT.mdat the repo root (orCONTEXT-MAP.md+ per-contextCONTEXT.mdfor multi-context) — the glossary stub with a## Glossaryheading, devoid of implementation detail (see "CONTEXT.md seed" below). The## Glossaryheading is mandatory —spec-grillingappends terms under it.docs/adr/— repo-wide ADR home (seed a.gitkeep; ADRs are namedADR-<NNN>-<slug>.md).docs/features/— per-feature artifact root (docs/features/<slug>/holds intent.md, prd.md, acceptance.md, environment.md, plan.md per feature; seed a.gitkeep).The
## Agent skillsblock in the chosen file (see below), pointing atSTATE.md,CONTEXT.md,docs/adr/,docs/session-state.md,docs/session-log.md,docs/progress.md, anddocs/lessons.md, and namingdocs/design.md— which setup does not create; the first UI surface writes it. The domain-doc consumer rules carry over fromreferences/domain-docs.md. Then the pointer in the other filename (see "pointer seed" below), naming the file that holds the block.docs/session-state.md— where the work stands, seeded with five empty fields (see "session-state.md seed" below), anddocs/session-log.md— why it stands there, seeded with its preamble and no entries (see "session-log.md seed" below). Skip either if it already exists — and for the log skipping is not a courtesy, it is the only safe move: it is append-only, so overwriting one destroys decisions that exist nowhere else. Seed no entries; entries are written byhandoffas decisions get made.Check
.gitignoreand make sure nothing matches either. Both are committed on purpose. Their whole job is to outlive the session that wrote them, and an ignored copy dies on the next fresh clone — which is precisely the case they exist for. If a pattern does match one, say so and ask before changing.gitignore; that file is the user's.docs/progress.md— the run record, seeded with the entry shape and no entries (see "progress.md seed" below). Skip it if it already exists, and here — as with the session log directly above — skipping is the only safe move rather than a courtesy: it is append-only, so overwriting one destroys the only account of what earlier runs actually did. Seed no entries; entries are written by the skill that runs a slice, one per slice, as slices run.Check
.gitignorehere too. A record nobody commits is a record nobody reads: the question it answers — what did the last run really do — is asked by someone who was not present, most often on a fresh clone. If a pattern matches it, say so and ask before changing.gitignore; that file is the user's.You are scaffolding this file in the repository itself, not in a worktree —
project-setupruns once, before any feature exists, so there is no slice branch for a write to get stranded on.docs/lessons.md— the lessons record, seeded with its## Entry shapeheading, the fenced field template beneath it, and the prose that states the rules every writer of an entry reads there (see "lessons.md seed" below). Seed no entries: every##heading after## Entry shapeis somebody else's, written by whoever root-causes a defect, bycode-reviewfor aCritical:finding it raised, and by theorchestratorcarrying back an entry a slice handed it. Skip the file if it already exists, for the same reason as the two above and with the same force: it is append-only, so overwriting one destroys what earlier defects cost somebody to work out.Check
.gitignorehere too. A lesson is written for the next person to work in that area, who is usually not the person who wrote it and often arrives on a fresh clone. If a pattern matches it, say so and ask before changing.gitignore; that file is the user's.
The ## Agent skills block:
## Agent skills
This repo is configured for the achilles-skills pipeline (run once by `project-setup`).
### State board
Work-in-flight lives in `STATE.md` at the repo root — a local two-level board (features → slices) with a
`gate` column marking who owns the next action. This is the only work tracker; there is no GitHub/GitLab
issue queue. See `STATE.md`.
### Domain docs
Single-context: `CONTEXT.md` + `docs/adr/` at the root. (Multi-context: `CONTEXT-MAP.md` → per-context
`CONTEXT.md`.) Skills read these before exploring; consumer rules in the project-setup skill's
`references/domain-docs.md`.
### Per-feature artifacts
Each feature's intent.md / prd.md / acceptance.md / environment.md / architecture.md /
architecture.html / plan.md live under `docs/features/<slug>/`, with each slice's concrete
steps in `docs/features/<slug>/plan/<slice-id>.md`.
### Code comments and docstrings
Explain the code to the developer reading it. A docstring that points at a spec instead of
explaining — `Implements US-3`, `see prd.md`, `per plan.md step 2` — leaves the next reader
no better off, and those files move as the feature moves while the code stays. Say what it
does, why it is built this way where that is not obvious, and which invariants it assumes. A
`docs/adr/` record may be cited **after** the explanation as provenance, never in place of it;
ADRs are immutable once written, which is why a citation to one keeps being true.
### Decided look
`docs/design.md` holds this repo's look once it is decided: the palette, the type, the layout language,
the motion posture, and the signature vocabulary every interface here shares. The **first** user interface
built in this repo writes it; every later one starts from it and records only what differs. Nothing
scaffolds it — a repo with no user interface has none, and that is correct rather than missing.
### Session state and decision log
Two files, because they grow differently. `docs/session-state.md` is the snapshot — five fields saying
where the work stands, rewritten each time, so it stays short no matter how long the project runs.
`docs/session-log.md` is the record — an append-only account of decisions, the reason, what was ruled out,
what is still open — and it only ever grows. Kept in one file, the record buries the snapshot every
session must read; kept apart, the log can grow for the life of the project without taxing anyone.
**Read the snapshot before starting work**, at the top of every session, and **read the log before
re-opening any question**, so one it already answers is not argued twice. The log is append-only: earlier
entries are never edited, re-ordered, or deleted, a wrong entry is corrected by a new entry naming it, and
an attempt to change one is reported as a violation rather than quietly refused. Entries hold only what
the git history cannot show — never which files changed. It is the weakest source here: a decision record
under `docs/adr/` or a signed `acceptance.md` outranks it, and a decision that must bind future work is
promoted to `docs/adr/`.
### Run record
`docs/progress.md` records what each slice actually executed: one entry per slice, carrying the commands
in the form they were run, their real output, the files that changed, and what was **not** run and why.
**Read it when you resume work you were not present for**, alongside the two files above. The board says
where things stand, the log says why, and this says what was actually done — a claim that a check passed
either has the command's output under it or says the check was not run, and there is no third state.
It is append-only on the same terms as `docs/session-log.md`, and it is evidence rather than a status page: no entry
carries a stage, a state, or an owner. `STATE.md` answers who owns the next action, and a second answer
here would eventually disagree with it.
### Lessons
`docs/lessons.md` holds what a root-caused defect turned out to be: one entry per defect, seven fields
each, naming the guard that would catch it coming back.
**Read it before building a slice**, so a pit somebody has already fallen into is not fallen into twice.
It is append-only on the same terms as `docs/session-log.md`. The same defect recurring is a second entry, never an
edit to the first — the count is the signal that the first guard did not hold, and folding two entries
together destroys it.
### Keeping these files worth reading
Every file above is read by somebody who has none of your context, at the moment they decide what to do
next. That is what they are for, and it is also the whole constraint on them: **a line earns its place by
changing what a cold reader would do.** A line that records merely that work happened changes nothing —
git holds that already, exactly — and a second copy of a derivable fact is free to disagree with its
source, leaving a reader no way to tell which one is lying.
So the test, before writing any line into any of them: *would somebody who just arrived act differently
for having read this?* If not, it does not go in. What that means file by file:
- **`STATE.md`** — tokens and short phrases in cells, never sentences. It is a board: where each slice
is, and who owns the next move. *Why* a slice is where it is belongs in `docs/progress.md`; what got
decided belongs in the log. A cell holding a paragraph is a board nobody can read across.
- **`docs/session-state.md`, the five fields** — where the work stands **now**, in the fewest words that
let a person resume without asking a question. They are overwritten every time, so nothing in them is
history: last session's state is not context, it is noise with a timestamp.
- **`docs/session-log.md`** — one entry per **decision**, in the four lines the entry shape
gives it, one sentence per line. Not one per session, not one per turn, not one per file touched. A
session that decided nothing appends nothing, and that is a correct outcome rather than a missing entry.
- **`docs/progress.md`** — the commands as they ran, and the output that settles whether each passed.
Quote what a reader needs in order to believe the claim, not the whole scroll, and say what was not run.
- **`docs/lessons.md`** — one entry per **root-caused** defect, seven fields, and a named guard. An
observation with no guard behind it is a preference, and preferences are what fill a file like this
until nobody reads it.
- **`CONTEXT.md`** — one definition per domain term, in the language of the domain, with no
implementation detail. A term already defined is sharpened in place, never defined a second time.
**The cure is at the point of writing, never at the point of reading.** These records are append-only for
a reason: what grows with decisions grows with work, and trimming it deletes the evidence somebody will
need. A file that has become too long to skim was not written too much — it was *admitted* too much. The
fix is the next line you are about to add, not the hundred already there.
5. Done
Tell the user setup is complete and which skills now read from these files (spec-grilling appends
CONTEXT.md and writes docs/adr/; interview-me/idea-refine write docs/features/<slug>/intent.md;
plan-breakdown adds feature blocks + slice rows to STATE.md; the orchestrator drives STATE.md;
handoff writes docs/session-state.md and appends to docs/session-log.md; whichever skill runs a slice —
incremental-implementation on its own, or the orchestrator when a run drives it — appends that slice's
entry to docs/progress.md; debugging-and-error-recovery appends a docs/lessons.md entry whenever a
defect is root-caused, code-review appends one for a Critical finding and for nothing lesser, and the
orchestrator appends the entries slices hand back, from the checkout it holds rather than from theirs).
Say which of CLAUDE.md / AGENTS.md holds the rules and that the other is a pointer to it, so a
contributor on the other tool lands in the right place. A rule change goes in the rules file; the pointer
never gets a copy of it.
Say what the two session files are for too: docs/session-state.md is a snapshot and
docs/session-log.md is a record, and the log is append-only — nothing in it is ever edited or deleted,
so it can be trusted as evidence rather than read as a status page. They are separate files so the record
never buries the snapshot. Both are committed so they survive a fresh clone; leaving either out of version
control defeats it.
Say the same about docs/progress.md, and say what it is not: it records what each slice ran and what
came back, and it never says which stage a slice is at or who owns the next action — that is the board's
question, and one question with two answers is one answer too many. It is append-only and committed for
the same reasons.
Say what docs/lessons.md is for as well: one entry per root-caused defect, each naming the guard that
would catch it coming back, read before a slice is built rather than after. It is append-only and committed
on the same terms, and the same defect recurring is a second entry — the count is the signal that the first
guard did not hold, and folding the two together destroys it.
Mention that STATE.md, CONTEXT.md, the five fields of docs/session-state.md, and the per-feature
docs are theirs to hand-edit later — docs/session-log.md, docs/progress.md, and docs/lessons.md being the
exceptions, since correcting an entry in any of them means appending a new one rather than changing the
old one. Re-running project-setup is only needed to repair or re-scaffold a substrate file that has
gone missing.
STATE.md seed
Write this empty board — the legend header is the stable section downstream consumers depend on; no feature
blocks yet (those are born from an already-sliced plan, added by plan-breakdown):
# Pipeline State — <project>
> Single source of truth for what's in flight. The orchestrator drives slice rows; the human reads the
> `gate` column to know what needs them. No emojis; text tokens only.
>
> Cells hold tokens and short phrases, never sentences — this is a board, and it is read across rather
> than down. Why a slice is where it is goes in `docs/progress.md`; what got decided goes in
> `docs/session-log.md`. A row whose cell has grown into a paragraph has taken on another
> file's job, and the board stops being skimmable at exactly the moment there is enough work to need it.
feature state: spec · plan · building · done ← the PRD's stage
slice state: impl · verify · review · ship · done · blocked · halted
gate: you · agent · done ← who owns the next action
<!-- Feature blocks are added by plan-breakdown. Slice ids are PRD-namespaced. Example shape:
## PWR · Password reset feature: building
origin: prd.md · acceptance.md · plan.md
| Slice | Title | Design ref | State | Gate | Blocked by | Artifacts |
|--------|--------------------|--------------------------------|---------|-------|-----------|-----------|
| PWR-1 | request reset link | `docs/features/pwr/design-contract.md` | impl | agent | — | — |
| PWR-2 | expire stale tokens| — | impl | agent | PWR-1 | — |
-->
Design ref holds the signed design contract + prototype the slice builds against, or — for a slice that
builds no UI. The orchestrator copies it into both the implement and the verify dispatch brief, so a — has
to be recorded rather than left blank — blank reads as "nobody looked".
CONTEXT.md seed
Write this glossary stub — the ## Glossary heading is the stable section spec-grilling appends terms
under (canonical heading; do not rename); no terms yet (those emerge as the ubiquitous language is
sharpened). The seed comment states the entry shape rather than inventing one: spec-grilling's
references/CONTEXT-FORMAT.md is what the appender writes to, so a seed describing a different shape
opens a file whose first entries disagree with every entry appended after them.
# <project>
## Glossary
<!-- Domain terms are appended here by spec-grilling as the ubiquitous language emerges.
One entry per term: the term in bold on its own line, its one-or-two-sentence definition
beneath it. No implementation detail. Group with ### subheadings once clusters emerge —
never with a second ## section, which splits the glossary in two. -->
For multi-context, seed CONTEXT-MAP.md at the root plus one per-context CONTEXT.md, each carrying its
own ## Glossary heading.
session-state.md seed
Write this stub at docs/session-state.md — five empty fields and the pointer to the log. The five field
headings are the stable sections consumers depend on (canonical headings; do not rename). The file holds
no log entries and no ## Log heading; the record is docs/session-log.md, seeded separately below.
# Session state
Where the work stands, right now. Every field below is a **snapshot** — overwritten each time this file
is written, so only the latest version is true and nothing here is history.
Why the work stands here, and which questions are already settled, is the record:
**`docs/session-log.md`** (append-only). Read this file before starting work, and read the log before
re-opening any question — one it already answers does not get argued from zero.
The two are separate files because they grow differently. This one is rewritten, so it stays short. The
log only ever grows, and underneath a snapshot it would bury the twelve lines every session came for.
## Current objective
## Current state
## Remaining issues
## Boundaries
## Next phase
Fill in none of the five fields — they are written by handoff when there is actually a session to
record.
handoff also adds ## Suggested skills and ## Referenced artifacts after ## Next phase the first
time it writes the file. The seed leaves them out because there is nothing to point at yet.
session-log.md seed
Write this stub at docs/session-log.md — the preamble and the entry shape, no entries. The explanation
is part of the seed: an append-only file is only append-only if the next person to open it can tell that
from the file.
# Session log
Why the work stands where it does. One entry per **decision**, appended and never edited, so every entry
stays true about the moment it was written. Where the work stands *now* is the snapshot:
`docs/session-state.md`.
Append-only. A new entry goes after the last one. Earlier entries are never edited, re-worded, re-dated,
re-ordered, or removed. An entry that turned out to be wrong is corrected by a **new** entry naming the
old one — that the call was once made that way is the fact worth keeping. An attempt to change or remove
an earlier entry is **reported as a violation**, naming the entry and what would have changed; refusing
quietly is not enough, because a silent refusal reads as a silent success.
An entry holds only what the git history cannot show: the decision, the reason, what was ruled out, and
what is still open — **one sentence each, four lines, no fifth.** Never which files changed or what was
added; git holds that exactly, and a second copy of a derivable fact can disagree with its source,
leaving a reader no way to tell which one is lying.
An entry records a **decision**, not that work happened. The test is whether a later session, not knowing
it, would re-open the question. A slice landing, a test going green, a plan step taken — none of those
are decisions, and what a run executed belongs in `docs/progress.md`. Most sessions append zero or one
entry, and a session that decided nothing appends nothing.
This is the weakest source in the repo. It never overrides a decision record under `docs/adr/` or a
signed `acceptance.md`; where they disagree, the committed contract is right and the entry is stale. A
decision that has to bind future work is **promoted** to a decision record under `docs/adr/`; the
original entry is left as it is, and a new entry names both it and the record it became.
Entries are kept, not compacted. They grow with decisions, and decisions are work; compaction is for
what grows with elapsed time. And this file is committed, not ignored — its whole purpose is to outlive
the session that wrote it.
<!-- Entry shape. Append below, oldest first:
### <date> — <one line: what was decided>
Decided: <the call>
Because: <the reason>
Ruled out: <the alternative, and why not>
Still open: <what this did not settle, or "nothing">
-->
Seed no entries — they are written by handoff as decisions get made. A seeded entry is a decision
nobody made.
progress.md seed
Write this stub at docs/progress.md — the entry shape and no entries. ## Entry shape is the one
heading the seed ships (canonical heading; do not rename); every other ## heading in the file is an
entry, and entries are appended after it, oldest first. The shape itself sits inside a fenced block,
which is what keeps it from reading as the first entry. The explanation is part of the seed, for the
same reason it is part of docs/session-log.md: an append-only file is only append-only if the next
person to open it can tell that from the file.
# Run record
What each slice actually executed. One entry per slice, appended when the slice starts and completed
when it returns.
This file exists because a run's own summary is otherwise the only account of itself, and a summary is
written by the party with the most reason to round up. An entry is evidence: the commands in the form
they were run, and what came back from them.
**It never says where the work stands.** No entry carries a stage, a state, or an owner — `STATE.md`
answers who owns the next action, and a second answer here would eventually disagree with it. Reading
this file will not tell you which slice is waiting on a person, and that is deliberate rather than
missing.
**Append-only.** A new entry goes after the last one. Earlier entries are never edited, re-worded,
re-dated, re-ordered, or removed — including an entry that recorded a failure the next attempt fixed.
The same failure twice is two entries; the second never replaces the first. An entry that turned out to
be wrong is corrected by a **new** entry naming the old one. An attempt to change or remove an earlier
one is a **STOP**: the work ends there, and the violation is reported, naming the entry and what would
have changed. Refusing quietly is not enough — a silent refusal reads as a silent success, and the text
already recorded stays readable in full either way.
**A heading with nothing under it is a slice that started and did not finish.** It is written the moment
the slice starts, before anything can go wrong with it, so a slice that dies mid-flight still leaves a
mark. That is not the same as a slice that was never started, which has no heading at all. Leave it as
it is; the missing fields are the record.
## Entry shape
Append below, oldest first:
```
## <date> — <SLICE-ID> — <title>
- Summary:
- Commands run: (verbatim, one per line)
- Results: (the output lines that settle whether it passed — the counts, the failure
signature, the exit status — quoted exactly. Not the whole scroll: an
entry a reader scrolls past is evidence nobody checked)
- Files changed:
- Not run: (what was skipped, and why — never omit this line)
- Follow-ups:
Do not claim a command was run if it was not run. Write it under "Not run" and say why.
Withhold any credential that appears in output, and say that you withheld it.
```
Nothing checks those last two lines. They are written here, next to the shape, because this is what
the writer is looking at when the temptation arises.
The last two lines of the shape are the whole point of the file and nothing mechanical enforces them, so do not add a claim that something does. A "Not run" line that is present and honest is what separates a check that passed from a check nobody performed; without it, the two read identically.
Seed no entries. An entry is a record of work somebody did, so a seeded one is fiction of exactly the
kind this file exists to prevent — the same reason docs/session-log.md ships with no entries.
lessons.md seed
Write this stub at docs/lessons.md — the field template and no entries. ## Entry shape is the one
heading the seed ships (canonical heading; do not rename); every other ## heading in the file is an
entry, and entries are appended after it, oldest first. The template sits inside a fenced block, which is
what keeps it from reading as the first entry. The explanation is part of the seed for the same reason it
is part of the other two append-only files: the rules are only followed if the next person to open the
file can read them there.
# Lessons
What a defect turned out to be, so the same pit is not fallen into twice. One entry per root-caused
defect, appended once the cause is known and the fix is in.
**Read it before you build.** A slice reads this file before it writes its skeleton. That is the moment
the knowledge is worth anything: the skeleton is where the decisions these entries are about get made,
and a lesson read after the code is written is a post-mortem of work already done.
**Every entry names a guard.** `Automated guard` says what would catch this defect coming back — a lint
rule, a test, a helper that makes the mistake unavailable, a checklist item, or an architecture
invariant. It is filled in as the entry is written, by whoever root-caused the defect and is the only
person who knows what would have caught it. An entry naming none is **refused**: a lesson nobody can name
a guard for is a preference, and preferences are what fill a file like this until nobody reads it.
**Append-only.** A new entry goes after the last one. Earlier entries are never edited, re-worded,
re-dated, re-ordered, or removed. An entry that turned out to be wrong is corrected by a **new** entry
naming the old one. An attempt to change or remove an earlier entry is a **STOP**: the work ends there,
and the violation is reported, naming the entry and what would have changed. Refusing quietly is not
enough — a silent refusal reads as a silent success.
**The same lesson twice is two entries.** A defect whose category is already in the file is recorded
again rather than folded into the entry already there. The count is the signal: a category appearing a
second time says the first guard did not hold, which is the one thing a reader most needs to know.
Merging the two destroys the only evidence of it.
**Three parties write here, and they write different entries.** Whoever root-causes a defect writes one.
A review writes one only for its most serious class of finding — a `Critical:` finding, once root-caused.
A Required or lesser finding is refused here and stays in that review's findings list, where it is
already tracked; admitting those would make this file a second copy of every review.
**The third is a courier, not an author.** An entry written from inside a slice's own checkout lands on a
branch that may never merge, so a slice hands its finished entry back and the run that drove it appends
the entry here, from the checkout the run itself holds. The courier carries what it was handed and infers
nothing from silence: a slice that hands back nothing has nothing appended for it.
## Entry shape
Append below, oldest first:
```
## <date> — <one line: what the defect turned out to be>
- Tags: (the area, so a later reader finds the others like it — and can count them)
- Symptom: (what was observed, in the form it was observed)
- Root cause: (why it happened — the cause, not the place it surfaced)
- Fix: (what changed to close it)
- Prevention: (what to do differently next time)
- Automated guard: (what would catch a recurrence — a lint, a test, a helper, a checklist item, or
an architecture invariant. Never empty: an entry without one is refused)
- References: (where to look — the commit, the run-record entry, the failing command)
Withhold any credential that appears in output, and say that you withheld it. `Symptom` and
`References` are the two fields one arrives in, and no entry here is ever edited — a value written
into one cannot be taken back out.
```
All seven fields get filled in. `Automated guard` is the one that gets left for later, and later is
where a lesson dies: by then whoever knew what would have caught the defect has moved on, and working it
out again costs what the defect cost the first time.
Seed no entries, for the same reason as the other two: an entry is a record of a defect somebody actually hit, so a seeded one is a claim about work that never happened. And do not soften the guard-missing rule into a warning while you write the seed. A warning about a missing field is how the field ends up optional, and an entry with no guard ca
…(truncated)