Skip to content

Validation Rules CatalogΒΆ

36 validation rules governing behavioural correctness in UC-BoK processes.

Validation Rules are process-level guards: they define what must be true before, during, or after an operation. Unlike Semantic Constraints (which express domain invariants), Validation Rules are contextual and process-scoped.

SummaryΒΆ

Type Count Purpose
⬆️ Pre-Condition 22 What must be true before an operation can begin.
🚧 Transition Guard 9 What must be true for a lifecycle state transition to be permitted.
⬇️ Post-Condition 5 What must be true after an operation has completed.
Severity Count
πŸ”΄ Invariant 32
🟑 Constraint 4
🟒 Guideline 0

⬆️ Pre-ConditionΒΆ

What must be true before an operation can begin.

Rule Applies To Context Subject Severity
bopis-requires-channel bopis process channel πŸ”΄ Invariant
bopis-requires-store-inventory bopis process inventory πŸ”΄ Invariant
boris-requires-order-with-items boris process order πŸ”΄ Invariant
capture-payment-requires-authorization capture-payment service payment πŸ”΄ Invariant
checkout-requires-active-basket checkout process basket πŸ”΄ Invariant
checkout-requires-active-product checkout process product πŸ”΄ Invariant
close-order-requires-fulfillment-complete close-order service fulfillment-order πŸ”΄ Invariant
confirm-reservation-requires-reserved confirm-reservation service reservation πŸ”΄ Invariant
customer-registration-requires-no-duplicate customer-registration process customer πŸ”΄ Invariant
endless-aisle-requires-active-channel endless-aisle process channel πŸ”΄ Invariant
exchange-anywhere-requires-closed-order exchange-anywhere process order πŸ”΄ Invariant
issue-points-requires-closed-order issue-points service order 🟑 Constraint
loss-prevention-intervention-requires-policy loss-prevention-case-handling process loss-prevention-case πŸ”΄ Invariant
loyalty-enrollment-requires-no-existing-account loyalty-enrollment process loyalty-member πŸ”΄ Invariant
publish-catalogue-requires-products publish-catalogue service catalogue πŸ”΄ Invariant
reserve-and-collect-requires-reservation reserve-and-collect process reservation πŸ”΄ Invariant
retail-media-requires-consent-aware-audience retail-media-attribution process sponsored-offer πŸ”΄ Invariant
return-anywhere-requires-return-window return-anywhere process return 🟑 Constraint
self-checkout-requires-active-terminal self-checkout process terminal πŸ”΄ Invariant
ship-from-store-requires-routed-order ship-from-store process order πŸ”΄ Invariant
ship-to-store-requires-routed-order ship-to-store process order πŸ”΄ Invariant
voucher-redemption-requires-valid-voucher voucher-redemption process voucher πŸ”΄ Invariant

??? details "Rule Descriptions"

**`bopis-requires-channel`** β€” BOPIS requires an active digital channel (web or mobile) as the ordering touchpoint.

*Condition:* `channel.lifecycle = active AND channel.type in [web, mobile-app]`

**`bopis-requires-store-inventory`** β€” BOPIS requires available inventory at the selected store before an order can be placed and confirmed.

*Condition:* `inventory-item.available-quantity >= 1 at selected store`

**`boris-requires-order-with-items`** β€” BORIS (Buy Online Return In Store) requires a closed online order with at least one item eligible for in-store return.

*Condition:* `order.lifecycle = closed AND order.count(order-line) >= 1`

**`capture-payment-requires-authorization`** β€” Payment capture can only execute against a Payment in authorized state. Attempting to capture an unauthorized payment must fail.

*Condition:* `payment.lifecycle = authorized`

**`checkout-requires-active-basket`** β€” The checkout process requires an active basket with at least one basket line. An empty or abandoned basket cannot proceed to checkout.

*Condition:* `basket.lifecycle = active AND basket.count(basket-line) >= 1`

**`checkout-requires-active-product`** β€” Every product referenced by a basket line must be active. Discontinued or inactive products cannot be checked out.

*Condition:* `product.lifecycle = active for each basket-line`

**`close-order-requires-fulfillment-complete`** β€” An order can only be closed when all its fulfillment orders have been either delivered or cancelled.

*Condition:* `all fulfillment-orders for order.lifecycle in [delivered, cancelled]`

**`confirm-reservation-requires-reserved`** β€” A reservation can only be confirmed from reserved state. Confirming an already-confirmed or expired reservation must fail.

*Condition:* `reservation.lifecycle = reserved`

**`customer-registration-requires-no-duplicate`** β€” Customer registration must not create duplicate profiles. Uniqueness is enforced on email address or phone number.

*Condition:* `no customer exists with same email or phone`

**`endless-aisle-requires-active-channel`** β€” Endless Aisle requires an active store channel to initiate the extended catalogue order.

*Condition:* `channel.lifecycle = active AND channel.type = store`

**`exchange-anywhere-requires-closed-order`** β€” Exchange Anywhere requires a closed order with at least one exchangeable item.

*Condition:* `order.lifecycle = closed AND order.count(order-line) >= 1`

**`issue-points-requires-closed-order`** β€” Loyalty points should only be issued against a closed order with a corresponding loyalty member record.

*Condition:* `order.lifecycle = closed AND loyalty-member exists for customer`

**`loss-prevention-intervention-requires-policy`** β€” A loss-prevention intervention requires recorded evidence and an authorised intervention policy; an anomaly alone does not establish culpability.

*Condition:* `loss-prevention-case.evidence-reference is not null AND loss-prevention-case.intervention-policy is not null`

**`loyalty-enrollment-requires-no-existing-account`** β€” A customer can only be enrolled in loyalty once. Re-enrollment is not valid if an active loyalty account already exists.

*Condition:* `customer has no existing loyalty-member record`

**`publish-catalogue-requires-products`** β€” A catalogue can only be published if it contains at least one active product.

*Condition:* `catalogue.count(product) >= 1 AND all products.lifecycle = active`

**`reserve-and-collect-requires-reservation`** β€” Reserve and Collect can only proceed if the customer's reservation is confirmed and has not expired.

*Condition:* `reservation.lifecycle = confirmed AND reservation.expiry > now()`

**`retail-media-requires-consent-aware-audience`** β€” Sponsored-offer activation and attribution require an approved audience policy and applicable customer consent when personal data is used.

*Condition:* `sponsored-offer.audience-policy is not null AND customer-consent.lifecycle = active when personal data is used`

**`return-anywhere-requires-return-window`** β€” Return Anywhere should only be processed within the defined return policy window from the original order date.

*Condition:* `order.lifecycle = closed AND order.date within return-policy-window`

**`self-checkout-requires-active-terminal`** β€” Self-checkout requires an active self-checkout terminal. Regular POS terminals cannot be used for self-checkout flow.

*Condition:* `terminal.lifecycle = active AND terminal.type = self-checkout`

**`ship-from-store-requires-routed-order`** β€” Ship From Store can only begin after an order has been confirmed and a fulfillment order has been created by the routing engine.

*Condition:* `order.lifecycle = confirmed AND fulfillment-order.lifecycle = created`

**`ship-to-store-requires-routed-order`** β€” Ship To Store can only begin after order confirmation and fulfillment order creation.

*Condition:* `order.lifecycle = confirmed AND fulfillment-order.lifecycle = created`

**`voucher-redemption-requires-valid-voucher`** β€” Voucher redemption requires an active, non-expired voucher with remaining balance.

*Condition:* `voucher.lifecycle = active AND voucher.expiry > now() AND voucher.balance > 0`

🚧 Transition Guard¢

What must be true for a lifecycle state transition to be permitted.

Rule Subject From To Condition Summary Severity
decision-execution-requires-approval decision-record approved executed decision-record.policy-reference is not null AND decision-re… πŸ”΄ Invariant
fulfillment-order-dispatch-requires-picking-complete fulfillment-order picking dispatched all picking-tasks.lifecycle = completed πŸ”΄ Invariant
loss-prevention-resolution-requires-outcome loss-prevention-case investigated resolved loss-prevention-case.resolution is not null πŸ”΄ Invariant
loyalty-account-close-requires-zero-balance loyalty-account active closed loyalty-account.balance = 0 🟑 Constraint
order-close-requires-all-fulfillment-done order confirmed closed all fulfillment-orders.lifecycle in [delivered, cancelled] πŸ”΄ Invariant
order-confirmed-requires-authorized-payment order validated confirmed payment.lifecycle = authorized πŸ”΄ Invariant
payment-void-from-authorized-only payment authorized voided order.lifecycle not in [closed] πŸ”΄ Invariant
product-deactivate-requires-no-active-basket-lines product active inactive no basket-line references this product with basket.lifecycle… 🟑 Constraint
reservation-expire-from-reserved reservation reserved expired reservation.expiry < now() πŸ”΄ Invariant

??? details "Rule Descriptions"

**`decision-execution-requires-approval`** β€” A material automated decision can execute only after approved authority and policy reference are recorded.

*Condition:* `decision-record.policy-reference is not null AND decision-record.reviewed-by is not null`

**`fulfillment-order-dispatch-requires-picking-complete`** β€” A Fulfillment Order can only be dispatched when all its Picking Tasks have been completed.

*Condition:* `all picking-tasks.lifecycle = completed`

**`loss-prevention-resolution-requires-outcome`** β€” A loss-prevention case cannot resolve without a verified outcome that is auditable after the transaction.

*Condition:* `loss-prevention-case.resolution is not null`

**`loyalty-account-close-requires-zero-balance`** β€” A Loyalty Account should only be closed after its point balance has been exhausted or transferred.

*Condition:* `loyalty-account.balance = 0`

**`order-close-requires-all-fulfillment-done`** β€” An Order can only close when every associated Fulfillment Order has reached a terminal state.

*Condition:* `all fulfillment-orders.lifecycle in [delivered, cancelled]`

**`order-confirmed-requires-authorized-payment`** β€” An Order can only transition to confirmed state if its associated Payment has been successfully authorized.

*Condition:* `payment.lifecycle = authorized`

**`payment-void-from-authorized-only`** β€” A Payment can only be voided from authorized state and only if its associated Order has not been closed.

*Condition:* `order.lifecycle not in [closed]`

**`product-deactivate-requires-no-active-basket-lines`** β€” A Product should not be deactivated while it is referenced by an active basket line.

*Condition:* `no basket-line references this product with basket.lifecycle = active`

**`reservation-expire-from-reserved`** β€” A Reservation can only expire from reserved state and only when its expiry timestamp has passed.

*Condition:* `reservation.expiry < now()`

⬇️ Post-ConditionΒΆ

What must be true after an operation has completed.

Rule Applies To Context Severity
authorize-payment-sets-authorized authorize-payment service πŸ”΄ Invariant
checkout-clears-basket checkout process πŸ”΄ Invariant
checkout-creates-order checkout process πŸ”΄ Invariant
loyalty-enrollment-creates-member loyalty-enrollment process πŸ”΄ Invariant
reserve-inventory-creates-reservation reserve-inventory service πŸ”΄ Invariant

??? details "Rule Descriptions"

**`authorize-payment-sets-authorized`** β€” After payment authorization, the Payment must be in authorized state with a valid authorization code.

*Condition:* `payment.lifecycle = authorized AND payment.authorization-code is not null`

**`checkout-clears-basket`** β€” After checkout completes, the originating basket must be in checked-out state and no longer mutable.

*Condition:* `basket.lifecycle = checked-out`

**`checkout-creates-order`** β€” After checkout completes, a confirmed Order must exist with the same number of lines as the originating basket.

*Condition:* `order.lifecycle = confirmed AND order.count(order-line) = basket.count(basket-line)`

**`loyalty-enrollment-creates-member`** β€” After loyalty enrollment, an active Loyalty Member record and a zero-balance Loyalty Account must exist for the customer.

*Condition:* `loyalty-member.lifecycle = active AND loyalty-account.balance = 0`

**`reserve-inventory-creates-reservation`** β€” After inventory reservation, a Reservation record must exist in reserved state with a valid expiry.

*Condition:* `reservation.lifecycle = reserved AND reservation.expiry > now()`

Generated from catalogs/validation-rules.yaml Β· Specification: specifications/015-semantic-model/constraint-vs-rule.md