Status: Adopted (agent-recorded design decision) — pending named architecture ratification Date: 2026-07-24 Authors: V1 Domain Workbenches extraction audit (Phase S)
Records the event/audit/idempotency and failure semantics for the shared workbench mutation path (
V1_DOMAIN_WORKBENCHES_TODOS_2026-07-23.md§S0.8). Built on the committed facts from §S0.8.a–c: 42 mutations → 34 local-ACID + 8 transactional-outbox, 0 sagas (tara-transaction-boundaries.json) and the identity contract (command / event / external-operation / correlation ids). The machine-checked model isevidence/v1-workbenches/event-audit-boundaries.json.
Context#
§S0.8.b chose the transactional outbox for every mutation that crosses an external boundary (8 of 42) and a local ACID transaction (persistence + audit in one commit) for the rest. No mutation requires a saga — none demands that an external effect be atomic with the local write. This ADR fixes the delivery, audit, and failure semantics that follow.
S0.8.d — Delivery semantics (the 8 outbox mutations)#
| Dimension | Rule |
|---|---|
| ordering | per-aggregate (per target id) order via the outbox row sequence; no global cross-aggregate order |
| delivery | at-least-once — the dispatcher retries a row until the external effect acks |
| duplication | consumers are idempotent, deduplicating by externalOperationId (the outbox row id, §S0.8.c) |
| replay | safe from the durable outbox log by externalOperationId; dedup makes re-dispatch a no-op |
| compatibility | events are versioned; additive changes are backward-compatible; a breaking change ships a new version, never a silent reshape |
| poison | after a bounded retry budget a row moves to a dead-letter queue (no infinite retry); it alerts and awaits manual replay |
S0.8.e — Audit semantics (all 42 mutations)#
| Dimension | Rule |
|---|---|
| atomicity | the audit row commits in the same local transaction as the persistence write (§S0.8.b local-ACID = persistence + audit); no mutation without its audit |
| refusal/failure capture | authorization refusals and validation failures are audited too, not only successes |
| immutability | audit rows are append-only; no UPDATE/DELETE on the ledger |
| redaction | PII/secret fields are redacted at write time; the ledger never stores raw secrets |
| retention | governed by a RetentionPolicy (per audit class); expiry is a scheduled purge, not an ad-hoc delete |
| evidence linkage | each audit event links to its target id + command id (§S0.8.c) — the chain actor → command → effect |
S0.8.f — Failure scenario diagrams#
The seven scenarios and the guarantee each relies on. Notation: W=workbench
BFF, DB=durable store (persistence + audit + outbox, one tx), D=outbox
dispatcher, X=external effect (provider/publish/blob/notification).
1. Failure before commit — atomic rollback#
2. After commit / before dispatch — durable outbox closes the window#
3. Duplicate delivery — idempotent consumer#
4. Worker loss — pending rows resume#
5. External success / local timeout — safe retry#
6. Compensation failure — NOT APPLICABLE (0 sagas)#
7. Replay — idempotent by construction#
Consequences#
- The only durability seam is the after-commit / before-dispatch window
(scenario 2), which the durable outbox closes. Everything downstream is made
safe by dedup on
externalOperationId(scenarios 3, 5, 7). - Because
sagaRequired = 0, there is deliberately no distributed-rollback code path — a simpler, safer design than compensating transactions. - Named architecture ratification remains a human step; the delivery/audit model and the scenario set are coherence-gated in CI against the §S0.8.b/c evidence.
References#
V1_DOMAIN_WORKBENCHES_TODOS_2026-07-23.md§S0.8evidence/v1-workbenches/tara-transaction-boundaries.json(§S0.8.b/c),tara-mutation-effect-matrix.json(§S0.8.a)evidence/v1-workbenches/event-audit-boundaries.json(this model, machine-checked)docs/adr/ADR-S1-*.md,docs/adr/ADR-S2-*.md