
Adr Writing
- 231 installs
- 74 repo stars
- Updated July 21, 2026
- existential-birds/beagle
Author Architecture Decision Records that capture context, options, and consequences while shipping features in active codebases.
About
adr-writing from existential-birds/beagle teaches agents to draft Architecture Decision Records with context, considered options, decision rationale, and consequences so teams preserve why choices were made.
- decision templates
- tradeoff capture
- docs/adr structure
- team alignment records
Adr Writing by the numbers
- 231 all-time installs (skills.sh)
- Ranked #493 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/existential-birds/beagle --skill adr-writingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 231 |
|---|---|
| repo stars | ★ 74 |
| Last updated | July 21, 2026 |
| Repository | existential-birds/beagle ↗ |
What it does
Author Architecture Decision Records that capture context, options, and consequences while shipping features in active codebases.
Files
ADR Writing
Overview
Generate Architectural Decision Records (ADRs) following the MADR template with systematic completeness checking.
Quick Reference
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ SEQUENCE │ ──▶ │ EXPLORE │ ──▶ │ FILL │
│ (get next │ │ (context, │ │ (template │
│ number) │ │ ADRs) │ │ sections) │
└─────────────┘ └──────────────┘ └─────────────┘
│ │
│ ▼
│ ┌─────────────┐
│ │ VERIFY │
│ │ (DoD │
└─────────────────────────────────│ checklist)│
└─────────────┘When To Use
- Documenting architectural decisions from extracted requirements
- Converting meeting notes or discussions to formal ADRs
- Recording technical choices from PR discussions
- Creating decision records from design documents
Workflow
Gates (objective pass conditions)
Advance to the next step only when the pass condition holds. These replace “I explored” / “I verified” with checkable artifacts.
| After | Pass condition |
|---|---|
| Step 2 | Pass: You have a written list (bullets in draft preamble, scratch notes, or the ADR body) of ≥0 paths under docs/adrs/ you consulted for related/superseded ADRs, or you explicitly record that docs/adrs/ is missing or empty after checking. And you list ≥1 repo path for related code or N/A with one-line reason. |
| Step 5 | Pass: For each E, C, A, D, R in references/definition-of-done.md, the draft either meets that letter’s checklist or contains an [INVESTIGATE: …] marker scoped to that gap. |
| Step 7 | Pass: The ADR file exists at docs/adrs/NNNN-slugified-title.md, and a read of the file shows line 1 is --- and frontmatter parses as YAML. |
Step 1: Get Sequence Number
If a number was pre-assigned (e.g., when called from /beagle:write-adr with parallel writes):
- Use the pre-assigned number directly
- Do NOT call the script - this prevents duplicate numbers in parallel execution
If no number was pre-assigned (standalone use):
python scripts/next_adr_number.pyThis outputs the next available ADR number (e.g., 0003).
For parallel allocation (used by parent commands):
python scripts/next_adr_number.py --count 3
# Outputs: 0003, 0004, 0005 (one per line)Step 2: Explore Context
Before writing, gather additional context:
1. Related code - Find implementations affected by this decision 2. Existing ADRs - Check docs/adrs/ for related or superseded decisions 3. Discussion sources - PRs, issues, or documents referenced in decision
Gate: Meet the Step 2 row in Gates (objective pass conditions) before Step 3.
Step 3: Load Template
Load references/madr-template.md for the official MADR structure.
Step 4: Fill Sections
Populate each section from your decision data:
| Section | Source |
|---|---|
| Title | Decision summary (imperative mood) |
| Status | Always draft initially |
| Context | Problem statement, constraints |
| Decision Drivers | Prioritized requirements |
| Considered Options | All viable alternatives |
| Decision Outcome | Chosen option with rationale |
| Consequences | Good, bad, neutral impacts |
Step 5: Apply Definition of Done
Load references/definition-of-done.md and verify E.C.A.D.R. criteria:
- Explicit problem statement
- Comprehensive options analysis
- Actionable decision
- Documented consequences
- Reviewable by stakeholders
Gate: Meet the Step 5 row in Gates (objective pass conditions) before Step 6 (use [INVESTIGATE: …] where data is missing).
Step 6: Mark Gaps
For sections that cannot be filled from available data, insert investigation prompts:
* [INVESTIGATE: Review PR #42 discussion for additional drivers]
* [INVESTIGATE: Confirm with security team on compliance requirements]
* [INVESTIGATE: Benchmark performance of Option 2 vs Option 3]These prompts signal incomplete sections for later follow-up.
Step 7: Write File
IMPORTANT: Every ADR MUST start with YAML frontmatter.
The frontmatter block is REQUIRED and must include at minimum:
---
status: draft
date: YYYY-MM-DD
---Full frontmatter template:
---
status: draft
date: 2024-01-15
decision-makers: [alice, bob]
consulted: []
informed: []
---Validation: Before writing the file, verify the content starts with --- followed by valid YAML frontmatter. If frontmatter is missing, add it before writing.
Gate: After write, meet the Step 7 row in Gates (objective pass conditions) (file on disk, YAML frontmatter present).
Save to docs/adrs/NNNN-slugified-title.md:
docs/adrs/0003-use-postgresql-for-user-data.md
docs/adrs/0004-adopt-event-sourcing-pattern.md
docs/adrs/0005-migrate-to-kubernetes.mdStep 8: Verify Frontmatter
After writing, confirm the file: 1. Starts with --- on the first line 2. Contains status: draft (or other valid status) 3. Contains date: YYYY-MM-DD with actual date 4. Ends frontmatter with --- before the title
File Naming Convention
Format: NNNN-slugified-title.md
| Component | Rule |
|---|---|
NNNN | Zero-padded sequence number from script |
- | Separator |
slugified-title | Lowercase, hyphens, no special characters |
.md | Markdown extension |
Reference Files
references/madr-template.md- Official MADR template structurereferences/definition-of-done.md- E.C.A.D.R. quality criteria
Output Example
---
status: draft
date: 2024-01-15
decision-makers: [alice, bob]
---
# Use PostgreSQL for User Data Storage
## Context and Problem Statement
We need a database for user account data...
## Decision Drivers
* Data integrity requirements
* Query flexibility needs
* [INVESTIGATE: Confirm scaling projections with infrastructure team]
## Considered Options
* PostgreSQL
* MongoDB
* CockroachDB
## Decision Outcome
Chosen option: PostgreSQL, because...
## Consequences
### Good
* ACID compliance ensures data integrity
### Bad
* Requires more upfront schema design
### Neutral
* Team has moderate PostgreSQL experienceDefinition of Done: E.C.A.D.R. Criteria
An ADR is complete when it meets all five E.C.A.D.R. criteria.
E.C.A.D.R. Checklist
E - Explicit Problem Statement
| Check | Criteria |
|---|---|
| [ ] | Context describes a real, specific problem |
| [ ] | Problem is scoped (not too broad, not too narrow) |
| [ ] | Constraints and requirements are stated |
| [ ] | Reader understands WHY a decision is needed |
Anti-patterns:
- "We need to choose a database" (too vague)
- Problem buried in decision outcome section
- Missing business or technical context
C - Comprehensive Options Analysis
| Check | Criteria |
|---|---|
| [ ] | At least 2 options considered |
| [ ] | Options are genuinely viable (not strawmen) |
| [ ] | Each option has pros AND cons listed |
| [ ] | "Do nothing" considered if applicable |
Anti-patterns:
- Single option presented as foregone conclusion
- Options listed without analysis
- Missing obvious alternatives
A - Actionable Decision
| Check | Criteria |
|---|---|
| [ ] | Chosen option is clearly stated |
| [ ] | Decision is specific enough to implement |
| [ ] | Rationale links to decision drivers |
| [ ] | No ambiguity about what was decided |
Anti-patterns:
- "We will use a modern approach" (vague)
- Decision contradicts stated constraints
- Missing implementation guidance
D - Documented Consequences
| Check | Criteria |
|---|---|
| [ ] | Good consequences listed |
| [ ] | Bad consequences listed (honest tradeoffs) |
| [ ] | Operational impacts considered |
| [ ] | Future implications noted |
Anti-patterns:
- Only positive consequences (overselling)
- Generic consequences that apply to any option
- Missing security, performance, or cost impacts
R - Reviewable by Stakeholders
| Check | Criteria |
|---|---|
| [ ] | Status is set appropriately |
| [ ] | Decision-makers are identified |
| [ ] | Language is accessible (not jargon-heavy) |
| [ ] | Sufficient context for outsiders to understand |
Anti-patterns:
- Missing metadata (date, status, authors)
- Assumes reader context not in document
- Dense technical prose without summaries
Quality Rubric
| Score | Criteria Met | Status |
|---|---|---|
| 5/5 | All E.C.A.D.R. criteria | Ready for proposed |
| 4/5 | One minor gap | Add [INVESTIGATE] prompt |
| 3/5 | Two gaps | Needs revision before proposing |
| 2/5 | Major gaps | Incomplete draft |
| 1/5 | Minimal content | Placeholder only |
Using [INVESTIGATE] Prompts
When a criterion cannot be met from available information, insert an investigation prompt:
## Decision Drivers
* Performance under 100ms response time
* [INVESTIGATE: Confirm budget constraints with finance team]
* Compatibility with existing Python stackThese prompts: 1. Signal incomplete sections 2. Document what information is missing 3. Enable async follow-up 4. Prevent premature status advancement
Status Progression
draft ──▶ proposed ──▶ accepted
│ │
│ ▼
│ rejected
│
└──▶ [fix gaps, remove INVESTIGATE prompts]Do not advance to proposed until all [INVESTIGATE] prompts are resolved.
Review Checklist
Final pass before marking proposed:
- [ ] No
[INVESTIGATE]prompts remain - [ ] All E.C.A.D.R. criteria checked
- [ ] File named correctly (
NNNN-slugified-title.md) - [ ] Frontmatter complete (status, date, decision-makers)
- [ ] Links to related ADRs if superseding/related
MADR Template
Markdown Any Decision Records (MADR) - https://adr.github.io/madr/
Template Structure
---
status: {draft | proposed | accepted | rejected | deprecated | superseded by [ADR-NNNN](NNNN-title.md)}
date: YYYY-MM-DD
decision-makers: [list of involved people]
consulted: [list of people whose opinions are sought]
informed: [list of people who are kept up-to-date]
---
# {Title: Short imperative statement of decision}
## Context and Problem Statement
{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story. You may want to articulate the problem in form of a question.}
## Decision Drivers
* {decision driver 1, e.g., a force, facing concern, ...}
* {decision driver 2, e.g., a force, facing concern, ...}
* ...
## Considered Options
* {title of option 1}
* {title of option 2}
* {title of option 3}
* ...
## Decision Outcome
Chosen option: "{title of option 1}", because {justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | ... | comes out best (see below)}.
### Consequences
* Good, because {positive consequence, e.g., improvement of one or more desired qualities, ...}
* Bad, because {negative consequence, e.g., compromising one or more desired qualities, ...}
* Neutral, because {neutral consequence, neither positive nor negative}
### Confirmation
{Describe how the implementation of/compliance with the ADR is confirmed. E.g., by a review or an ArchUnit test. Although we classify this element as optional, it is recommended to include it.}
## Pros and Cons of the Options
### {title of option 1}
{example | description | pointer to more information | ...}
* Good, because {argument a}
* Good, because {argument b}
* Neutral, because {argument c}
* Bad, because {argument d}
* ...
### {title of option 2}
{example | description | pointer to more information | ...}
* Good, because {argument a}
* Good, because {argument b}
* Neutral, because {argument c}
* Bad, because {argument d}
* ...
### {title of option 3}
{example | description | pointer to more information | ...}
* Good, because {argument a}
* Good, because {argument b}
* Neutral, because {argument c}
* Bad, because {argument d}
* ...
## More Information
{You might want to provide additional evidence/confidence for the decision outcome here and/or document the team agreement on the decision and/or define when this decision should be re-considered and/or links to other decisions and resources.}Section Guide
Status Values
| Status | Meaning |
|---|---|
draft | Initial creation, not yet reviewed |
proposed | Ready for team review |
accepted | Approved and active |
rejected | Considered but not adopted |
deprecated | No longer recommended |
superseded by [ADR-NNNN] | Replaced by newer decision |
Title
- Use imperative mood ("Use X", "Adopt Y", "Migrate to Z")
- Keep concise (5-10 words)
- Start with verb
Context and Problem Statement
- 2-4 sentences describing the situation
- Can be phrased as a question
- Include relevant constraints
Decision Drivers
- List forces influencing the decision
- Prioritize by importance
- Include both technical and business drivers
Considered Options
- Minimum 2 options (including chosen)
- Include "do nothing" if viable
- Brief titles, details in Pros/Cons section
Decision Outcome
- State chosen option clearly
- Explain why it was chosen
- Reference decision drivers it satisfies
Consequences
- Categorize as Good/Bad/Neutral
- Be honest about tradeoffs
- Include operational impacts
Optional Sections
These sections enhance completeness but may be omitted for simpler decisions:
- Confirmation - How to verify compliance
- Pros and Cons of the Options - Detailed option analysis
- More Information - Links, references, caveats
Minimal Template
For quick decisions, use this shortened form:
---
status: draft
date: YYYY-MM-DD
---
# {Title}
## Context and Problem Statement
{description}
## Decision Drivers
* {driver 1}
* {driver 2}
## Decision Outcome
Chosen option: "{option}", because {reason}.
### Consequences
* Good, because {positive}
* Bad, because {negative}#!/usr/bin/env python3
"""Get the next ADR sequence number.
Scans docs/adrs/ for existing ADRs and returns the next available number.
Usage:
python scripts/next_adr_number.py
python scripts/next_adr_number.py --dir /path/to/docs/adrs
python scripts/next_adr_number.py --count 3 # Pre-allocate 3 numbers for parallel writes
"""
import argparse
import re
import sys
from pathlib import Path
def find_adr_directory() -> Path:
"""Find the ADR directory by searching up from cwd."""
candidates = [
Path("docs/adrs"),
Path("docs/adr"),
Path("adr"),
Path("adrs"),
Path("doc/adr"),
Path("doc/adrs"),
]
# Search from current directory
cwd = Path.cwd()
for candidate in candidates:
if (cwd / candidate).is_dir():
return cwd / candidate
# Search up to git root
git_root = cwd
while git_root != git_root.parent:
if (git_root / ".git").exists():
break
git_root = git_root.parent
for candidate in candidates:
if (git_root / candidate).is_dir():
return git_root / candidate
return cwd / "docs/adrs"
def get_existing_numbers(adr_dir: Path) -> list[int]:
"""Extract ADR numbers from filenames in the directory."""
pattern = re.compile(r"^(\d{4})-.*\.md$")
numbers = []
if not adr_dir.exists():
return numbers
for file in adr_dir.iterdir():
if file.is_file():
match = pattern.match(file.name)
if match:
numbers.append(int(match.group(1)))
return sorted(numbers)
def next_number(existing: list[int]) -> int:
"""Calculate the next ADR number."""
if not existing:
return 1
return max(existing) + 1
def format_number(num: int) -> str:
"""Format number as zero-padded 4-digit string."""
return f"{num:04d}"
def main() -> int:
parser = argparse.ArgumentParser(
description="Get the next ADR sequence number"
)
parser.add_argument(
"--dir",
type=Path,
help="ADR directory (auto-detected if not specified)",
)
parser.add_argument(
"--list",
action="store_true",
help="List existing ADR numbers",
)
parser.add_argument(
"--count",
type=int,
default=1,
help="Number of sequential ADR numbers to allocate (for parallel writes)",
)
args = parser.parse_args()
adr_dir = args.dir or find_adr_directory()
existing = get_existing_numbers(adr_dir)
if args.list:
if existing:
print(f"ADR directory: {adr_dir}")
print(f"Existing ADRs: {[format_number(n) for n in existing]}")
else:
print(f"No ADRs found in {adr_dir}")
return 0
next_num = next_number(existing)
if args.count == 1:
print(format_number(next_num))
else:
# Output multiple numbers, one per line, for parallel allocation
allocated = [format_number(next_num + i) for i in range(args.count)]
print("\n".join(allocated))
return 0
if __name__ == "__main__":
sys.exit(main())