| name | decision-logging |
| description | Defines when and how commands must log architecture decisions to _state.json decisions[] for auditability |
Decision Logging Skill
When commands make architecture decisions (choosing tech stacks, patterns, frameworks, databases, etc.), they MUST log those decisions to _state.json.decisions[] for auditability and future reference.
When to Log a Decision
Log a decision whenever your command:
- Chooses a technology (database, framework, language, messaging system, auth provider)
- Selects an architectural pattern (monolith vs. microservices, clean vs. modular, etc.)
- Determines a trade-off (prioritize cost over latency, scalability over simplicity, etc.)
- Makes a significant strategic choice that affects the system's design
Do NOT log:
- Routine operations (generating files, running tests)
- Configuration of already-decided technologies
- Re-running previous decisions
Examples of decisions to log:
- ✅ "Choose PostgreSQL for primary database" (blueprint command)
- ✅ "Use React over Vue for UI framework" (scaffold command)
- ✅ "Prioritize fast iteration over peak performance" (mvp-scope)
- ✅ "Use monolithic architecture for initial MVP" (architecture design)
- ❌ "Generated auth routes file" (implementation detail, not decision)
- ❌ "Installed dependencies" (mechanical task, not decision)
Decision Structure
Each decision is a JSON object in the decisions[] array:
{
"id": "D-NNN",
"title": "Choose PostgreSQL for primary database",
"rationale": "ACID compliance for order consistency. Team has PostgreSQL expertise. Query flexibility for future analytics features.",
"alternatives_rejected": [
"MongoDB (weak multi-document transactions, overkill for relational data)",
"DynamoDB (cost scales poorly with joins, limited query flexibility)"
],
"trade_offs": "Higher operational complexity than managed cloud DBs. Team must maintain backups. Cold-start slower than serverless options.",
"made_by_command": "blueprint",
"timestamp": "2026-04-24T14:32:10Z"
}
Field Details
| Field | Type | Required | Description |
|---|
id | string | Yes | Unique decision ID (e.g., "D-001", "D-002"). Increment sequentially within a project. |
title | string | Yes | Brief decision title (3-10 words). E.g., "PostgreSQL for primary database" |
rationale | string | Yes | Why this choice? What does it enable? What problem does it solve? (2-5 sentences or bullets) |
alternatives_rejected | array of strings | Yes | What were the runner-up options and why did they lose? Makes future reconsideration easier. At least 2 alternatives. |
trade_offs | string | Yes | What are you giving up? What becomes harder or more expensive? (1-3 sentences) |
made_by_command | string | Yes | Which command made this decision? (e.g., "blueprint", "sdl", "scaffold") |
timestamp | string | Yes | ISO-8601 timestamp when decision was made (e.g., "2026-04-24T14:32:10Z") |
How Commands Log Decisions
Pattern: Read → Decide → Log
1. Read current _state.json (if exists)
2. Read SDL and extract/determine tech choices
3. For each decision (e.g., database choice):
a. Create decision object with all 7 fields
b. Append to _state.json.decisions[] array
4. Write _state.json back to disk
Code Example: Python/JavaScript
import json
from datetime import datetime
state = json.load(open('architecture-output/_state.json')) if os.path.exists(...) else {"decisions": []}
new_decision = {
"id": f"D-{len(state.get('decisions', [])) + 1:03d}",
"title": "PostgreSQL for primary database",
"rationale": "ACID compliance for order consistency. Team has expertise. Query flexibility for analytics.",
"alternatives_rejected": [
"MongoDB (weak transactions, overkill for relational data)",
"DynamoDB (cost scales poorly, limited queries)"
],
"trade_offs": "Higher ops complexity. Team maintains backups. Cold-start slower than serverless.",
"made_by_command": "blueprint",
"timestamp": datetime.utcnow().isoformat() + "Z"
}
state.setdefault("decisions", []).append(new_decision)
json.dump(state, open('architecture-output/_state.json', 'w'))
const fs = require('fs');
const path = require('path');
const stateFile = 'architecture-output/_state.json';
const state = fs.existsSync(stateFile)
? JSON.parse(fs.readFileSync(stateFile, 'utf8'))
: { decisions: [] };
const newDecision = {
id: `D-${String(state.decisions.length + 1).padStart(3, '0')}`,
title: "PostgreSQL for primary database",
rationale: "ACID compliance for order consistency. Team has expertise. Query flexibility for analytics.",
alternatives_rejected: [
"MongoDB (weak transactions, overkill for relational data)",
"DynamoDB (cost scales poorly, limited queries)"
],
trade_offs: "Higher ops complexity. Team maintains backups. Cold-start slower than serverless.",
made_by_command: "blueprint",
timestamp: new Date().toISOString().replace('Z', 'Z')
};
state.decisions.push(newDecision);
fs.writeFileSync(stateFile, JSON.(state, , ));
Decision ID Sequence
Decision IDs are per-project (not global). Start at D-001 for the first decision:
- First decision by blueprint:
D-001
- Second decision by blueprint:
D-002
- First decision by scaffold command:
D-003 (continues sequence, not restart)
ID format: D-NNN where NNN is zero-padded 3-digit number.
Multiple Decisions Per Command
If your command makes multiple decisions, create one object per decision:
{
"decisions": [
{
"id": "D-001",
"title": "PostgreSQL for primary database",
"rationale": "...",
"alternatives_rejected": ["..."],
"trade_offs": "...",
"made_by_command": "blueprint",
"timestamp": "2026-04-24T14:32:10Z"
},
{
"id": "D-002",
"title": "React for frontend framework",
"rationale": "...",
"alternatives_rejected": ["..."],
"trade_offs":
Each decision gets its own ID and timestamp (even if made seconds apart).
Linking Decisions to ADRs (Phase 1.5+)
When ADRs are generated, they should reference decision IDs:
# ADR-001: Database Choice
**Decision ID:** D-001
## Status
Accepted
## Context
[Rationale from decisions[0].rationale]
## Decision
PostgreSQL
## Consequences
[Trade-offs from decisions[0].trade_offs]
## Alternatives Considered
- MongoDB: [from decisions[0].alternatives_rejected[0]]
- DynamoDB: [from decisions[0].alternatives_rejected[1]]
Viewing & Analyzing Decisions
Users can analyze decisions via:
- Manual review:
jq '.decisions' architecture-output/_state.json
- Decision report:
/architect:decisions-linked (Phase 1.5 command) generates markdown mapping decisions to ADRs
- Check-state command:
/architect:check-state validates decision logging compliance
Best Practices
- Be specific: "PostgreSQL" not "choose a database"
- Capture rationale: Explain WHY, not just WHAT
- List real alternatives: Don't strawman rejected options; explain their actual merits
- Quantify trade-offs: "5x higher ops complexity", "30% cost increase", not vague
- Timestamp accurately: Use actual decision time, not file generation time
- One per decision: Don't lump unrelated choices into one decision object
- Made_by_command: Always set; helps trace where decision came from
Related Commands
/architect:check-state — Validates decisions array
/architect:decisions-linked (Phase 1.5) — Maps decisions to ADRs
/architect:generate-docs — Includes decision log in documentation
Example Decisions from Real Projects
E-commerce SaaS
{
"id": "D-001",
"title": "Next.js App Router for frontend",
"rationale": "Server components reduce client-side complexity. File-based routing matches team's experience. Excellent TypeScript support.",
"alternatives_rejected": [
"React SPA + Express (more configuration, fewer conventions)",
"Remix (smaller ecosystem, steeper learning curve)"
],
"trade_offs": "Tied to Vercel deployment for optimal performance. Vendor lock-in risk.",
"made_by_command": "blueprint",
"timestamp": "2026-04-24T10:00:00Z"
}
Startup MVP
{
"id": "D-001",
"title": "Monolithic architecture for MVP",
"rationale": "Faster time-to-market. Simpler deployment. Team of 2 engineers. Can split to microservices later if needed.",
"alternatives_rejected": [
"Microservices (overkill complexity for MVP, ops overhead)",
"Serverless (unpredictable cold starts for user-facing APIs)"
],
"trade_offs": "Will need refactoring when scaling to 10+ services. Single point of failure. Database scaling limited.",
"made_by_command": "blueprint",
"timestamp": "2026-04-20T14:32:10Z"
}
Not Logging Is Not OK
If your command makes a tech choice but doesn't log it:
- Future teams don't understand why the choice was made
- Second-guessing common (why Postgres? why not Mongo?)
- ADRs can't be linked to decisions
/architect:check-state may flag missing decisions
- Institutional knowledge is lost when team member leaves
Always log decisions. It's cheap, and it pays dividends.