Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .claude/skills/dld-audit-auto/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Shared scripts:
Skill-specific scripts:
```
.claude/skills/dld-audit/scripts/find-annotations.sh
.claude/skills/dld-audit/scripts/find-missing-amends.sh
.claude/skills/dld-audit/scripts/update-audit-state.sh
```

Expand Down Expand Up @@ -57,6 +58,7 @@ Check for all drift categories:
3. **Stale references in decisions** — frontmatter references to files that no longer exist
4. **Unreferenced code changes** — annotated files modified since last audit (if previous audit state exists)
5. **Decisions without annotations** — accepted decisions with code references but no corresponding annotations in code
6. **Missing amendment relationships** — decisions whose body references modifying part of a previous decision but have an empty `amends` field

For check (4), if `decisions/.dld-state.yaml` exists with an `audit.commit_hash`, first verify reachability:
```bash
Expand All @@ -76,6 +78,10 @@ Apply fixes for each issue category. Use judgment on what can be safely fixed au

**Annotations referencing superseded decisions** — Update the annotation to reference the superseding decision (read the `supersedes` field of the newer decision to find the chain). For deprecated decisions, remove the annotation.

**Annotations referencing amended decisions** — Do **not** rewrite or remove these annotations. The original decision is still active. Instead, note the amendment relationship in the PR description so reviewers can verify the code aligns with the amendment.

**Missing amendment relationships** — Run `bash .claude/skills/dld-audit/scripts/find-missing-amends.sh` to get candidates. For each candidate, read the source decision's body and determine if it describes a partial modification. If so, add the referenced ID to the `amends` field. Flag prominently in the PR for review, since this is an inferred relationship.

**Decisions without annotations** — If an accepted decision has code references but no annotations, and the referenced files exist, add the missing `@decision(DL-NNN)` annotations to the referenced code locations.

### Best-effort fixes (apply, but flag prominently for review):
Expand Down
22 changes: 21 additions & 1 deletion .claude/skills/dld-audit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Shared scripts:
Skill-specific scripts:
```
.claude/skills/dld-audit/scripts/find-annotations.sh
.claude/skills/dld-audit/scripts/find-missing-amends.sh
.claude/skills/dld-audit/scripts/update-audit-state.sh
```

Expand Down Expand Up @@ -55,6 +56,10 @@ Annotations in code that reference non-existent decision IDs. These indicate dec

Annotations referencing decisions with status `deprecated` or `superseded`. Code is still tied to a decision that's no longer active.

#### b2) Annotations referencing amended decisions

Annotations referencing decisions that have been amended by a newer decision (check all decisions for `amends` fields that reference this ID). This is **informational, not an error** — the original decision is still active, but the developer should be aware of the amendment. Surface these as notes, not issues.

#### c) Stale references in decisions

Decision records whose `references` list code paths that no longer exist in the repository. Use file existence checks.
Expand All @@ -80,7 +85,19 @@ git diff --name-only <commit_hash>..HEAD

Cross-reference this list with annotated files. Files that changed but whose associated decisions weren't updated may indicate undocumented drift.

#### e) Decisions without annotations
#### e) Missing amendment relationships

**This check is mandatory — do not skip it.** Run the find-missing-amends script to get initial candidates:

```bash
bash .claude/skills/dld-audit/scripts/find-missing-amends.sh
```

This outputs lines in the format `<source-id>:<referenced-id>` — decisions whose body references another decision ID that isn't listed in their `supersedes` or `amends` fields. Not every candidate is a missing amendment — some are just informational references (e.g., "this is similar to DL-005").

For each candidate, read the source decision's body and evaluate whether the reference describes a partial modification of the referenced decision. Look for language like: "supersedes the X portions of", "changes the Y behavior from DL-Z", "replaces the approach in DL-Z for...", "modifies how DL-Z handles...". If so, flag it as a missing amendment.

#### f) Decisions without annotations

`accepted` decisions that have code references in their frontmatter but no corresponding `@decision` annotations found in the code. The references claim code is linked, but the annotations are missing.

Expand All @@ -102,6 +119,9 @@ Present findings grouped by severity:
#### Deprecated/Superseded References
- `src/auth/login.ts:15` references `DL-003` (status: superseded by DL-012)

#### Amended Decisions (informational)
- `src/billing/vat.ts:42` references `DL-003` — amended by DL-012. Verify code aligns with the amendment.

#### Modified Annotated Files (since last audit)
- `src/billing/vat.ts` — modified, contains `@decision(DL-012)`. Review if decision needs updating.

Expand Down
55 changes: 55 additions & 0 deletions .claude/skills/dld-audit/scripts/find-missing-amends.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Find decisions that reference other decision IDs in their body
# but don't list them in supersedes or amends.
# Output: one line per candidate in the format: <source-id>:<referenced-id>
# Outputs nothing (exit 0) if no candidates found.
# These are candidates — the agent must evaluate whether the reference
# is actually a partial modification or just informational.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../dld-common/scripts/common.sh"

RECORDS_DIR="$(get_records_dir)"

# Find all decision files
shopt -s nullglob
files=("$RECORDS_DIR"/DL-*.md "$RECORDS_DIR"/*/DL-*.md)
shopt -u nullglob

if [[ ${#files[@]} -eq 0 ]]; then
exit 0
fi

for file in "${files[@]}"; do
id=$(basename "$file" .md)

# Extract supersedes and amends from frontmatter (between --- markers)
frontmatter=$(sed -n '1,/^---$/{ /^---$/d; p; }; /^---$/,/^---$/{ /^---$/d; p; }' "$file" | head -50)
declared=$(echo "$frontmatter" | grep -E '^(supersedes|amends):' | grep -oE 'DL-[0-9]+' || true)

# Extract body (everything after the second ---)
body=$(awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}' "$file")

# Find DL-IDs referenced in the body
body_refs=$(echo "$body" | grep -oE 'DL-[0-9]+' | sort -u || true)

if [[ -z "$body_refs" ]]; then
continue
fi

for ref in $body_refs; do
# Skip self-references
if [[ "$ref" == "$id" ]]; then
continue
fi

# Skip if already declared in supersedes or amends
if echo "$declared" | grep -qF "$ref" 2>/dev/null; then
continue
fi

echo "$id:$ref"
done
done
9 changes: 6 additions & 3 deletions .claude/skills/dld-decide/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ Good reasons to ask:
Scan existing decision files for potential relationships:
- Decisions that reference the same code paths
- Decisions with overlapping tags
- Decisions that this one might supersede
- Decisions that this one might supersede or amend

If you find related decisions, mention them and ask whether this decision supersedes any of them.
If you find related decisions, mention them and ask whether this decision **supersedes** (fully replaces) or **amends** (partially modifies) any of them. A superseded decision gets marked as `superseded` and is no longer active. An amended decision stays `accepted` — the amendment changes part of its scope while the rest remains in effect.

### 4. Determine namespace (namespaced projects only)

Expand All @@ -93,10 +93,11 @@ printf "## Context\n\nWhat prompted this decision.\n\n## Decision\n\nWhat was de
--namespace "billing" \
--tags "tag1, tag2" \
--supersedes "DL-003, DL-007" \
--amends "DL-005" \
--body-stdin
```

Flags `--namespace`, `--tags`, `--supersedes` are optional. The script creates the file with YAML frontmatter and the body content, and outputs the file path.
Flags `--namespace`, `--tags`, `--supersedes`, `--amends` are optional. The script creates the file with YAML frontmatter and the body content, and outputs the file path.

> **Note:** If the body contains literal `%` characters, escape them as `%%` (printf format string requirement).

Expand All @@ -105,6 +106,8 @@ If this decision supersedes others, also update their status:
bash .claude/skills/dld-common/scripts/update-status.sh DL-003 superseded
```

**Do not** update the status of amended decisions — they stay `accepted`.

### 7. Regenerate INDEX.md

```bash
Expand Down
14 changes: 13 additions & 1 deletion .claude/skills/dld-decide/scripts/create-decision.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Create a decision record file.
# Usage: create-decision.sh --id <DL-NNN> --title <title> [--namespace <ns>] [--tags <t1,t2>] [--supersedes <DL-X,DL-Y>] [--body-stdin]
# Usage: create-decision.sh --id <DL-NNN> --title <title> [--namespace <ns>] [--tags <t1,t2>] [--supersedes <DL-X,DL-Y>] [--amends <DL-X,DL-Y>] [--body-stdin]
# All flags except --id and --title are optional.
# --body-stdin reads the markdown body (Context, Decision, Rationale, Consequences sections) from stdin.

Expand All @@ -14,6 +14,7 @@ TITLE=""
NAMESPACE=""
TAGS=""
SUPERSEDES=""
AMENDS=""
BODY=""
READ_STDIN=false

Expand All @@ -24,6 +25,7 @@ while [[ $# -gt 0 ]]; do
--namespace) NAMESPACE="$2"; shift 2 ;;
--tags) TAGS="$2"; shift 2 ;;
--supersedes) SUPERSEDES="$2"; shift 2 ;;
--amends) AMENDS="$2"; shift 2 ;;
--body-stdin) READ_STDIN=true; shift ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
Expand Down Expand Up @@ -76,13 +78,23 @@ format_supersedes() {
fi
}

# Format amends as YAML inline array
format_amends() {
if [[ -z "$1" ]]; then
echo "[]"
else
echo "[$1]"
fi
}

{
echo "---"
echo "id: $ID"
echo "title: \"$TITLE\""
echo "timestamp: $TIMESTAMP"
echo "status: proposed"
echo "supersedes: $(format_supersedes "$SUPERSEDES")"
echo "amends: $(format_amends "$AMENDS")"
if [[ "$MODE" == "namespaced" && -n "$NAMESPACE" ]]; then
echo "namespace: $NAMESPACE"
fi
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dld-implement/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Read each decision record carefully. Understand:
- What was decided
- The rationale and constraints
- The code areas referenced
- Any superseded decisions (read those too for context on what changed)
- Any superseded or amended decisions (read those too for context on what changed)

### 2. Make code changes

Expand Down
5 changes: 4 additions & 1 deletion .claude/skills/dld-plan/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Once determined, also read `decisions/records/<namespace>/PRACTICES.md` if it ex
Before proposing the breakdown, scan existing decision files for:
- Decisions that reference the same code areas
- Decisions with overlapping tags or topics
- Decisions that the new feature might supersede
- Decisions that the new feature might supersede or amend

Mention any related decisions to the developer so the breakdown accounts for them.

Expand Down Expand Up @@ -102,6 +102,7 @@ printf "## Context\n\n...\n\n## Decision\n\n...\n\n## Rationale\n\n...\n\n## Con
--namespace "billing" \
--tags "payment-gateway" \
--supersedes "DL-003" \
--amends "DL-005" \
--body-stdin
```

Expand All @@ -112,6 +113,8 @@ If any decision supersedes an existing one, also update the old decision's statu
bash .claude/skills/dld-common/scripts/update-status.sh DL-003 superseded
```

**Do not** update the status of amended decisions — they stay `accepted`.

For each decision, compose a focused body. Keep it concise — the full feature context is captured across the group. Each individual decision should capture its own specific rationale.

### 7. Regenerate INDEX.md
Expand Down
10 changes: 7 additions & 3 deletions .claude/skills/dld-snapshot/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Write `decisions/SNAPSHOT.md` — the detailed per-decision reference.

### DL-NNN: <Title>
*Supersedes: DL-XXX* ← only if applicable
*Amends: DL-XXX* ← only if applicable
*Amended by: DL-YYY* ← only if another decision amends this one

<The Decision section from the record — what was decided. Copy verbatim or lightly condense.>

Expand All @@ -84,6 +86,7 @@ Write `decisions/SNAPSHOT.md` — the detailed per-decision reference.
- Only include `accepted` decisions
- Skip `superseded`, `deprecated`, and `proposed` decisions entirely
- When a decision supersedes another (check the `supersedes` field in the YAML frontmatter), include a `*Supersedes: DL-XXX*` note
- When a decision amends another (check the `amends` field), include an `*Amends: DL-XXX*` note on the amending decision and an `*Amended by: DL-YYY*` note on the original decision
- The **Decision** section content should be copied directly or lightly condensed — this is the authoritative statement
- The **Rationale** should be condensed to 1-3 sentences — enough to understand *why*, not every detail
- **Code** references should list paths and symbols from the frontmatter `references` field
Expand Down Expand Up @@ -135,12 +138,13 @@ sequenceDiagram

## Decision Relationships

<Only include this section if there are supersession chains or closely related decision groups.
If no decisions supersede others and there are no meaningful relationship clusters, omit this section entirely.>
<Only include this section if there are supersession chains, amendment relationships, or closely related decision groups.
If no decisions supersede or amend others and there are no meaningful relationship clusters, omit this section entirely.>

```mermaid
graph LR
DL-003 -->|superseded by| DL-012
DL-012 -.->|amends| DL-005
DL-012 -.->|related| DL-014
```

Expand All @@ -160,7 +164,7 @@ belong to a single area. E.g., "PostgreSQL for primary data store (DL-001)",
- **Include Mermaid diagrams where they add clarity.** Good candidates:
- Architecture/component diagrams when decisions span multiple components
- Sequence diagrams when decisions describe flows or protocols
- Relationship diagrams when there are supersession chains
- Relationship diagrams when there are supersession chains or amendment relationships
- Don't force diagrams if the decisions are simple or unrelated
- **Keep it proportional.** A project with 5 decisions needs a short overview. A project with 50 needs more structure. Scale the document to the content.
- **Group by namespace (namespaced projects) or by domain theme (flat projects).** For flat projects, identify natural domain groupings from tags and decision content.
Expand Down
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ Scripts live in `skills/<skill>/scripts/` (tessl) and `.claude/skills/<skill>/sc

Scripts use `set -euo pipefail` and source `common.sh` via `BASH_SOURCE` path resolution.

## Testing

Tests use [bats-core](https://github.com/bats-core/bats-core) installed as a git submodule at `tests/bats/`. Run tests with:

```bash
tests/bats/bin/bats tests/
```

If tests fail with "Could not find bats-support", init submodules first: `git submodule update --init --recursive`

## Conventions

- Commit messages: concise, no buzzwords
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ title: "Use exponential backoff for payment gateway retries"
timestamp: 2026-02-15T09:20:00Z
status: accepted
supersedes: [DL-002]
amends: []
tags: [payments, resilience]
references:
- path: src/payments/gateway.ts
Expand Down Expand Up @@ -270,7 +271,7 @@ This project uses Decision-Linked Development. Decision records (DL-*.md) live i

- When you encounter `@decision(DL-XXX)` annotations in code, use `/dld-lookup DL-XXX` to read the referenced decision BEFORE modifying the annotated code.
- ALWAYS look up and verify related decisions before modifying annotated code. Do not skip this step.
- NEVER modify code in a way that contradicts an existing decision without first confirming with the user. If the change requires breaking a previous decision, a new decision must be recorded (via `/dld-decide`) that explicitly supersedes the old one.
- NEVER modify code in a way that contradicts an existing decision without first confirming with the user. If the change requires breaking a previous decision, a new decision must be recorded (via `/dld-decide`) that explicitly supersedes the old one. If it only partially modifies a previous decision, record it as an amendment instead.
- Use `/dld-decide` to record new decisions
- Use `/dld-implement` to implement proposed decisions
- Use `/dld-lookup` to query decisions by ID, tag, or code path
Expand Down
3 changes: 2 additions & 1 deletion docs/concept/dld-concept.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ Building on the established terminology and concepts from Architecture Decision
- **Decision** — what was decided
- **Rationale** — why this choice over alternatives
- **Consequences** — what becomes easier or harder
- **Supersedes** — references to previous decisions that this one replaces. A single new decision may supersede one or several older decisions.
- **Supersedes** — references to previous decisions that this one fully replaces. A single new decision may supersede one or several older decisions.
- **Amends** — references to previous decisions that this one partially modifies. Unlike supersession, amended decisions stay active — the amendment changes part of the original's scope while the rest remains in effect.
- **Code references** — explicit links to the areas of the codebase this decision affects

By aligning with ADR conventions and terminology, DLD lowers the adoption barrier for teams already familiar with architectural decision records, while extending the concept to cover not just architectural but also functional, product, and implementation-level decisions.
Expand Down
2 changes: 1 addition & 1 deletion docs/concept/dld-faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Over time, you can expand coverage. You can also use AI to help bootstrap: have

**What if a decision turns out to be wrong?**

You don't edit or delete the original decision. You create a new decision that supersedes it, explaining why the previous approach is no longer valid. This preserves the full history — which is valuable both for understanding how the system evolved and for preventing the same mistake from being repeated.
You don't edit or delete the original decision. If the original decision is entirely wrong, you create a new decision that supersedes it, explaining why the previous approach is no longer valid. If only part of it needs to change, you create a new decision that *amends* it — the original stays active, and the amendment clarifies what changed. This preserves the full history — which is valuable both for understanding how the system evolved and for preventing the same mistake from being repeated.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/concept/dld-tldr.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ In practice, `@decision(DL-XXX)` annotations on methods and classes act as mecha

Three additional design choices support this:

1. **The decision log is append-only.** Decisions can supersede previous ones — including multiple at once — but the content (reasoning and intent) is never rewritten. Metadata like `status` and `references` can be updated mechanically (e.g., after code refactors). This creates a complete timeline of how the system evolved, borrowing directly from event sourcing.
1. **The decision log is append-only.** Decisions can supersede (fully replace) or amend (partially modify) previous ones — but the content (reasoning and intent) is never rewritten. Metadata like `status` and `references` can be updated mechanically (e.g., after code refactors). This creates a complete timeline of how the system evolved, borrowing directly from event sourcing.

2. **The spec is a generated projection**, not a manually maintained document. Just like event sourcing builds read models from event streams, an LLM periodically generates a consolidated "current state" snapshot from the decision log. Humans never maintain the spec — only the individual decisions.

Expand Down
Loading
Loading