The Classic Workflow Engine
SkillAI & modelsRead and change the classic Workflow engine (wf_workflow, wf_workflow_version, wf_activity, wf_transition, wf_context) — the checked-out-version model, running contexts, and when the right answer is to leave the workflow alone and build the new thing in Flow Designer.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the The Classic Workflow Engine skill
What this skill tells your AI
The instructions your AI receives, as published by serac-labs/serac in packages/skills/classic-workflow/SKILL.md and read by ahel’s review.
Flow Designer is what ServiceNow recommends and flow-designer is the skill for it. This is the other
engine — the one that still fulfils catalog items on most instances that have been live more than a few
years. blast-radius says outright that legacy workflows are not scanned, so an agent that cannot read
one is blind to a large part of what a record actually does.
Every tool here carries a ⚠️ LEGACY marker in its description. That is guidance about what to build,
not permission to ignore what exists.
The version model — read this before changing anything
This is the part that catches people out, and the tools do not hide it from you:
wf_workflow the workflow's identity. Name, table, description. Almost no content.
└── wf_workflow_version THE ACTUAL WORKFLOW. One per checkout. Has `published`.
├── wf_activity steps — belong to a VERSION, not to the workflow
└── wf_transition edges between activities — also per version
wf_context one running instance of a published version against one record
A workflow is not edited in place. Checking it out creates a new wf_workflow_version with
published = false; you edit that; publishing flips it to published = true and retires the previous
one. Records already running keep executing the version they started on — which is why a fix does not
apply to work in flight, and why wf_context rows can point at versions nobody can find in the UI.
So the first question about any classic workflow is which version:
await snow_query_table({
table: "wf_workflow_version",
query: "workflow.name=Standard Change Approval^published=true",
fields: "sys_id,name,published,sys_updated_on",
})
The trap in the write tools
snow_create_workflow_activity and snow_start_workflow both set the workflow_version field, and
both resolve a name by looking it up in wf_workflow — which returns a workflow sys_id, not a
version sys_id. wf_activity.workflow_version and wf_context.workflow_version reference
wf_workflow_version. Passing a name therefore points the new row at a version that does not exist.
Both parameters skip the lookup when you give them a 32-character hex sys_id, and that is the way to use them: resolve the version yourself and pass its sys_id.
const versions = await snow_query_table({ table: "wf_workflow_version",
query: "workflow=" + workflowSysId + "^published=true",
fields: "sys_id" })
const versionSysId = versions.records[0].sys_id
await snow_create_workflow_activity({ name: "Manager approval", workflowName: versionSysId,
activityType: "approval", order: 100 })
Even done correctly, adding an activity to a published version edits a live workflow underneath running contexts. The safe sequence is checkout → edit the unpublished version → publish, and checkout is a UI operation. If you cannot check out, do not write.
Reading one
snow_workflow_manage is the tool for everything except creation:
await snow_workflow_manage({ action: "list", table: "sc_req_item" }) // what runs on this table
await snow_workflow_manage({ action: "get", workflow_id: "Standard Change Approval" })
await snow_workflow_manage({ action: "get_history", context_id: ctxSysId })
get returns the workflow with its activities and transitions — enough to describe the graph without
opening the UI. list filtered by table is the fastest answer to "what else fires on this record", and
the one blast-radius cannot give you.
Running contexts
wf_context is one execution. It is where "the request is stuck" lives.
// everything currently executing for a workflow
await snow_query_table({ table: "wf_context",
query: "workflow_version.workflow.name=Standard Change Approval^state=executing",
fields: "id,state,started,workflow_version" })
await snow_workflow_manage({ action: "stop", context_id: ctx }) // cancels the execution
await snow_workflow_manage({ action: "retry", context_id: ctx }) // re-runs the failed activity
stop cancels the run and leaves the record where it is — approvals already generated are not withdrawn
and the record's state is not rolled back. Cancelling a context is not undoing a workflow; there is no
such operation. Clean up whatever the workflow created separately.
snow_workflow_analyze({ workflow_name, time_range_hours }) aggregates contexts over a window for error
and duration patterns. Use it before concluding a workflow is broken — "it never completes" is usually
one activity waiting on an approval nobody sees.
Starting one
await snow_start_workflow({ workflow_sys_id: versionSysId, table: "sc_req_item", record_sys_id: ritm })
It inserts a wf_context and returns. Workflows are asynchronous: a success here means the context was
created, not that anything ran. Poll wf_context.state rather than assuming.
Note the same version trap: pass a wf_workflow_version sys_id. If the call succeeds and nothing ever
runs, an unresolvable workflow_version is the first thing to check.
When to leave it alone
Classic workflows are a poor place to add behaviour and a fine place to read it. Prefer the honest answer when it applies:
- The change is new behaviour. Build it in Flow Designer, triggered on the same table, and leave the workflow untouched. Two engines on one record is normal on real instances.
- The workflow is not the actual problem. A catalog item that stalls is more often a missing approver or an inactive group than the graph.
- You cannot check out. Editing a published version under running contexts is not a change you can reason about.
The one case for editing it in place: the workflow is wrong and it must keep running for records that are mid-flight. Then it is checkout → edit the version → publish, and every context started before the publish keeps the old behaviour by design.
Related
flow-designer—sys_hub_*, the engine to build new automation in.blast-radius— does not scan classic workflows.snow_workflow_manage({ action: "list", table })is the manual step that fills that gap.update-set-workflow— a published workflow version is captured; a running context is not.
Signals
- GitHub stars
- 78
- Forks
- 26
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
classic-workflow- Source
- github.com/serac-labs/serac