0010: Compose audited commands in phases
Authorize once, prepare once, and retry only transactional execution
- Status: Accepted
- Date: 2026-09-18
- Decision maker: Trevor Gerhardt
Context
Backend writes repeated session checks, audit setup, and transaction handling. A single failure outcome also hid the difference between denial, business rejection, and storage failure.
Decision
Auth and Services define commands with explicit authorization, preparation, and execution phases. The owner keeps session policy and failure classification. Persistence supplies transaction retries and audit storage without importing domain policy.
Authorization fixes caller authority and tenant scope once at command start. Preparation validates input and creates stable facts. Each transaction attempt rereads current target state and checks lifecycle, uniqueness, and remaining-owner invariants. Only execution retries. A successful write and its audit event commit together. Failure recording follows rollback and preserves the original cause.
Each execution and retry gets its own audit context. Domain code adds only allowed safe facts and cannot commit an audit row itself. Ordinary validation and stale-session refusals create no business audit row. Authorization refusals record denied; business constraints record rejected; storage failures record failure.
The module-shape standard owns the command interface. PRD-0006 owns the observable outcomes.
Consequences
- An accepted command can finish after revocation, including during a retry. The next command checks current authority.
- Current target state remains transactional despite the command-start permission decision.
- Audit events do not expose submitted secrets or raw internal errors.
History
This decision replaced permission rechecks on every transaction attempt.