Evaluation insights
Explain condition outcomes and field proposals from an Editorial Workflows evaluation.
Prerelease
Editorial Workflows is a prerelease, built in public. Read How the prerelease works before you rely on it.
Editorial Workflows evaluates a running workflow instance to answer what the current actor can see and do now. The result is a WorkflowEvaluation: a read-only projection of the current stage, activities, transitions, editable fields, and action verdicts.
A direct engine integration receives this projection from engine.evaluate({instanceId}). A custom App SDK interface receives the same projection as session.evaluation from the reactive session. The session recomputes it when the workflow instance, referenced content, guards, or field previews change.
Insights are explanations nested inside an evaluation. A condition insight explains why one condition is satisfied, unsatisfied, or unevaluable. WorkflowEvaluation.fieldInsights groups those explanations by the fields that the conditions read.
Use insights to answer two questions in a custom interface or diagnostic: why is this action, requirement, transition, or field in its current state; and which condition outcomes would change if a field were set to a proposed value? You do not need insights to write conditions.
Insights describe the engine’s current advisory evaluation. They do not enforce access or replace Content Lake permissions.
Explain a waiting transition
Suppose a custom workflow screen must explain why its publish transition is waiting and what field changes would affect that decision. Pass the current WorkflowEvaluation to a helper that reads both the transition insight and the field proposals:
import type {ConditionOutcome, WorkflowEvaluation} from '@sanity/workflow-engine'
interface TransitionChange {
field: string
value: unknown
before: ConditionOutcome
after: ConditionOutcome
}
interface TransitionExplanation {
outcome: ConditionOutcome
blockedBy: string[]
changes: TransitionChange[]
}
export function explainTransition(
evaluation: WorkflowEvaluation,
transitionName: string,
): TransitionExplanation {
const evaluated = evaluation.currentStage.transitions.find(
({transition}) => transition.name === transitionName,
)
if (!evaluated) throw new Error(`Unknown transition: ${transitionName}`)
const changes = evaluation.fieldInsights.flatMap((field) =>
field.proposals.flatMap((proposal) =>
proposal.consequences
.filter(
({site}) => site.kind === 'transition' && site.transition === transitionName,
)
.map(({before, after}) => ({
field: field.field,
value: proposal.assign.value,
before,
after,
})),
),
)
return {
outcome: evaluated.insight.outcome,
blockedBy: evaluated.insight.blockedBy.map(({atom}) => atom.groq),
changes,
}
}Call explainTransition(session.evaluation, 'to-publishing'). The returned blockedBy list explains why the transition is waiting. Each changes entry names a proposed field value and the transition outcome before and after that value. These are verified condition consequences, not a prediction of document writes or external effects.
The outcome has three possible values:
- satisfied: the condition is true.
- unsatisfied: the condition is false.
- unevaluable: GROQ returned null because the condition could not be decided from the current values.
Where condition insights appear
Evaluated nodes expose the insight beside the decision it explains:
StartEvaluation.insight
ConditionInsight | undefined
Explains
start.allowedwhen the definition declares it.ActionEvaluation.insight
ConditionInsight | undefined
Explains an action
filterwhen declared.ActionEvaluation.whenInsight
ConditionInsight | undefined
Explains the
whentrigger for a cascade-fired action.ActivityEvaluation.filterInsight
ConditionInsight | undefined
Explains an activity
filterwhen declared.ActivityEvaluation.requirementInsights
Record<string, ConditionInsight> | undefined
Explains each named activity requirement when requirements are declared.
TransitionEvaluation.insight
ConditionInsight
Explains the transition’s
whencondition. Every transition evaluation includes it.EditableFieldEvaluation.insight
ConditionInsight | undefined
Explains a field’s effective editable predicate when it is conditional.
Read field insights
WorkflowEvaluation.fieldInsights provides the field-centric view of the same current-stage conditions. Use it to show where a field matters and which concrete assignments the engine verified would change an outcome.
A field can be involved in several conditions without having a useful proposal. Proposals appear only when a blocking atom identifies one concrete value and re-evaluation confirms that the assignment changes at least one involved site.
field
string
The rendered
$fieldsentry name.reads
ConditionRead[]
Every distinct read of that field across current-stage condition sites.
involvedIn
InsightSite[]
The actions, action triggers, activity filters, requirements, transitions, or editable fields whose conditions read it.
proposals
FieldProposal[]
Verified assignments and the condition sites whose outcomes they change. Empty when no single concrete assignment can be proposed.
Condition insight reference
analysis
ConditionAnalysis
The parsed condition, its atom tree, and the scope reads found in it.
outcome
'satisfied' | 'unsatisfied' | 'unevaluable'
The engine’s verdict for the complete condition.
atoms
AtomInsight[]
Every atomic predicate with its outcome, pivotal flag, and any derivable requirement.
frontier
ConditionClause | undefined
The remaining unmet clause tree. Absent when the condition is satisfied.
blockedBy
AtomInsight[]
A flat, source-ordered list of the unmet frontier. Empty when the condition is satisfied.
AtomInsight
atom
ConditionAtom
The atomic predicate. Use
atom.groqfor display.outcome
ConditionOutcome
The atom’s current verdict.
pivotal
boolean
True when satisfying this atom alone, while other atoms keep their current outcomes, would satisfy the condition.
requirement
AtomRequirement | undefined
A machine-readable requirement when the atom can be solved from one scope read and a literal value.
Exports and utilities
The insight types and utilities shown on this page are exported from @sanity/workflow-engine. Import only the shapes and functions your integration uses:
import type {
AtomInsight,
AtomRequirement,
ConditionInsight,
ConditionOutcome,
ConditionRead,
FieldInsight,
FieldProposal,
InsightSite,
ScopeAssignment,
SiteConsequence,
} from '@sanity/workflow-engine'
import {
analyzeCondition,
explainCondition,
whatIfCondition,
} from '@sanity/workflow-engine'Engine and reactive consumers usually read the insights already present on WorkflowEvaluation. Use these utilities only when you need to analyze or evaluate a condition independently. The package’s TypeScript declarations provide the exhaustive parameter documentation.
analyzeCondition(): ConditionAnalysis
Parses a condition and returns its clause tree, atoms, scope reads, and whether it reads the dataset.
explainCondition(): Promise<ConditionInsight>
Evaluates one condition against a supplied dataset and parameter scope, then returns its outcome and atom-level explanation.
whatIfCondition(): Promise<WhatIfOutcome>
Re-evaluates a condition with one scope value replaced and reports the outcome before and after the assignment.
Next steps
Use The reactive session to keep the evaluation current in a custom interface.
Use App SDK for a practical example that renders blocked transition atoms.
See Engine for the complete WorkflowEvaluation projection.
Visiting agent?
Editorial Workflows includes an MCP server for inspecting, operating, authoring, validating, and deploying workflows. Ask your human to set up the MCP server.