decision-logging
Use this when writing any entry to runs/<run-id>/decision-log.jsonl — decision, lifecycle, communication, event, failure, refactor, skill-evolution, or budget. Covers canonical JSON schemas, per-agent
SKILL.mdskills/decision-logging/SKILL.md
GitHubdecision-logging (SDK-mode, v1.4.0)
Delta (v1.3.0 → v1.4.0)
Removed the cross-LLM ensemble telemetry layer introduced in v1.3.0. The cross-llm-devil MCP server and its sibling devils (sdk-overengineering-critic-gemini, sdk-security-devil-gemini) were removed from the pipeline; consequently the cross-llm-call / cross-llm-agreement / cross-llm-conflict event categories and the model_id / transcript_path / ensemble_role optional fields are no longer documented or emitted.
Backward compat: pre-existing decision-log entries that still carry these fields remain readable — readers ignore unknown categories/fields. No retroactive scrub of historical run data is required.
Delta (v1.1.0 → v1.2.0)
One additive envelope field across every entry type to support the language-agnostic pipeline (CLAUDE.md rule 34).
Required field on every entry: language
{
...,
"language": "go" | "python" | "unknown"
}
Source of truth: read from runs/<run-id>/context/active-packages.json:target_language once package resolution (Wave I5.5) has completed.
Pre-resolution entries: any entry written BEFORE Wave I5.5 (i.e., during sdk-intake-agent waves I1–I5.5 itself) MUST emit "language": "unknown". Re-stamping is not required — pre-resolution entries are intake-internal and never participate in cross-language metrics.
Post-resolution entries: "language": "unknown" is a BLOCKER signal. The G01 validator (or its successor) MUST FAIL any entry with language: unknown whose timestamp is after active-packages.json:resolved_at.
Rationale: per-language partitioning of metrics, baselines, and learning-engine signals depends on every log line being attributable to a language. Without this stamp, cross-language quality scoring, devil-verdict-history, and skill-coverage analysis cannot disambiguate.
Applies to: decision, lifecycle, communication, event, failure, refactor, skill-evolution, budget — all eight entry types.
Validator mapping: G01 extended to assert presence + valid value. Backward-compat: entries written by pipeline_version <0.5.0 may legitimately omit language and are not retroactively invalidated.
Delta (v1.0.0 → v1.1.0)
Two new entry types added for SDK-pipeline self-evolution:
skill-evolution (cap ≤10 per run)
Emitted by learning-engine OR sdk-bootstrap-lead when a skill is created / bumped / deprecated.
{
"run_id": "<uuid>",
"pipeline_version": "sdk-pipeline@0.7.0",
"skill_version_snapshot": {"<skill>": "<version>", ...},
"timestamp": "2026-04-17T...",
"type": "skill-evolution",
"agent": "learning-engine",
"phase": "feedback",
"skill": "<skill-name>",
"action": "create | bump-patch | bump-minor | bump-major | deprecate",
"from_version": "1.2.0",
"to_version": "1.2.1",
"trigger": "drift-finding:SKD-042 | devil-verdict:NEEDS-FIX | pattern-recurrence:3 | user-request",
"devil_verdict": "ACCEPT | NEEDS-FIX | REJECT"
}
budget (per-agent emit every 5min OR on phase exit)
Emitted by every agent to track per-phase token + wall-clock consumption.
{
"run_id": "<uuid>",
"pipeline_version": "sdk-pipeline@0.7.0",
"skill_version_snapshot": {...},
"timestamp": "2026-04-17T...",
"type": "budget",
"agent": "<agent-name>",
"phase": "<phase>",
"tokens_in": 12345,
"tokens_out": 6789,
"wall_clock_sec": 42.5,
"cumulative_pct_of_budget": 0.34
}
Consumers of new types
baseline-managerreadsbudgetentries to compute phase-duration + token-consumption metricsimprovement-plannerreadsskill-evolutionto detect unstable skills (high patch frequency)sdk-skill-drift-detectorreads both to correlate prescription vs. reality
Validator mapping
- Guardrail
G01unchanged (valid JSONL). - Guardrail
G06(NEW): every entry MUST carrypipeline_version+skill_version_snapshot.
Decision Logging
Provides the canonical JSON schemas, entry limits, and best practices for appending to decision-log.jsonl across both Architecture and Detailed Design phases.
When to Activate
- When any agent needs to log an architectural or design decision
- When an agent starts, completes, or fails (lifecycle logging)
- When consolidating minor decisions into grouped entries
- When logging a conflict resolution between agents
- When unsure which
phasefield value to use - Used by: ALL 24 agents (architecture-lead, system-decomposer, api-designer, infrastructure-architect, database-architect, pattern-advisor, simulated-cto, simulated-security-architect, simulated-sre, simulated-tech-lead, simulated-product-owner, detailed-design-lead, component-designer, data-model-designer, sdk-designer, interface-designer, algorithm-designer, concurrency-designer, coding-guidelines-generator, guardrail-validator, simulated-senior-developer, simulated-dba, simulated-qa-lead, simulated-security-engineer)
Decision Log File Locations
| Phase | File Path |
|---|---|
| Architecture | docs/architecture/decisions/decision-log.jsonl |
| Detailed Design | docs/detailed-design/decisions/decision-log.jsonl |
| Implementation | docs/implementation/decisions/decision-log.jsonl |
| Testing | docs/testing/decisions/decision-log.jsonl |
| Frontend | docs/frontend/decisions/decision-log.jsonl |
Each file is append-only JSONL (one JSON object per line, no trailing commas, no array wrapper). ALL six entry types (decision, lifecycle, communication, event, failure, refactor) go into the same file for a given phase.
Decision Entry Schema
Per CLAUDE.md rule #1, every architectural or design decision uses this exact schema:
{
"run_id": "<from-run-manifest>",
"type": "decision",
"timestamp": "<ISO-8601>",
"agent": "<agent-name>",
"phase": "<see-phase-values-below>",
"decision": "<what was decided>",
"rationale": "<why this over alternatives>",
"alternatives_considered": ["<alt1>", "<alt2>"],
"trade_offs": "<what is sacrificed>",
"confidence": "<high|medium|low>",
"status": "<success|failed|retried|degraded>",
"context": "<input that led to this>",
"impacts": ["<affected-agents-or-artifacts>"],
"reversibility": "<easy|moderate|hard|irreversible>",
"tags": ["<tag1>", "<tag2>"]
}
Required Fields — Never Omit
Every field in the schema is mandatory. If a field has no meaningful value, use:
"alternatives_considered": [](empty array, not null)"trade_offs": "none identified"(not empty string)"impacts": [](empty array, not null)"tags": [](empty array, not null)
Lifecycle Entry Schema
Per CLAUDE.md rule #1, lifecycle events (agent start, completion, failure) use this schema:
{
"run_id": "<from-run-manifest>",
"type": "lifecycle",
"timestamp": "<ISO-8601>",
"agent": "<agent-name>",
"event": "<started|completed|failed|retried>",
"wave": "<1|2|3|4>",
"outputs": ["<list-of-files-written>"],
"duration_seconds": "<elapsed>",
"error": "<error-description-if-failed>"
}
Lifecycle entries do NOT count toward the decision entry limit.
Phase Field Values
Architecture Phase Agents
| Agent | Phase Value |
|---|---|
| system-decomposer | decomposition |
| api-designer | api-design |
| infrastructure-architect | infrastructure |
| database-architect | database |
| pattern-advisor | patterns |
| architecture-lead | review (for synthesis decisions) |
| All Wave 3 review agents | review |
Detailed Design Phase Agents
| Agent | Phase Value |
|---|---|
| component-designer | component-design |
| interface-designer | interface-design |
| data-model-designer | data-model |
| algorithm-designer | algorithm |
| concurrency-designer | concurrency |
| sdk-designer | sdk-design |
| coding-guidelines-generator | coding-guidelines |
| detailed-design-lead | review |
| All Wave 4 review agents | review |
Entry Limits (per agent per run)
| Type | Limit | Rationale |
|---|---|---|
decision |
15 (10 for review agents) | Architectural/design choices — consolidate minor ones |
lifecycle |
Unlimited | Agent start/end/retry — low volume |
communication |
20 | Cap chattiness — log meaningful exchanges only |
event |
30 (max 10 major, 20 minor/info) | Most granular type — cap to prevent noise |
failure |
10 | Failures should be exceptional |
refactor |
10 | Typically one per review finding |
Only decision entries count toward the traditional 15/10 cap. All other types have their own independent limits.
Communication Entry Schema
Tracks every meaningful agent-to-agent exchange for coordination audit:
{
"run_id": "<from-run-manifest>",
"type": "communication",
"timestamp": "<ISO-8601>",
"from_agent": "<sender>",
"to_agent": "<recipient|*>",
"wave": "<N>",
"channel": "<message|context-file|escalation>",
"severity": "<critical|high|normal|low>",
"topic": "<short subject>",
"content_summary": "<1-2 sentence summary>",
"references": ["<file-paths-or-decision-ids>"],
"requires_response": true,
"response_status": "<pending|acknowledged|resolved|ignored>",
"tags": ["<assumption|conflict|dependency|blocker>"]
}
When to Log Communications
- Task assignments from lead to agent
- Escalation messages (ESCALATION: or BLOCKER: prefix)
- Cross-agent dependency requests ("I need X from agent Y")
- Assumption notifications ("ASSUMPTION — pending Z confirmation")
- Review relay messages (lead relaying findings to responsible agent)
- Conflict reports
When NOT to Log Communications
- "Still working" status pings
- Acknowledgements with no new information
- Internal monologue or self-notes
Channel Values
| Channel | When to Use |
|---|---|
message |
SendMessage / inbox communication |
context-file |
Writing/reading a context summary as implicit communication |
escalation |
ESCALATION: or BLOCKER: prefixed messages |
Event Entry Schema
Captures major and minor happenings during agent work:
{
"run_id": "<from-run-manifest>",
"type": "event",
"timestamp": "<ISO-8601>",
"agent": "<agent-name>",
"wave": "<N>",
"severity": "<major|minor|info>",
"category": "<see-category-values-below>",
"title": "<short description>",
"detail": "<what happened and why>",
"artifacts": ["<files-read-or-written>"],
"duration_ms": null,
"outcome": "<success|warning|error|skipped>",
"parent_event_id": "<optional, for nested events>"
}
Event Category Values
| Category | When to Use |
|---|---|
input-read |
Reading a critical input file or context summary |
output-write |
Writing a major output artifact |
validation |
Running a validation check (schema, syntax, constraint) |
compilation |
Running the active language adapter's build command (resolved from toolchain.build in the package manifest) |
test-run |
Running the active language adapter's test command (resolved from toolchain.test) |
tool-call |
Invoking an external tool or script |
dependency-resolution |
Resolving a dependency between agents or packages |
assumption |
Making an assumption about upstream agent output |
skip |
Deliberately skipping optional work |
retry |
Retrying a failed operation |
workaround |
Applying a workaround for a known issue |
Severity Guidelines
- major: Events that change the agent's approach or affect downstream agents (e.g., "compilation failed, switching strategy").
- minor: Routine progress events that confirm expected behavior (e.g., "read service-map.md successfully").
- info: Context-only events useful for debugging (e.g., "found 5 services in decomposition").
Failure Entry Schema
Rich failure context for individual operations (supplements lifecycle "event":"failed"):
{
"run_id": "<from-run-manifest>",
"type": "failure",
"timestamp": "<ISO-8601>",
"agent": "<agent-name>",
"wave": "<N>",
"failure_type": "<see-failure-types-below>",
"severity": "<critical|high|medium|low>",
"title": "<short description>",
"detail": "<what failed and why>",
"error_output": "<first 500 chars of error output>",
"partial_outputs": ["<files-written-before-failure>"],
"attempted_recovery": "<none|retry|fallback|skip|manual>",
"recovery_successful": null,
"blocked_agents": ["<agents-waiting-on-this>"],
"root_cause_hint": "<agent's best guess at why>",
"related_decision_id": "<if failure relates to a logged decision>"
}
Failure Type Values
| Failure Type | When to Use |
|---|---|
timeout |
Operation exceeded time limit |
missing-input |
Required upstream file or context not found |
validation-error |
Output failed schema/syntax/constraint validation |
compilation-error |
The active adapter's toolchain.build command failed |
test-failure |
The active adapter's toolchain.test command failed |
dependency-failure |
Upstream agent failed, blocking this agent |
tool-error |
External tool (linter, formatter, script) failed |
resource-limit |
Exceeded entry limit, file size limit, or iteration cap |
Key Rule
Log a failure entry BEFORE attempting recovery. This ensures the failure is captured even if recovery succeeds. After recovery, update recovery_successful field via a follow-up event entry.
Refactor Entry Schema
Tracks post-review and post-failure changes to distinguish initial work from rework:
{
"run_id": "<from-run-manifest>",
"type": "refactor",
"timestamp": "<ISO-8601>",
"agent": "<agent-name>",
"wave": "<N>",
"trigger": "<review-finding|test-failure|guardrail-failure|escalation|self-improvement>",
"trigger_source": "<agent-or-script that triggered this>",
"trigger_reference": "<finding-id|defect-id|guardrail-name>",
"files_changed": ["<modified-files>"],
"change_summary": "<what was changed>",
"before_state": "<brief description of old approach>",
"after_state": "<brief description of new approach>",
"severity": "<major|minor>",
"confidence": "<high|medium|low>",
"regression_risk": "<none|low|medium|high>"
}
Trigger Values
| Trigger | When to Use |
|---|---|
review-finding |
Changing output after a reviewer flagged an issue |
test-failure |
Fixing code/design after a test failed |
guardrail-failure |
Updating output to pass an automated guardrail check |
escalation |
Changing approach after a BLOCKER or ESCALATION message |
self-improvement |
Agent voluntarily improving output without external trigger |
Consolidating Minor Decisions
When an agent makes several small, related decisions, consolidate them into a single grouped entry rather than logging each individually.
When to Consolidate
- Three or more decisions about the same domain (e.g., naming multiple NATS subjects)
- Decisions that share the same rationale and alternatives
- Stylistic or formatting choices within a single output file
How to Consolidate
{
"run_id": "a1b2c3d4-...",
"type": "decision",
"timestamp": "2026-03-09T14:30:00Z",
"agent": "api-designer",
"phase": "api-design",
"decision": "NATS subject naming for entity lifecycle: tenant.{tid}.<domain>.created, tenant.{tid}.<domain>.updated, tenant.{tid}.<domain>.deleted (applied to all bounded contexts)",
"rationale": "Consistent verb-based suffixes align with AsyncAPI spec and enable wildcard subscriptions per tenant",
"alternatives_considered": ["dot-separated-entity.action", "slash-separated paths"],
"trade_offs": "Longer subject names increase NATS overhead slightly",
"confidence": "high",
"status": "success",
"context": "Designing async event subjects for domain services",
"impacts": ["infrastructure-architect", "concurrency-designer"],
"reversibility": "easy",
"tags": ["nats-subjects", "consolidated"]
}
Use the "consolidated" tag to mark grouped entries.
Conflict Resolution Logging
Per CLAUDE.md rule #8, when architecture-lead or detailed-design-lead resolves a conflict between agents, log it with the "conflict-resolution" tag:
{
"run_id": "a1b2c3d4-...",
"type": "decision",
"timestamp": "2026-03-09T16:00:00Z",
"agent": "architecture-lead",
"phase": "review",
"decision": "Tenant isolation uses schema-per-tenant per database-architect's design, not application-level filtering as proposed by api-designer",
"rationale": "Schema-per-tenant provides hard isolation; application filtering alone risks cross-tenant data leakage if a query is missed",
"alternatives_considered": ["application-level tenant filtering", "separate schemas per tenant", "row-level security (RLS)"],
"trade_offs": "Schema-per-tenant requires per-tenant connection pool management and coordinated migrations across all tenant databases",
"confidence": "high",
"status": "success",
"context": "ESCALATION: CONFLICT between database-architect (schema-per-tenant) and api-designer (app-level filtering)",
"impacts": ["api-designer", "database-architect", "component-designer"],
"reversibility": "hard",
"tags": ["conflict-resolution", "tenant-isolation"]
}
Required Tags for Conflict Entries
"conflict-resolution"(always present)- Domain-specific tag (e.g.,
"tenant-isolation","nats-subjects")
Status Field Values
| Value | When to Use |
|---|---|
success |
Decision made with confidence, no issues |
failed |
Decision attempted but agent could not reach a conclusion |
retried |
Decision made after a previous failed attempt |
degraded |
Decision made with incomplete inputs (upstream agent failed) |
Reading the run_id
Before logging any entry, the agent MUST read the run_id from the appropriate manifest:
- Architecture phase:
docs/architecture/state/run-manifest.json - Detailed Design phase:
docs/detailed-design/state/run-manifest.json
Never generate a run_id. Never hardcode a run_id. Always read it from the manifest.
Examples
GOOD
{"run_id":"f47ac10b-58cc-4372-a567-0e02b2c3d479","type":"decision","timestamp":"2026-03-09T10:15:00Z","agent":"database-architect","phase":"database","decision":"All tenant tables use UUID v7 for primary keys with tenant_id as the first column in composite indexes","rationale":"UUID v7 is time-sortable, reducing index fragmentation; schema-per-tenant isolation means no tenant_id columns needed in tables","alternatives_considered":["UUID v4","BIGSERIAL","ULID"],"trade_offs":"UUID v7 requires Go 1.26+ uuid library; 16-byte keys are larger than BIGSERIAL","confidence":"high","status":"success","context":"Designing primary key strategy for tenant-scoped tables","impacts":["data-model-designer","component-designer"],"reversibility":"hard","tags":["primary-keys","multi-tenancy"]}
BAD
{"agent":"database-architect","decision":"use UUIDs","rationale":"they are good"}
Why it is wrong: Missing run_id, type, timestamp, phase, status, alternatives_considered, trade_offs, confidence, context, impacts, reversibility, and tags. Every field is mandatory.
BAD
{"run_id":"f47ac10b-...","type":"decision","timestamp":"2026-03-09T10:15:00Z","agent":"database-architect","phase":"database","decision":"chose index type A","rationale":"","alternatives_considered":null,"trade_offs":"","confidence":"high","status":"success","context":"","impacts":null,"reversibility":"easy","tags":null}
Why it is wrong: Empty strings and null arrays. Use "none identified" for trade_offs, [] for arrays, and descriptive text for rationale and context.
Common Mistakes
Forgetting to read run_id from the manifest -- Agents generate their own UUID or hardcode a placeholder. The run_id MUST come from
run-manifest.jsonso all entries for a run are correlated.Using the wrong phase value -- An
api-designeragent logging with"phase": "design"instead of"phase": "api-design". Check the phase table above for the exact string.Exceeding the entry limit without consolidating -- Logging 20+ individual decisions when related ones could be grouped with the
"consolidated"tag. Count entries before appending and consolidate if approaching the limit.Counting lifecycle entries toward the decision cap -- Lifecycle entries (
"type": "lifecycle") are separate and unlimited. Only"type": "decision"entries count toward the 15/10 limit.Omitting the conflict-resolution tag -- When
architecture-leadresolves a conflict per CLAUDE.md rule #8, the resulting decision entry MUST include"conflict-resolution"in tags. Without it, conflict audit trails are broken.Logging to the wrong file -- Architecture agents must append to
docs/architecture/decisions/decision-log.jsonl. Detailed design agents must append todocs/detailed-design/decisions/decision-log.jsonl. Never cross-write.