Decisions & resolvers
Every automated and manual ruling in a case is captured as a Decision on state.decisions[]. When a decision needs to be settled — a policy hit approved, a re-run superseded — a decisionResolver links the settling decision to the one it clears. Together they form an auditable chain from 'flagged' to 'resolved'.
What a Decision can be
Each record carries a decisionKind, an output with a result (GREEN / YELLOW / BLUE / RED) and usually a code + policyId, and the input it was evaluated against.
SYSTEM_DECISION
Emitted by the process code itself — screening outcomes, "already approved" shortcuts, derived gates. No DMN table behind it.
DMN_POLICY_DECISION
One per policy rule that fires in a decision table (e.g. evaluate-application). Carries a code and policyId, and a result that gates the case.
HUMAN_DECISION
A caseworker’s explicit ruling — approve or reject a specific hit. Records who decided and, via a resolver, which policy hit it settles.
DMN_BUSINESS_DECISION
Not a gate — a rule that computes an output the process consumes, such as which documents to generate for a channel.
A decisionResolver is a small link — sourceId (the decision doing the resolving) → targetId (the hit being cleared) + a reason. Chains of them let a case re-evaluate safely: a re-fired BLUE hit is resolved by pointing back at the human approval that already cleared its identical predecessor.
One case, from start to approved application
A single hit, start to finish: evaluate-stakeholder raises a BLUE INCOME_DEVIATION, a caseworker assesses it, and a resolver links the approval back to the hit — turning it into a settled, auditable outcome.
Each arrow is a decisionResolver — source → target with its reason. Read left-to-right to see a hit assessed, then re-confirmed on re-assessment.
After each assessment the engine looks at every unresolved decision — the terminal end of each resolver chain. If they are all GREEN, the application is approved. Otherwise it acts on what remains: an unresolved BLUE asks a human to assess it, a YELLOW goes to manual handling, a RED rejects.
The screening tables pass cleanly. evaluate-stakeholder raises a BLUE INCOME_DEVIATION — the one thing needing a human.
The case is re-assessed whenever the picture changes — which can happen many times through the flow. Each re-run re-fires the hit as a fresh record, but isPolicyDecisionApproved() re-uses the caseworker’s approval as long as the DMN input is identical (matched on code::referenceId + unchanged input). Change the basis and the hit re-opens.