agentleFS
llopresto87llopresto87/Cypress/skills/adr-writer/SKILL.md

adr-writer

Skill119 starsChanged 31 days agoMITView on GitHub

Author an Architecture Decision Record that captures a non-obvious technical choice — its context, the decision, the consequences, the rejected alternatives, and the reversibility cost. Use whenever a non-trivial dependency is picked, a framework is chosen, a one-way door is opened, two specialists disagree and the orchestrator must pick, or anyone in a future session would ask "why did we do this?" An ADR exists so the answer is on disk, not in someone's head. Its lifecycle status lives in frontmatter in the schema's one vocabulary (proposed, accepted, open, deferred, hotfix, rejected, superseded, closed); superseding is a new ADR plus a status flip on the old — never an edit of its body.

12 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

9 rules. 0 are common, 4 are distinctive, and 5 are very specific to this project.

  • 0

    Common

    Most of the 7 edited versions we read keep these.

  • 4

    Distinctive

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use docs/graph/templates/adr.template.md.

    line 113
  2. Distinctive:

    When the owner states how an external arrangement works (a partner handles a class of requests, a contract covers a duty), record it as a …

    line 129
  3. Distinctive:

    Every ADR tags reversibility as one of:

    line 173
  4. Distinctive:

    Declining a fix is a decision: When the owner accepts a residual risk instead of remediating it, the ADR says plainly that nothing is …

    line 209
  5. Distinctive:

    A declared no-go area is a decision: When the owner puts a repository, system, or environment off limits, the ADR states the boundary …

    line 223
  6. Very specific:

    If this ADR replaces one: set the old ADR's frontmatter to status: superseded + superseded_by: ADR-NNNN + status_date, and nothing else in …

    line 254
  7. Very specific:

    Add a row to docs/graph/decisions/README.md (the index).

    line 257
  8. Very specific:

    Add a row to grill.md §6 with the ADR's identifier.

    line 258
  9. Very specific:

    Before the status flips to accepted: the body is prose a person reads in a year.

    line 265

“Common” is measured: kept by at least 6 in 10 of the 7 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

adr-writer

ADRs (Architecture Decision Records) live in docs/graph/decisions/ and record every non-obvious technical choice. Each is a short, recorded answer to "why did we pick this?"; a design doc is a different artifact.

This skill encodes the discipline of writing them well.

When to apply this skill

  • A new dependency is being committed to (the wiki page handles the what; the ADR handles the why).
  • A framework, language, or platform is being chosen.
  • A one-way door is being opened (data model, public API contract, vendor lock-in).
  • A boundary between two services or modules is being drawn.
  • Two specialists disagreed and the orchestrator picked an option.
  • A bug post-mortem revealed an implicit decision that should have been explicit.
  • A T2 contained-lane change made a real choice among options and owes its why-record (tiers.contained-lane). Write the short form: Context, Decision, Consequences, Reversibility, the sections that carry the why. Skip Alternatives when none were weighed, and say so in one line. A contained change whose why needs every section was misclassified: hand it back for T3.
  • Anyone asks "why did we do it this way?" and the answer isn't in an existing ADR.

ADR numbering and status

ADRs are numbered monotonically: adr-NNNN-short-slug.md. Find the next free number in docs/graph/decisions/ and in the index, because a number can be taken without a file. Each number names one decision for good, because citations outlive files.

A number allocated to a decision that is owed but not yet written (a spec reserves it, or the owner has not ruled yet) gets an index row marked reserved or owed, naming who owes the body. The index row is the whole reservation, because a stub ADR file is fiction that satisfies a linter. The reservation holds even when the decision is abandoned. Where a program keeps more than one ADR series (one per repository, say), a citation names the series as well as the number, so two ADRs with the same number cannot be confused.

Status lives in frontmatter, in the schema's lifecycle vocabulary (docs/graph/_schema.md §"Lifecycle status" is the home; read it for the meanings): status: proposed | accepted | open | deferred | hotfix | rejected | superseded | closed, always with status_date, plus the companion the value requires: owner while open | hotfix | deferred, reopen_when when deferred, status_evidence when closed, superseded_by when superseded. The body's ## Status section is a pointer to the frontmatter; a body value that disagrees is a lint failure (python3 docs/graph/status-register.py --root docs/graph), the drift the register exists to catch. An ADR is born proposed and becomes accepted when the owner ratifies it (the grill.md §6 row); the base values apply as the schema defines them: a "do nothing now" decision parked behind a named trigger is deferred with reopen_when, a choice taken under pressure that owes a proper decision is hotfix with an owner.

To replace a decision, write a new ADR with a new number and status: accepted, and set the old ADR's frontmatter to status: superseded + superseded_by: ADR-NNNN + a fresh status_date. A ratified ADR's body is never rewritten (the append-only exception in holistic-editing). Besides the supersede flip it takes one other change: when the decision stands but a fact in its context or reasons proves false, or execution reached the decision by a route the body does not describe, a dated ## Correction is appended after the body, stating the corrected fact with its evidence and a pointer, and the text above it stays as written. When the decision itself moves, supersede it. To see what is still owed: python3 docs/graph/status-register.py --by-kind adr --open --hotfix (add --deferred for parked decisions). The register is the query surface; the index at docs/graph/decisions/README.md is the human table.

The template (4 sections that matter most)

Use docs/graph/templates/adr.template.md. Its frontmatter is the status home (above); the four body sections that earn their keep:

Context

What is the situation that forces a decision? Include the constraint that makes "do nothing" not viable. Cross-link to grill.md and the relevant spec.

Bad: "We need a database."

Good: "Spec SPEC-0003 requires submissions to persist across restarts. Grill.md §4 caps p95 latency at 200ms and cost at $X/month. The current implementation uses an in-memory map, which loses state on restart. We need to choose a persistent store."

When the owner states how an external arrangement works (a partner handles a class of requests, a contract covers a duty), record it as a declaration attributed to them, in their words where they matter, and name what it leaves open. Conclusions drawn from it, legal ones above all, are agent.legal's to reach against its corpus; until then the ADR records the question as owed.

Decision

What we have decided, in one sentence. Optionally a short paragraph naming the central tradeoff. A sentence that holds several decisions is several ADRs: split it.

Bad: "We'll use PostgreSQL."

Good: "We will persist submissions in PostgreSQL 16, using the managed instance on platform X, accepting an additional ~$45/mo in exchange for ACID guarantees and ecosystem maturity over the in-memory alternative."

Consequences

What changes downstream. Be concrete:

  • New constraints (e.g. "migrations now belong in migrations/ and run on deploy").
  • Migration or rewrite cost if reversed.
  • Effect on the verification plan (new gates, integration tests against a test database).
  • Effect on the wiki (new library to wikify: the database driver and the migration tool).

Alternatives considered

For each rejected alternative: one paragraph naming the alternative and the concrete reason it lost, because "not as good" tells the next agent nothing.

Bad: "SQLite — not as good for our use case."

Good: "SQLite — rejected because grill.md §4 requires concurrent writes from multiple workers; SQLite serializes them and would violate the 200ms p95 budget at the projected request rate."

Reversibility tag

Every ADR tags reversibility as one of:

  • reversible: can be changed in a single session without data migration.
  • expensive: can be changed but requires a multi-day project.
  • one-way: changing it later requires a rewrite or a migration on live data.

The tag tells the next agent how much weight the decision carries. one-way ADRs get extra scrutiny. They are the ones where the "alternatives considered" section earns its keep: the next agent needs to understand why the alternative was rejected, not just that it was.

What counts as a decision

An ADR records a choice that was made. An implementation detail reconstructed from source is an observation: record it where observations live (a node, a runbook) with its rationale marked "not recorded". A survey that turns up no genuine decisions leaves the index empty and says so, because the next agent trusts every ADR it finds, and a fabricated one is worse than a missing one.

Four decisions people forget to record, because they feel like inaction:

  • "Do nothing now" is a decision. Ratifying a destination while taking no code yet, deferring the first increment behind a named, checkable trigger, is an ADR. Separate the destination (which end-state is correct) from the timing (what licenses starting), and state what makes waiting safe ("the drift is now tested, not invisible"). Its frontmatter is status: deferred with reopen_when: naming that trigger, so the register lists it among what is parked.
  • The asymmetric cost of being wrong is often the whole rationale. Record what being wrong costs in each direction ("wrong on X risks an irreversible incident; wrong on Y costs a bounded, recoverable delay") and let the asymmetry decide, rather than arguing which option is abstractly "best".
  • Declining a fix is a decision. When the owner accepts a residual risk instead of remediating it, the ADR says plainly that nothing is remediated by it; what it changes is the finding's standing, not the measurement. It separates exactly what is declined from what is not. An implemented-but-unverified control is a different finding from an unremediated one; name which each item is, because a record that blurs the two misleads both ways. It names the residual in plain terms and its register row, lists any compensating control with what that control does not cover, and quotes the owner's constraints and the attesting person. It states the revisit trigger, or states that there is none; a qualifier like "for now" in the acceptance is kept, because it makes the standing expire. What a declined fix is in the register, and when a standing acceptance becomes a deviation node, is method.incident-posture §6, its one home; link it.
  • A declared no-go area is a decision. When the owner puts a repository, system, or environment off limits, the ADR states the boundary exactly: which commands are barred (read-only ones included, if the owner said so), and which nearby configuration that lives elsewhere stays in scope. A contradiction seen inside the boundary before it closed is recorded not recorded; the owner's boundary outranks settling it with a probe. The consequences say that the graph's facts about the area are frozen at their last pin and must be presented as historical. Its standing form is a deviation node (docs/graph/nodes/_deviation.template.md).

Workflow

  1. Find the next free number. Read the most recent few ADRs to match the project's tone.
  2. Copy docs/graph/templates/adr.template.md to docs/graph/decisions/adr-NNNN-<slug>.md, and fill the frontmatter: status: proposed (or accepted when the owner has already ratified), status_date, and the companion the value requires. Leave the body ## Status as the pointer it is.
  3. Fill Context first. If you can't write the context, you don't yet know what decision you're making.
  4. Fill Decision in one sentence. If you can't, the decision isn't yet made; back up to research or brainstorm.
  5. Fill Consequences concretely, with file paths and budget impacts where applicable.
  6. Fill Alternatives considered: at least one alternative, usually two or three, each with a concrete rejection reason.
  7. Fill Reversibility and, if expensive or one-way, name the cost in concrete terms.
  8. Cross-link: spec, grill.md, wiki pages, external sources.
  9. If this ADR replaces one: set the old ADR's frontmatter to status: superseded + superseded_by: ADR-NNNN + status_date, and nothing else in it.
  10. Add a row to docs/graph/decisions/README.md (the index).
  11. Add a row to grill.md §6 with the ADR's identifier; when the owner ratifies, flip the frontmatter to accepted with a fresh status_date. If what shipped departs from a proposed ADR, the as-written text is not what gets flipped to accepted: record the difference between as-written and as-shipped next to it (its index row) and owe the amendment to the ADR's author. The close-out leaves the body as written.
  12. Before the status flips to accepted: the body is prose a person reads in a year. Apply docs/graph/skills/humanizer.md in file mode and run python3 docs/graph/prose-lint.py --file <adr> --against HEAD; a strong tell or a dropped number, heading, or code span blocks the flip.

Reference files

  • docs/graph/templates/adr.template.md: the template.
  • docs/graph/_schema.md §"Lifecycle status": the vocabulary and its companions; docs/graph/status-register.py: its lint and query.
  • docs/graph/agents/01-architect.md: the agent that primarily writes ADRs.
  • docs/graph/templates/docs/ + decisions/README.md: the index template installed at docs/graph/decisions/README.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.