Money mutation without idempotency key
errormoney-mutation-without-idempotency-key
A call that moves money is retried after a lost response. Without an idempotency key on that hop the retry becomes a second charge, which is exactly what the design's exactly-once-effect promise rules out.
- Severity
- error
- Selects
- every edge
- Components in scope
- 2,366
It looks at every edge, and fires when:
- — all of these hold:
- — the edge's target node:
- — any of these hold:
- — the node's `handlesMoney` is true
- — the node's `doubleEntry` has a value
- — the edge's `idempotent` has no value
- — the design requirement `correctness.exactlyOnceEffect` is true
Edge '{{edge.from}}' -> '{{edge.to}}' moves money but does not declare idempotent, while the design requires exactly-once effect. Add an idempotency key to this hop.This rule is about an edge, so it belongs to a pair of nodes rather than one component.
This rule names no kind: it reads the design as a whole.
Reference designs whose broken twin names this rule in its header. The gate reads the same header, and fails when a rule no design trips.
# yaml-language-server: $schema=../schema/rule.schema.json # A call that moves money (to a third party that handles money, or into a double-entry # ledger) will be retried after a lost response. Without an idempotency key on that hop # the retry becomes a double charge, which the design promises never happens. # Tripped by: designs/payment-system.broken.yaml schemaVersion: "1" type: "rule" id: "money-mutation-without-idempotency-key" severity: "error" select: edge: {} when: all: - to: any: - prop: "handlesMoney" equals: true - prop: "doubleEntry" exists: true - field: "idempotent" exists: false - requirement: "correctness.exactlyOnceEffect" equals: true message: "Edge '{{edge.from}}' -> '{{edge.to}}' moves money but does not declare idempotent, while the design requires exactly-once effect. Add an idempotency key to this hop."