> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solya.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Business rules engine

> The technical model behind rule evaluation: hooks, enforcement, paradigms, phases, the operations layer, and audit.

This is the technical companion to [Rules & rulesets](/en/intelligence-layer/rules-and-rulesets).

## Core enumerations

* **Enforcement mode** — `BLOCK` (reject), `WARN` (proceed + surface), `LOG` (silent
  audit).
* **Check level** — `SHOP`, `BRAND`, `PRODUCT`, `VARIANT`, `INVENTORY_ITEM`, `PLAN`,
  `PLAN_ITEM`.
* **Category** — `CAPACITY`, `ASSORTMENT`, `ORDERING`, `TRANSFER`, `PRICING`,
  `DISTRIBUTION`, `COMPLIANCE`.
* **Action family** — `RESTOCK_PLAN`, `REBALANCE_PLAN`, `MARKDOWN_PLAN`,
  `PRE_SEASON_PLAN`, `SUPPLIER_RETURN_PLAN`, `SUPPLIER_EXCHANGE_PLAN`, `RECOMMENDATION`,
  `WORKFLOW`.
* **Evaluation point** — `PRE_PLAN_ITEM_ADD`, `PRE_PLAN_VALIDATE`,
  `RECOMMENDATION_GENERATION`, `WORKFLOW_EXECUTION`, `MANUAL_CHECK`.

### Validation hooks

The exact hooks rules attach to:

```
RESTOCK_PRE_ADD_ITEM, RESTOCK_PRE_VALIDATE_ITEM, RESTOCK_PRE_CHANGE_STATUS,
REBALANCE_PRE_ADD_ITEM, REBALANCE_PRE_VALIDATE_ITEM, REBALANCE_PRE_CHANGE_STATUS,
MARKDOWN_PRE_ADD_ITEM, MARKDOWN_PRE_VALIDATE_ITEM,
PRE_SEASON_PRE_ADD_ITEM, PRE_SEASON_PRE_VALIDATE_ITEM, PRE_SEASON_PRE_CHANGE_STATUS,
SUPPLIER_RETURN_PRE_ADD_ITEM, SUPPLIER_RETURN_PRE_CHANGE_STATUS,
SUPPLIER_EXCHANGE_PRE_ADD_ITEM, SUPPLIER_EXCHANGE_PRE_CHANGE_STATUS,
RECOMMENDATION_PRE_GENERATE, MANUAL_CHECK
```

### Paradigm & phase

* **Paradigm** — `VALIDATION` rules are enforced by the app at hook time (block/warn);
  `DECISION_SHAPING` rules are applied only by the Databricks decision layer and are
  **skipped** by the app's evaluator.
* **Phase** — `SCORING`, `SIZING`, `SOURCING`, `APPROVAL`: an orthogonal axis used by the
  decision layer to scope evaluation to a planning step (nullable for backward
  compatibility).

## Evaluation

`evaluateBusinessRules(organizationId, hook, contexts)` resolves the applicable rules and
returns, per context, the **blocks** and **warnings**. Multi-shop mutations (rebalance,
supplier exchange) evaluate **once per shop** and aggregate. Each rule's condition is fed
by **context enrichers** — small resolvers that fetch live signals (plan totals, current
stock, sell-through, OTB remaining, size-curve deviation, supplier MOQ gap, markdown
margin, …); there are dozens of enricher types. The catalog of rule **templates** maps
each template to a category, applicable families, default check level, and default
condition.

## Operations layer

For mutations that need **async context resolution** (e.g. loading both shops for a
rebalance) or that have **multiple entry points** (action + workflow + API), the logic
lives in an **operations layer** built with `defineMutation`. An operation:

1. Optionally **bypasses** rules with an explicit, auditable `bypassRules: { reason }`.
2. Resolves `EvaluationContext[]` (async).
3. Evaluates rules; **blocks** abort before persistence (warnings still surface).
4. Runs a pure **handler** (persistence).
5. Writes the **audit** entry (best-effort).

```mermaid theme={null}
flowchart TD
  Start([Plan mutation invoked]) --> CheckBypass{Bypass rules<br/>requested?}
  CheckBypass -->|No| ResolveCtx["Resolve EvaluationContext[]"]
  CheckBypass -->|Yes - skip rules| Handler
  ResolveCtx --> EvalRules["Evaluate rules"]
  EvalRules --> HasBlocks{Any blocks?}
  HasBlocks -->|Yes| ReturnFail["Return failure<br/>abort mutation"]
  HasBlocks -->|No| Handler["Run handler<br/>persistence"]
  Handler --> Audit["Write audit entry<br/>best-effort"]
  Audit --> ReturnSuccess["Return success<br/>+ warnings"]
  ReturnFail --> End([Done])
  ReturnSuccess --> End
```

This is a pilot; representative migrated mutations include adding a rebalance item and
adding/validating a markdown item. Mutations without async rule context continue to use
the standard action → service path.

## Audit

Every plan mutation is recorded in an append-only audit log. **Event types** include
`PLAN_CREATED`, `PLAN_VALIDATED`, `PLAN_STATUS_CHANGED`, `PLAN_CLOSED`, `PLAN_DELETED`,
the approval lifecycle (`PLAN_APPROVAL_REQUESTED`, `PLAN_APPROVED`, `PLAN_REJECTED`,
`PLAN_APPROVAL_EXPIRED`), item events (`ITEM_ADDED`, `ITEM_UPDATED`, `ITEM_REMOVED`,
`ITEM_STATUS_CHANGED`), and configuration events (`RULESET_CHANGED`,
`PLAN_HEADER_UPDATED`). Each row records the **actor** (`USER`, `WORKFLOW`,
`SCENARIO_GENERATOR`, `SYSTEM`, `API_TOKEN`), the **source**, an optional reason, the
rules that applied, and — for items — an **attribution** (`MANUAL`, `DECISION_VECTOR`,
`WORKFLOW_FALLBACK`). When rules were bypassed, the reason is stored in the entry.

<Note>
  Human-readable labels (variant, shop, user names) are resolved at **read time**, not
  stored — so the audit trail stays accurate even as catalog data changes.
</Note>
