feat(skill): adr — the house format, and how to record a decision you cannot make yet
Codifies cad's ADR template (frontmatter + Status/Context/Decision/Consequences/
Validation) and formalises the productive-tensions pattern that ADR-0001 already
does informally under "Known refinements, deliberately not built yet".
The RED run is why the skill is shaped this way. An unaided agent given a
genuinely-undecidable transport choice produced a strong ADR — it invented a
"Cost of reversal" section, wrote concrete revisit triggers, and reframed the
unanswerable question ("which is better?") into an answerable one ("which
option's correctness depends on the unsettled variables?"). So the reasoning was
not the gap. What it could not guess was the house format: it invented a bullet
header instead of the frontmatter that makes the corpus greppable, and it wrote
no Validation section at all.
The GREEN run then found eleven problems. Four mattered:
- **The Overview contradicted the Escalation section in its first sentence** —
"An ADR is not documentation of a decision. It is the decision" against
"decides nothing". Escalation is right; the flourish is gone.
- **The escalation clause trapped every agent-authored ADR.** The deciders are
never the agent, so `proposed` is mandatory — and the skill separately called
a lingering `proposed` a failure mode. Now: a `proposed` ADR with a named
decider is waiting on a person and gets chased; one without is waiting on
nobody. Naming that person is the deliverable, not a formality.
- **No filename or path convention, in a skill whose thesis is greppability.**
Called "the biggest hole in the skill", correctly. Now docs/adr/NNNN-slug.md,
with the cross-repo caveat that a bare number is ambiguous between repos.
- **"Pick the option whose correctness does not depend on the unknown" assumed
such an option always exists.** It often does not — that is what makes a
tension productive — and the rule would then push the author to manufacture a
reason one of them is, which reads as reasoned and never gets re-examined. A
fallback ladder now says: cheaper to reverse, lower switching cost, already
built — and say that is why.
Also: `deciders` had no valid value while proposed; the body Status section
duplicated frontmatter with no precedence; the tension template silently dropped
the "address the strongest case against" requirement the plain format demands;
the preservation strategy — the actual technical content — got one angle bracket
while the easy parts got full templates; resolution triggers were singular for
tensions that routinely have several; and lineage/grill-me were listed as
cross-references at the bottom when they are pre-steps that change the content.
Closes #9
Bump-Type: minor
This commit is contained in:
@@ -9,6 +9,7 @@ the SKILL.md, not the table.
|
|||||||
<!-- BEGIN skills-table -->
|
<!-- BEGIN skills-table -->
|
||||||
| Skill | Description | Use when | Surfaces |
|
| Skill | Description | Use when | Surfaces |
|
||||||
|-------|-------------|----------|----------|
|
|-------|-------------|----------|----------|
|
||||||
|
| `adr` | Architecture Decision Records — house format, and how to record a decision you cannot make yet | Recording an architectural decision; revisiting one; or deciding between options where the right answer genuinely is not knowable yet | claude-code, library |
|
||||||
| `atdd` | Acceptance Test-Driven Development workflow | Feature development with acceptance criteria | claude-code, library |
|
| `atdd` | Acceptance Test-Driven Development workflow | Feature development with acceptance criteria | claude-code, library |
|
||||||
| `cad-babysit` | Focused human-in-the-loop session working CAD's stuck queue (read-only) | Picking up whatever `cad stuck` flags, with a human present | claude-code, library |
|
| `cad-babysit` | Focused human-in-the-loop session working CAD's stuck queue (read-only) | Picking up whatever `cad stuck` flags, with a human present | claude-code, library |
|
||||||
| `clean-code` | Clean code principles with Go adaptation | Code review, refactoring, new code | claude-code, library |
|
| `clean-code` | Clean code principles with Go adaptation | Code review, refactoring, new code | claude-code, library |
|
||||||
@@ -21,6 +22,7 @@ the SKILL.md, not the table.
|
|||||||
| `feature-spec` | Implementation-level feature spec (tighter than spec-driven-dev) | Scoping one feature inside an already-specced project | claude-code, library |
|
| `feature-spec` | Implementation-level feature spec (tighter than spec-driven-dev) | Scoping one feature inside an already-specced project | claude-code, library |
|
||||||
| `gitea-ci` | Gitea CI/CD pipeline with act_runner, buildah, k3s, GitHub mirror | Setting up or debugging CI on this infra | claude-code, library |
|
| `gitea-ci` | Gitea CI/CD pipeline with act_runner, buildah, k3s, GitHub mirror | Setting up or debugging CI on this infra | claude-code, library |
|
||||||
| `grill-me` | Structured plan interrogation — Quick Poke, Full Grill, Pre-mortem | Stress-testing a plan before committing; end of Diamond 1; before promoting to pre-prod | claude-code, library, chat |
|
| `grill-me` | Structured plan interrogation — Quick Poke, Full Grill, Pre-mortem | Stress-testing a plan before committing; end of Diamond 1; before promoting to pre-prod | claude-code, library, chat |
|
||||||
|
| `lineage` | Backward decision archaeology — was this tried before, and does the reason it failed still hold | Before proposing something that feels new; when an idea keeps resurfacing; when you inherit code whose reason is gone | claude-code, library |
|
||||||
| `own-the-task` | Delegation ownership — Intake → Brief → Plan → Execute | A task is handed over open-ended; \"take this off my plate\"; before a plan exists | claude-code, library, chat |
|
| `own-the-task` | Delegation ownership — Intake → Brief → Plan → Execute | A task is handed over open-ended; \"take this off my plate\"; before a plan exists | claude-code, library, chat |
|
||||||
| `planning` | Planning and task breakdown | Sprint planning, task decomposition | claude-code, library |
|
| `planning` | Planning and task breakdown | Sprint planning, task decomposition | claude-code, library |
|
||||||
| `playwright` | Playwright E2E testing — setup, HTMX patterns, SSE streams, CI wiring | Adding browser tests to any project with a web UI | claude-code, library |
|
| `playwright` | Playwright E2E testing — setup, HTMX patterns, SSE streams, CI wiring | Adding browser tests to any project with a web UI | claude-code, library |
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
---
|
||||||
|
name: adr
|
||||||
|
surfaces: [claude-code, library]
|
||||||
|
summary: "Architecture Decision Records — house format, and how to record a decision you cannot make yet"
|
||||||
|
use_when: "Recording an architectural decision; revisiting one; or deciding between options where the right answer genuinely is not knowable yet"
|
||||||
|
description: Write an Architecture Decision Record in this estate's format — YAML frontmatter, Status/Context/Decision/Consequences/Validation — including the case where the tradeoff is genuinely context-dependent and forcing a resolution would be worse than preserving the tension. Use when recording or superseding an architectural decision, or when a decision keeps getting re-litigated because nobody wrote down why.
|
||||||
|
---
|
||||||
|
|
||||||
|
# ADR
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
An ADR is the *record* of a decision — the artifact someone reads in eight months when
|
||||||
|
they are about to undo it without knowing why it exists. It does not confer the decision;
|
||||||
|
a person with authority does that, and this skill never is that person.
|
||||||
|
|
||||||
|
Two things make one useful, and neither is the prose quality: a **format consistent
|
||||||
|
enough to grep across repos**, and an honest account of **what was not decided**.
|
||||||
|
|
||||||
|
## Before you write
|
||||||
|
|
||||||
|
Both of these change what goes in the ADR, so they are not cross-references, they are
|
||||||
|
steps:
|
||||||
|
|
||||||
|
1. **`lineage`** — was this decided before and reversed? The reason it was dropped is
|
||||||
|
usually the most valuable sentence in your Context, and if an earlier ADR exists,
|
||||||
|
yours supersedes it rather than ignoring it.
|
||||||
|
2. **`grill-me`** (Full Grill) — find the hole now. It is cheaper here than in the
|
||||||
|
Consequences section of the ADR that supersedes yours.
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- Any choice where a reasonable alternative existed and was not taken
|
||||||
|
- Superseding or amending an earlier ADR (never edit the old one — see Status)
|
||||||
|
- A decision that keeps getting re-litigated, which means the reasoning was never recorded
|
||||||
|
- A tradeoff that is genuinely context-dependent — see *Preserving a productive tension*
|
||||||
|
|
||||||
|
**Do not use for:** implementation choices with no alternative worth naming, or anything
|
||||||
|
a code comment answers. An ADR per function is an ADR nobody reads.
|
||||||
|
|
||||||
|
## The format
|
||||||
|
|
||||||
|
**Where it goes:** `docs/adr/NNNN-kebab-slug.md`, zero-padded to four digits, numbered
|
||||||
|
sequentially within the repo. Frontmatter you cannot locate is not greppable, and half
|
||||||
|
the value of the format is that `docs/adr/*` is a known path in every repo here.
|
||||||
|
|
||||||
|
**The number is repo-local.** `cad` ADR-0002 and `infra` ADR-0002 are different
|
||||||
|
documents. When referring across repos — including in `supersedes` — qualify it:
|
||||||
|
`cad#0002`. Within a repo the bare number is fine.
|
||||||
|
|
||||||
|
Frontmatter first. It exists so the corpus is machine-readable — "which ADRs does 0004
|
||||||
|
supersede", "what is still `proposed`" — and a bespoke header block, however well
|
||||||
|
written, cannot answer those.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
adr: 0004
|
||||||
|
title: <the decision, as a sentence — not "database choice">
|
||||||
|
status: proposed | accepted | superseded | amended
|
||||||
|
date: 2026-08-29
|
||||||
|
deciders: <who accepted it, by name — or "pending: <name>" while proposed>
|
||||||
|
supersedes: <adr number, or ->
|
||||||
|
---
|
||||||
|
|
||||||
|
# ADR-0004 — <same title>
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
**The frontmatter is authoritative for status and date.** This section exists for the one
|
||||||
|
thing the frontmatter cannot hold: *why* it moved — what superseded it and on what
|
||||||
|
evidence, or what accepting it was contingent on. If it would only restate the YAML,
|
||||||
|
delete it.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The forces. What is true that makes this a decision rather than a default — constraints,
|
||||||
|
the failure that prompted it, what was tried. Name the unknowns explicitly; a decision
|
||||||
|
made under uncertainty is not weaker for saying so, it is auditable.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
One sentence of what was decided, then why *this* option rather than the named
|
||||||
|
alternatives. Address the strongest case against, not the weakest.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive:** what this buys.
|
||||||
|
|
||||||
|
**Negative / watch:** what it costs, and the specific thing to watch that would tell you
|
||||||
|
it was wrong. "Some added complexity" is not a consequence; "the event schema is now the
|
||||||
|
coupling surface between cad and every executor — version it deliberately" is.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
How you will know. A link to the experiment or the metric that proves or kills it, and
|
||||||
|
the condition that fires a revisit. An ADR with no validation section is a preference
|
||||||
|
with a template around it.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preserving a productive tension
|
||||||
|
|
||||||
|
Some tradeoffs are not unresolved because nobody thought hard enough. They are
|
||||||
|
context-dependent, the context is not settled, and picking now means picking blind.
|
||||||
|
Forcing resolution there produces a confident record that is wrong half the time.
|
||||||
|
|
||||||
|
Record the tension instead, in the Decision section:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Provisional: **<option A>**, on this reasoning: <what makes it safe to pick now>.
|
||||||
|
|
||||||
|
<The strongest case for the option you did NOT take, and why it does not win today. The
|
||||||
|
plain format demands this and a preserved tension does not get an exemption — the whole
|
||||||
|
point is that the loser is still live.>
|
||||||
|
|
||||||
|
### The tension, preserved
|
||||||
|
|
||||||
|
- **Option A** — <what it assumes, what it costs>
|
||||||
|
- **Option B** — <what it assumes, what it costs>
|
||||||
|
|
||||||
|
**What we do not know:** <the variable that decides it>
|
||||||
|
|
||||||
|
**Preservation strategy:** <what we build now so BOTH stay reachable. This is the
|
||||||
|
technical content of the ADR, not a one-liner — enumerate it. It is usually the part both
|
||||||
|
options need anyway, which is often most of the work: the schema, the interface seam, the
|
||||||
|
idempotency key. Name each piece and say which option it keeps alive.>
|
||||||
|
|
||||||
|
**Resolution triggers:** <plural, and one per unknown. Two independent unknowns do not
|
||||||
|
collapse into one condition, and a trigger may exist that neither unknown implies.>
|
||||||
|
```
|
||||||
|
|
||||||
|
Two rules keep this from becoming an excuse:
|
||||||
|
|
||||||
|
1. **A preserved tension still ships a decision.** "We will decide later" is not one.
|
||||||
|
Prefer the option whose correctness does not depend on the unknown, say so, and mark
|
||||||
|
it provisional.
|
||||||
|
|
||||||
|
**When no such option exists** — and often none does, because a tension is productive
|
||||||
|
precisely when both options are hostage to the same unknown — do not manufacture a
|
||||||
|
reason that one of them is. That reads as reasoned and will not be re-examined, which
|
||||||
|
makes it worse than an honest arbitrary pick. Fall back, in order: the option that is
|
||||||
|
cheaper to reverse; the one with the lower switching cost given your preservation
|
||||||
|
strategy; the one already built. Then **say that is why you picked it.** "Chosen
|
||||||
|
because it is the cheaper mistake" is a real justification and an auditable one.
|
||||||
|
2. **The resolution trigger must be observable by someone who is not you.** "When it
|
||||||
|
becomes a problem" is not a trigger. "When executor count passes five and they are all
|
||||||
|
long-lived services in one cluster" is. A trigger nobody can evaluate is a decision
|
||||||
|
that quietly becomes permanent.
|
||||||
|
|
||||||
|
`cad`'s ADR-0001 does this informally under *"Known refinements (recorded, deliberately
|
||||||
|
not built yet)"* — sovereignty deferred to a 3-class model *"until a client actually opts
|
||||||
|
into an EU provider"*. That clause is the pattern: option, cost, and the event that
|
||||||
|
forces the choice.
|
||||||
|
|
||||||
|
## Status discipline
|
||||||
|
|
||||||
|
- **Never edit an accepted ADR to change its decision.** Write a new one and set
|
||||||
|
`supersedes`. The record of what was believed, and when, is the whole value.
|
||||||
|
- **Amend** for corrections that do not change the decision (a broken link, a clarified
|
||||||
|
consequence). Note the amendment inline with its date.
|
||||||
|
- **`proposed` is not a parking space.** An ADR that sits proposed for a month *with no
|
||||||
|
named decider* is a decision nobody is making; either accept it provisionally or close
|
||||||
|
it as declined. With a named decider it is a decision waiting on a person, which is a
|
||||||
|
different thing and is chased rather than closed.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
|
||||||
|
| Anti-pattern | Why it fails |
|
||||||
|
|---|---|
|
||||||
|
| Consequences that are all positive | Then it was not a decision, it was an announcement |
|
||||||
|
| "We chose X because it is better" | Names no alternative and no force; unfalsifiable |
|
||||||
|
| Written after the code shipped | Reverse-engineers a justification. Record the reasoning while it is still contested |
|
||||||
|
| One ADR amended repeatedly | Destroys the history the format exists to keep |
|
||||||
|
| No Validation section | Nothing distinguishes a good decision from a lucky one |
|
||||||
|
| A tension "resolved" by picking arbitrarily | The next person re-litigates it, having lost the reasoning |
|
||||||
|
|
||||||
|
## Escalation
|
||||||
|
|
||||||
|
Writes a file, so it acts — but it decides nothing.
|
||||||
|
|
||||||
|
Stop and hand back when: the deciders are not you — which, for an agent, is always. An
|
||||||
|
ADR records a decision someone with authority made; writing one to *create* that
|
||||||
|
authority is how a preference acquires a document number.
|
||||||
|
|
||||||
|
So the deliverable is a draft marked `proposed` with **the person who must accept it named
|
||||||
|
in `deciders` as `pending: <name>`**, and that hand-back stated in the response, not left
|
||||||
|
implied. Naming the decider is the escalation, not a formality — an unnamed one is how a
|
||||||
|
proposal sits for a month.
|
||||||
|
|
||||||
|
That is the intended state for an agent-authored ADR, and it is not the parking-space
|
||||||
|
failure below: a `proposed` ADR with a named decider is waiting on someone; one without is
|
||||||
|
waiting on nobody.
|
||||||
|
|
||||||
|
Also stop when the Context cannot be written without inventing a constraint. A missing
|
||||||
|
force means the decision has not been thought through yet, and an ADR built on an
|
||||||
|
invented one is worse than none: it will be cited.
|
||||||
|
|
||||||
|
## Cross-References
|
||||||
|
|
||||||
|
- `lineage` and `grill-me` — see *Before you write*; they change the content, so they are
|
||||||
|
steps rather than references.
|
||||||
|
- `spec-driven-dev` / `feature-spec` — an ADR is the *why*; a spec is the *what*. If you
|
||||||
|
are describing behaviour, you are writing a spec.
|
||||||
Reference in New Issue
Block a user