Decision Record
Intro
A DecisionRecord captures a single consequential choice: what was decided, why, what alternatives were rejected, and what consequences are expected. This is the classic Architecture Decision Record (ADR) pattern, promoted to a first-class primitive in processkit.
MCP server. This skill ships a self-contained MCP server at
mcp/server.py(PEP 723 script — requiresuvand Python ≥ 3.10 on PATH). Agent harnesses reach its tools by reading a single MCP config file at startup, so the contents ofmcp/mcp-config.jsonmust be merged into the harness's MCP config and placed at the harness-specific path before this skill is usable. If processkit was installed by an installer, that wiring is the installer's responsibility; if processkit was installed manually, the project owner must do it by hand.
Overview
When to record a decision
Record a decision when the choice:
- Has consequences someone will ask about in six months ("why are we using X?").
- Rules out real alternatives that took thought to reject.
- Affects more than one file, component, or person.
- Is the kind of thing that "we decided in a meeting" but nobody wrote down.
Don't record: trivial choices made inside a single function, preference calls with no downside, decisions already documented somewhere authoritative.
Shape
---
apiVersion: processkit.projectious.work/v2
kind: DecisionRecord
metadata:
id: DEC-steady-river
created: 2026-04-06T00:00:00Z
spec:
title: "Use official Python MCP SDK with uv PEP 723 inline dependencies"
state: accepted
context: "Skill MCP servers need a runtime. Options vary from zero-dep stdlib to full SDK."
decision: "Use the official mcp Python SDK, distributed as standalone scripts with PEP 723 inline deps, launched via uv."
rationale: |
- The SDK is the standard; rolling our own JSON-RPC means maintaining protocol code.
- PEP 723 + uv eliminates per-skill env setup; uv caching amortizes first-run cost.
- Container already has Python ≥3.10 and uv — no base image change.
alternatives:
- option: "Raw JSON-RPC with zero dependencies"
rejected_because: "Reimplements the protocol, fragile for complex servers."
- option: "Pydantic-only minimal server"
rejected_because: "Kept as escape hatch if container size becomes critical; not default."
consequences: |
- +300-400 MB image size per skill with MCP server.
- First-run 5-10s for uv resolution; cached thereafter.
- Escape hatch documented if size becomes an issue.
deciders: [ACTOR-owner, ACTOR-claude]
decided_at: 2026-04-05T00:00:00Z
---
Optional body: longer narrative, links to the discussion, related research.
Workflow
- Pick an ID:
DEC-<generated-id>. - Write a declarative
title— state the decision itself, not a question. - Fill in
context(what prompted this),decision(the chosen option),rationale(why). - List
alternativeswith explicitrejected_because— this is the part future-you will thank present-you for. - Write
consequences— what follows from this decision, positive and negative. - Set
state: proposedif still under discussion,acceptedif finalized. - Save to
context/decisions/. - Log
decision.proposedordecision.accepted.
Superseding
When a decision is replaced by a later one:
- In the new DecisionRecord, set
spec.supersedes: DEC-<old-id>. - In the old DecisionRecord, set
spec.state: supersededandspec.superseded_by: DEC-<new-id>. - Log
decision.supersededreferencing both IDs.
Never delete or edit the old record. Its existence is why we can trace the evolution of the decision.
This skill also provides the /decision-record-write slash command for direct invocation — see commands/decision-record-write.md. This skill also provides the /decision-record-query slash command for direct invocation — see commands/decision-record-query.md.
Gotchas
Agent-specific failure modes — provider-neutral pause-and-self-check items:
- Recording a decision before it has actually been made. A proposal in a discussion is not a DecisionRecord. Open a Discussion if reasoning is still in flight; only record when the choice is settled. Recording too early creates a fictional history.
- Capturing WHAT but not WHY. A DecisionRecord without rationale is just a workitem in the wrong directory. The whole value of the ADR pattern is the reasoning trail — what was considered, why this was picked, what was rejected.
- Omitting the rejected alternatives. "We decided X" is half a decision; "We picked X over Y and Z because …" is a decision. Future-you needs the alternatives to know whether the call still holds when context changes.
- Editing an old DecisionRecord instead of superseding it.
Decisions are immutable history. If the call needs to change, write
a NEW DecisionRecord with
supersedes:pointing at the old one and set the old one's state tosuperseded. Never overwrite — the old reasoning is part of the audit trail. - Recording trivial choices as DecisionRecords. A DecisionRecord is overhead; reserve it for choices a future agent or human will ask about in six months. Variable naming inside one function is not a DecisionRecord.
- Putting the rationale in the Discussion body instead of the DecisionRecord. Discussions are pre-decisional reasoning; DecisionRecords are post-decisional summary. The rationale must be copied (not linked) into the DecisionRecord so the latter is self-contained when read in isolation.
- Hallucinating decision history when the user asks "what did we
decide about X". Run
query_decisionsandsearch_entitiesagainst the actual store before answering. Do not synthesize an answer from memory; it will be wrong.
Full reference
State machine
See src/primitives/state-machines/decisionrecord.yaml:
proposed → accepted → superseded (terminal)
↓
rejected (terminal)
rejected is for decisions considered and declined — kept for history.
superseded is for decisions replaced by a later one.
Full field list
See src/primitives/schemas/decisionrecord.yaml. Notable fields:
deciders: list of Actor IDs. Who agreed.supersedes/superseded_by: DEC IDs. Replacement chain.related_workitems: BACK IDs. Work that prompted or implements the decision.decided_at: when the decision was finalized. Distinct frommetadata.created(when the file was written).
Distinguishing decisions from discussions
A Discussion (separate primitive) is a multi-turn conversation exploring a question. A DecisionRecord is the crisp outcome. Workflow:
- Open a Discussion when a question arises.
- Explore, debate, research.
- When the group converges on an answer, write a DecisionRecord.
- Link the Discussion via
spec.related_discussions(cross-reference).
Not every discussion produces a decision; not every decision is preceded by a discussion.
Writing good titles
Good titles are declarative, active, and concrete:
- ✓ "Use official Python MCP SDK with uv PEP 723 inline dependencies"
- ✓ "Two-repo split: aibox + processkit"
- ✗ "Which MCP library to use?" (question, not decision)
- ✗ "Tooling update" (too vague)
- ✗ "Alice's proposal" (credits the person, not the decision)
Consequences that are honest
Future readers trust the decision record more when you write the real consequences, including the uncomfortable ones. "Costs 300-400 MB image size" builds more credibility than "some increase in container size."
Linking to workitems
When a decision prompts work to happen, link via spec.related_workitems:
spec:
related_workitems: [BACK-calm-fox, BACK-swift-oak]
This lets queries find "all work that implements DEC-foo" and "all decisions that motivated BACK-bar."