Skip to main content

Audit-Ready Mutation

Evidence status: Rust 4.1.1 with SQLite has an executed audited mutation. The Java examples are reviewed shapes but are not executable evidence while the current generated Java workspace remains blocked. Audit-sink contents, rollback, and masking assertions are still required.

Problem

Change an entity while preserving who initiated the operation, why the state changed, and which transaction owns the write.

Use this recipe for application mutations. Do not use a generated entity as a place to hide request identity, authorization, or transaction policy.

Inputs

  • A generated entity and its exact update methods.
  • A request-scoped UserContext carrying application identity and policy.
  • A provider/runtime that supports the required transaction behavior.
  • A concrete audit description meaningful to an operator or reviewer.

Read the generated entity before choosing an update method. Generated method names vary with the model and target stack.

Java Path

Keep transaction ownership at the service/use-case boundary:

@Transactional
public void confirmOrder(CustomUserContext ctx, Order order) {
order.updateStatusToConfirmed();
order.auditAs("Confirm customer order").save(ctx);
}

The exact updateStatusToConfirmed() method is illustrative. Use the method emitted by the current generated entity.

For several saves that must commit together, keep them inside the same service transaction:

@Transactional
public void confirmOrderAndReserveStock(
CustomUserContext ctx, Order order, Stock stock) {
order.updateStatusToConfirmed();
order.auditAs("Confirm customer order").save(ctx);

stock.updateReservedQuantity(order.getTotalQuantity());
stock.auditAs("Reserve stock for confirmed order").save(ctx);
}

Rust Path

Rust graph saves carry audit intent on the entity persistence chain:

order
.audit_as("Confirm customer order")
.save(&ctx)
.await?;

For graph writes, confirm that the registered provider implements the transaction capability required by the runtime. Do not assume that a memory or experimental provider has the same rollback guarantees as a relational provider.

Verification

Verify more than successful return:

  1. Reload the entity with a query carrying comment and purpose.
  2. Confirm the intended state and optimistic-lock version.
  3. Inspect the configured audit sink/log for identity and business intent.
  4. Force a validation or child-write failure and confirm transaction rollback.
  5. Confirm no sensitive payload is exposed by the selected log level.

Failure Modes

SymptomLikely causeFix
Save rejected for missing auditPersistence chain has no audit intent.Add a concrete auditAs / audit_as before save.
Update method missingHandwritten code guessed a generated method.Read generated entity source and use its exact method.
Partial multi-entity writeTransaction boundary is missing or provider semantics differ.Add/verify the application transaction and provider capability.
Audit entry lacks actorRequest context was created without project identity integration.Populate and test the application UserContext identity boundary.
Sensitive data in logsPayload logging or masking policy is unsafe.Reduce log detail and configure masking before production.

References