协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
decision-records【开发与工程】Creates, supersedes and validates decision records (ADRs) against the convention a collection already follows, instead of imposing a published one. Use when the user wants to record a decision, write an ADR, supersede an existing decision, audit or lint a decisions folder, check that an ADR index is in sync, or asks "why did we decide X". Deduces the…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Decision records are cheap to write and expensive to trust. The expensive part is the collection: numbers that collide, an index that stopped matching the directory, a record that says it was superseded and does not say by what. This skill writes records and, mainly, holds a collection to its own convention.
template.md next to the records is a second home for the convention, and two homes
diverge in silence: the template keeps a section the records dropped a year ago and
nothing says which one is the convention. The most-installed prior art
(affaan-m/ECC, skills/architecture-decision-records) creates one; this is a
deliberate departure, not an oversight.privacy-guard skill, whose denylist is gitignored on
purpose. Duplicating that list here would put the same list in two places, which is the
failure above with worse consequences. The --portable check covers only what breaks
when a record is copied: absolute paths, and links that climb out of the collection.Find the collection. The three defaults in the wild are all different, so look, do not
assume: doc/adr (adr-tools), docs/adr (the ECC skill), docs/decisions (MADR 4.0.0).
bash "${CLAUDE_PLUGIN_ROOT}/skills/decision-records/scripts/check-decisions.sh" <dir>
Its first block reports what the collection does: filename scheme, where the status lives, the status vocabulary in use, the sections most records carry, and the index. That report is the convention. Everything you write next matches it, including the parts you would have written differently.
If the collection is empty or does not exist, ask once, and offer both schemes:
| Scheme | Gains | Costs |
|---|---|---|
NNNN-slug.md (recommend this one) | a short reference you can say out loud: "see 0007" | two parallel PRs both take the next number and both merge (adr/madr#28, open since 2020, no convention adopted); breaks on copy between repos and on reordering |
YYYY-MM-DD-slug.md | no collision between parallel PRs, survives a copy into another repo, sorts correctly | no short reference: you cite a record by its whole filename |
Recommend the numbered scheme unless the repo takes decisions through parallel PRs, which
is the case the date scheme was invented for (log4brains adopted it in that thread for
exactly this reason). Nothing else about the record changes with the choice.
max + 1 when the
scheme is numbered (never reuse a number, not even of a deleted record).INDEX check exists to catch; do not create it and then
report it.The skeleton, for a collection that has none to deduce. Sections adapt to the collection's own set when there is one:
# NNNN. <decision in a short noun phrase>
## Status
proposed
## Context
What forced a decision now. The constraints, the pressure, what was true at the time.
Not the solution.
## Decision
What we do, in the present tense: "We use X". One or two sentences.
## Consequences
What gets easier, what gets harder, what we now owe. Both directions, honestly: a record
with only benefits documents an advert, not a decision.
Two rules about content, from Nygard's original and worth keeping: write why, because the what is already visible in the code; and record the alternative that was rejected and the reason, because the next person will otherwise re-propose it.
An accepted record is a statement about a moment. Never edit one to change its mind — that destroys the only evidence of why the old decision made sense, and the record's whole value is that evidence.
superseded by <new ref>, and nothing else in it.Use the reference form the collection already uses. The validator resolves markdown links
([0009](0009-slug.md)), ADR-0009, and a bare number, and reports one that resolves to
nothing. A bare number is only resolvable where the filename scheme carries one; under a
dated or free-form scheme records cite each other by filename, and the validator says it
left those references unchecked rather than calling them all dangling.
check-decisions.sh [--require "A,B"] [--status "a,b"] [--portable] DIR
# 0 clean, 1 violations, 2 usage error or nothing to check
Eight checks, each printing a stable code so the reason is readable and greppable:
| Code | What it catches |
|---|---|
NAME | a filename outside the scheme the other records use, or a collection that agrees on no scheme at all. Four schemes are recognised: YYYY-MM-DD-slug.md, NNNN-slug.md, <prefix>-NNN-slug.md, and free-form |
SECTION | a section missing from a record that more than half the collection carries |
STATUS | a record with no status, in a collection whose other records have one. Four forms are read: frontmatter status:, a - Status: bullet, **Status**:, a ## Status section. When no record has one, that is not eight violations, it is one line in checks that did not run |
DRIFT | one status spelled two ways: Accepted beside accepted |
SUPERSEDE | a reference that resolves to nothing, or a status claiming supersession that names no replacement |
DUPLICATE | two records claiming one identifier, where the scheme carries one (NNNN-slug.md, ADR-031-slug.md); under a dated or free-form scheme the whole filename is the identifier and cannot collide |
INDEX | the index and the directory disagreeing, in either direction |
PORTABLE | with --portable: absolute paths, and links that climb out of the collection |
Three deliberate properties:
OK: no violations and has
no way to know how many of the eight were in a position to say anything, and silence
reads as a pass. Two shipped defects came from exactly that gap.--require and --status are there
for a collection that wants to be held to something stricter than its current habits, and
they are the only way a rule enters that the collection is not already following.The STATUS and DRIFT checks assume the status field holds a word, and key the deduced
vocabulary off its first word. Some collections put a paragraph there instead: a status word
followed by two sentences of narrative and a list of cross-references. On those, the deduced
vocabulary is the first word of prose and means nothing, and --status would report every
such record as outside the vocabulary.
That is a limit, not a bug to normalise away: deciding where a token ends inside free text is
guesswork, and guessing is how a validator starts reporting a collection's own convention back
to it. Do not pass --status on a collection whose status field carries prose. Everything
else on it still works, because the other seven checks do not read that field.
When the user asks "why did we decide X", read the index first, then the matching records, and answer from Context and Decision. If nothing matches, say so and offer to record one.
Not this skill's job, and not duplicated here. If privacy-guard is set up in the repo, its
denylist is the list:
grep -n -i -E -f <(grep -vE '^[[:space:]]*(#|$)' .local/privacy-denylist.txt) <dir>/*.md
check_privacy.sh itself reads git diff --cached, so it covers a record on its way into a
commit but not one already sitting in the tree. Its own skill names that limit and says to
grep the tracked tree against the denylist by hand for an audit. The grep -f form is
written out here rather than there, and reads that same gitignored file directly.
template.md in the target repo.