agentleFS
holgerleichsenringholgerleichsenring/specification-first-agentic-development/skills/log-decision/SKILL.md

log-decision

Skill41 starsChanged 15 days agoMITView on GitHub

Append a design decision to the active spec's YAML under decisions/. Triggers on a choice between alternatives, 'log this decision', 'record this choice', or a non-obvious design call.

  • Deletes or force-pushes

7 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

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

  • 0

    Common

    Most of the 7 edited versions we read keep these.

  • 0

    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:

    Only category and chose are required.

    line 70
  2. Very specific:

    Check which spec is currently active in .{project}/contexts/<name>/context.yaml (state.active).

    line 76
  3. Very specific:

    If it exists: read it, preserve its spec: (or run:) field and its existing decisions: entries.

    line 82
  4. Very specific:

    If it doesn't exist: create a fresh file with the schema header, the spec: (or run:) field, and an empty decisions: array ready to receive …

    line 83
  5. Very specific:

    Add the new entry to the END of the decisions: array (chronological order).

    line 91

“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

Log Decision

Append a decision entry to the spec's YAML file under .{project}/decisions/. Decisions capture the why behind choices — the code shows the what, but six months later nobody remembers the reasoning.

When to Log

Log a decision when:

  • Choosing between two or more viable approaches
  • Rejecting a common or expected pattern for a specific reason
  • Accepting a trade-off (performance vs. readability, simplicity vs. flexibility)
  • Making a security-relevant choice
  • Choosing a tool, library, or framework
  • Narrowing a spec's scope (in/out trade-offs)

Don't log:

  • Obvious choices (using the project's language, following established patterns)
  • Temporary decisions ("I'll refactor this later")
  • Style preferences already covered by coding-principles.md

Decision vs memory: a decision records a CHOICE made at a point in time (chose/over/reason) and lives in decisions/. A transferable FACT or RULE that future work consults — a ratified operator preference, a project constraint, an external pointer — is a MEMORY and lives in .{project}/memory/ instead (see the layout in /spec-first:spec-first-workflow). This skill never writes memory/, and memory writes never land in decisions/; when a decision teaches a durable lesson, distil that lesson into a separate memory entry rather than stretching the decision file.

File Convention

One YAML file per spec. Filename = <spec-or-run-id>.yaml:

SourceFilename
Spec decisionsdecisions/2026-08-24-8a3f.yaml (or decisions/p0042.yaml for a counter-era spec)
Run-attached notedecisions/r07.yaml

The file holds ALL decisions made within that spec or run. Multiple decisions for one spec = multiple entries in the same file's decisions: array. Glob decisions/p0042.yaml to see every decision for that spec; cat decisions/*.yaml to dump everything.

YAML Shape

# yaml-language-server: $schema=../decision.schema.json
spec: p0042                # OR: run: r07   (exactly one — must match filename)

decisions:
  - category: Architecture
    chose: "<one-line summary of what was picked>"
    over: "<one-line summary of the obvious rejected alternative>"   # optional
    reason: |
      Multi-line explanation. Cite constraints, prior incidents,
      related decisions via [[other-spec-id]].
    alternatives:           # optional
      - "<one-line — what else was considered, why rejected>"

  - category: TradeOff
    chose: "..."
    reason: |
      ...
CategoryUse when
ArchitectureStructural decisions: patterns, layers, module boundaries
ToolingLibrary, framework, or tool choices
ImplementationHow something is built (algorithm, data structure, approach)
TradeOffAccepting a limitation for a specific benefit
SecuritySecurity-relevant choices
ScopeIn/out trade-offs at spec boundaries

Only category and chose are required. over, reason, alternatives are optional — but encouraged for non-trivial decisions.

Steps

1. Identify the spec or run

Check which spec is currently active in .{project}/contexts/<name>/context.yaml (state.active). If multiple contexts have active specs, ask the user which one this decision belongs to. If no spec is active, ask whether the decision attaches to a spec ID (planned/done) or to a run ID.

2. Read or create the spec decision file

Target path: .{project}/decisions/<spec-id>.yaml.

  • If it exists: read it, preserve its spec: (or run:) field and its existing decisions: entries.
  • If it doesn't exist: create a fresh file with the schema header, the spec: (or run:) field, and an empty decisions: array ready to receive the new entry.

3. Compose the new entry

Author the entry following the shape above. Lead with category and chose:. Add over:, reason:, and alternatives: only when they apply.

4. Append to the file

Add the new entry to the END of the decisions: array (chronological order). Do not rewrite or reorder existing entries.

5. Confirm

Show the user the new entry and the file path. If they want to refine the wording, edit the file.

Examples

decisions/p0042.yaml (after two decisions logged in spec p0042):

# yaml-language-server: $schema=../decision.schema.json
spec: p0042

decisions:
  - category: Architecture
    chose: "Adapter pattern for ticket providers"
    over: "Direct SDK coupling per provider"
    reason: |
      Lets GitHub/Jira/AzureDevOps swap behind one ITicketProvider without
      touching application logic. Each SDK surface stays isolated in
      Infrastructure; the rest of the codebase sees only the contract.
    alternatives:
      - "Repository pattern — rejected, overkill for read-mostly ticket data"

  - category: TradeOff
    chose: "Raw HttpClient for GitHub API"
    over: "Octokit SDK"
    reason: |
      Octokit adds 3 MB of transitive deps for 4 API calls. Raw HttpClient
      with a small typed wrapper is leaner and the responses are stable JSON.

decisions/r07.yaml (a run-attached decision file):

# yaml-language-server: $schema=../decision.schema.json
run: r07

decisions:
  - category: Implementation
    chose: "Rollback the last 3 commits via revert, not reset"
    over: "git reset --hard HEAD~3 + force push"
    reason: |
      The branch was already pushed and other operators had pulled it.
      Force-pushing would have orphaned their work. Three explicit revert
      commits preserve history and let downstream branches merge cleanly.

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.