Research
Partial12 minAI-native BPM: a process engine where the work is done by agents
Rootnia’s workflow engine is, without having called itself one, a BPM — of the agentic / AI-native family (iBPMS). It keeps the real backbone of a business-process engine (durable instances, gateways, human tasks, a scheduler) and puts AI agents where the service tasks are and a deterministic verifier jury where the business rules are.
BPM, iBPMS, agentic BPM
Rootnia drives its automations with a workflow engine that is, structurally, a business-process engine — a BPM. It executes a process as a directed acyclic graph in topological order, with branches, real parallelism, scoped loops, retries, error handling by edge, and human tasks that suspend and resume a running instance. Around that sits a full orchestration — scheduler, queue and worker pool — with durable instances in a persistent store, idempotent resume and crash recovery. That is the skeleton of a BPM.
What separates it from a classical BPM (Camunda, jBPM, Activiti) is not the skeleton but what each task does. In a classical BPM a service task means "call this microservice"; here, around 28 node subtypes are AI agents — extract, reason, classify, retrieve, OCR, vision, multi-agent swarm. And the business rules are not hand-coded: the flagship one is the verifier jury, a deterministic check of the agent’s output — for example, that an invoice’s arithmetic actually closes. Gartner calls this category iBPMS (intelligent BPMS); today it is named agentic BPM.
| Generation | What it is | Service tasks | Rules |
|---|---|---|---|
| BPM / BPMS | Business-process engine (BPMN) | Calls to systems / microservices | Hand-coded rules (DMN) |
| iBPMS (Gartner) | BPMS + analytics + ML + events | Plus embedded ML models | Rules + ML scoring |
| Agentic / AI-native (this engine) | Process engine where the work is done by AI agents, under deterministic control with a human in the loop | LLM agents (extract / reason / retrieve / swarm) | Symbolic verification (verifier jury) + gateways |
The point is deliberately not to reinvent the paradigm: we adopt BPM and put AI in place of the tasks and symbolic verification in place of the rules. That keeps the positioning honest — it is a real BPM in its backbone, with a clear and defensible differential in the tasks — and it is how the rest of this write-up is organised: the backbone, the differential, the formal mapping to BPMN, the same process in three views, what it already meets and what it does not, and the export layer.
The backbone: a real process engine
The engine executes a directed acyclic graph in topological order (Kahn’s algorithm), detecting cycles rather than looping forever. Nodes are a generic shape — an id, a type, a subtype, config — and edges carry a source handle and a field mapping. The engine itself does not know what any node does: it dispatches on the pair (type, subtype) against a registry, which is what lets agents, integrations and logic nodes coexist under one executor.
- Branching
- A node returns the handle it took; only the edges whose handle matches are activated. This is an exclusive gateway by another name.
- Inclusive gateway
- An OR-split activates every branch whose condition holds (0..N); the matching OR-join then runs once, over exactly the branches that fired — an unactivated branch is skipped and does not block it.
- Real parallelism
- A parallel node runs its branches on a thread pool, each over an isolated (deep-copied) context, and merges them serially at the join. The join waits for every live branch, and an approval that lands inside a parallel branch suspends the whole instance and resumes idempotently — the other branches’ completed nodes are not re-run.
- Loops & rework as scope
- Loop and for-each re-execute a dominated sub-graph with a hard iteration cap, rather than adding a cycle to the graph. Rework cycles (review → changes → correction) reuse the same scope machinery, so even a human send-back loop keeps the graph acyclic and termination guaranteed.
Orchestration is a scheduler, a queue and a worker pool, all persisted. The scheduler is a daemon that ticks on an interval (15 s by default) and only enqueues — it never executes. The queue is rows in the store marked QUEUED, claimed atomically in priority-then-age order. A worker pool (4 workers by default) claims, runs and persists, under a global concurrency cap (8 by default) and a per-workflow cap. The process definition is snapshotted immutably at enqueue time, which gives versioning for free. Every run — manual, scheduled or queued — is inspectable afterward in a first-class operations view: a queue and history with a concurrency semaphore, cancel and retry, and a drill-down into each instance’s lifecycle and per-node trace.
The property that makes it genuinely a BPM — not just a task runner — is the durable instance. Partial context is persisted (throttled) during a run; the whole context is serialisable; and resume is idempotent — it does not re-run nodes already marked SUCCESS, it only re-propagates. Crash recovery is a lease plus a heartbeat: a RUNNING execution whose lease expires is marked INTERRUPTED on restart rather than left as a zombie. A wait no longer blocks a worker — it suspends with a wake-up time that a sweeper re-wakes — and human tasks can carry durable boundary timers: a non-interrupting reminder, or an interrupting deadline that routes the flow onward. The distributed lock is pluggable (in-process by default). An instance moves through explicit states.
Instance states are first-class: PENDING, QUEUED, RUNNING, WAITING_APPROVAL, RETRYING, COMPLETED, COMPLETED_WITH_SKIPS, FAILED, CANCELLED, TIMED_OUT, INTERRUPTED. COMPLETED_WITH_SKIPS is deliberate honesty — a run that finished with skipped branches says so, rather than masking it as a clean COMPLETED.
The differential: AI tasks, symbolic verification
Two pieces turn the generic BPM into an AI-native BPM with guarantees.
First, the service tasks are agents. Around 28 of the engine’s ~110 node types are AI — the agent, agentic and knowledge families (extract, classify, reason, draft, retrieve, SQL, vision, OCR…) — and they export as BPMN service tasks flagged as AI tasks. The process stays a BPM any tool can read, but every such task declares that a model executes it. An “agent” here is a component with a defined job and a stable output contract, governed and grounded by the verifier jury — not any call to a model: autonomy is bounded (step caps, read-only tools) and any outward-effecting action is proposed for human approval, never taken unilaterally.
Second, the business rules are a deterministic verifier jury (exported as BPMN business-rule tasks) that checks the agent’s output on more than one axis — and runs no model in its own verification path, so the checker cannot hallucinate the way the generator can:
- Calculation consistency
- Exact arithmetic (money as integer cents, never float) over the invariants of a document: quantity × price = line amount, Σ − discounts + surcharges = subtotal, base × rate = tax, the totals, double-entry balance and the accounting equation — with per-document contracts (invoice, payroll, receipt…). One-sided: an inconsistency is certainty; consistency is not proof the document is true in the world.
- Coverage / anchoring
- Each extracted value is anchored back to the OCR of the source; a hard hallucination — a value that is not in the document — is blocked. Fail-closed: with no OCR or no contract it returns “review”, never “clean”. It detects invented values but does not certify a clean document.
- Contradictions
- A deterministic tribunal for relational, currency and reference-integrity contradictions (e.g. total = base + tax, net = gross − deductions). Now a third dimension of the verdict (coverage + calculation + contradictions), opt-in and shadow-by-default — visible but not gating until explicitly made authoritative. It checks integrity against the source, not truth in the world.
- Diagnosis
- On a failure, a minimal-conflict diagnosis names the likely culprit and, where OCR evidence exists, attributes the error to the document, the OCR or the model — and says “indeterminate” rather than blaming the model without evidence.
The guarantee is deliberately one-sided: a violation is certainty (an unsatisfiable set of equations cannot be satisfied), consistency is not truth, and where the jury cannot decide it abstains — a first-class “review”, never a false “all clear”. It can run in shadow (observe and record only) or authoritative (gate the flow by routing policy). The AI produces; the symbolic layer verifies and gates. Almost no BPM or agent product on the market puts these two things together.
The formal mapping: our graph ↔ BPMN 2.0
The export layer implements a canonical table: every node category is expressed as its BPMN 2.0 element, marking which are AI tasks. Abbreviated subtype lists below; the mapping is exhaustive in code.
| Node category | BPMN element | AI? | Event / marker |
|---|---|---|---|
| trigger — manual / schedule / webhook / email / document-incoming … | startEvent | — | none / timer / message |
| agent — llm, rag, reasoning, extractor, classifier, validator, vision, ocr, sql, code … | serviceTask | ✓ | — |
| agentic — supervisor, delegator, critic, consensus, reflection, routing, swarm, handoff … | serviceTask | ✓ | — |
| agentic — verifier, guardrail, validation, calculation, coverage | businessRuleTask | ✓ | verifier-jury gate |
| knowledge — vector_search, knowledge_base, rag_retrieval, embedding, memory, cache … | serviceTask | ✓ | — |
| data — set_variable, transform, json_parser, schema_mapper, filter, merge, split, aggregate … | scriptTask | — | — |
| logic — condition, switch, router, branch | exclusiveGateway | — | — |
| logic — parallel / join | parallelGateway | — | fork / join |
| logic — inclusive | inclusiveGateway | — | OR fork / join |
| logic — foreach, loop, retry, rework | subProcess | — | loop / multiInstance |
| logic — wait | intermediateCatchEvent | — | timer |
| logic — fallback, error_handler | serviceTask (error-edge target) | — | error (on edge) |
| integration — rest, graphql, sql databases, kafka, s3, email, teams, slack … | serviceTask | — | — |
| human — approval, review, request_input, assign_task, escalation, manual_decision | userTask | — | — |
| output — response, notification, send_email | sendTask | — | — |
| output — publish_event | intermediateThrowEvent | — | message |
| output — save_document, store_data, write_database, generate_file, archive … | serviceTask | — | — |
Edges become sequence flows. An edge is an error flow when its source handle is one of {error, onerror, catch, failure, exception} or its target is an error handler / fallback — replicating the engine’s own rule. When the source is an activity (a task), the error is modelled as a standard BPMN boundary event with an error event definition attached to that task — not a proprietary attribute; when the source is not an activity (a gateway, say), it degrades to a sequence flow labelled "error". A normal branch handle is exported as the sequence flow’s name, which is what BPMN tools show over the arrow.
A node’s AI-ness comes from its real (type, subtype), not a heuristic. An unknown (type, subtype) maps to a plain service task and is recorded in the export’s warnings — the layer never invents a type to make the picture look complete.
The same process, three views
Take invoice intake. The skeleton of the graph is identical in all three readings; the only thing that changes is what each box does — which is exactly what makes it fair to say "it is the same process" in each diagram.
The graph is an ordinary business process — any BPMN tool can read it. What makes it AI-native is the content of the boxes: extraction is done by an agent, and the decision gate is a verifier jury that checks the arithmetic deterministically before a human is asked to approve. The AI produces; the symbolic layer verifies; the person authorizes.
Read against classical BPMN, each box has a direct counterpart — the difference is purely in the implementation of the task:
| Box | Classical BPMN | Our layer |
|---|---|---|
| Invoice arrives | Start (message event) | trigger (document-incoming) |
| Extract entities | Service task | extractor agent (AI) |
| Does the math close? | Business-rule gateway (DMN) | verifier jury (symbolic verification) |
| Approve | User task | human approval → suspends the instance |
| Review / correct | Exception flow + task | error handler |
| Save + notify | Service / send task | save-document + notification |
And the third view is the export itself — conformant BPMN 2.0 any tool can open (illustrative fragment):
BPMN 2.0 export (fragment)<bpmn:process id="invoice_intake" name="Invoice intake"> <bpmn:startEvent id="n1" name="Invoice arrives"> <bpmn:messageEventDefinition/> </bpmn:startEvent> <bpmn:serviceTask id="n2" name="Extract entities" rootnia:aiTask="true"/> <bpmn:businessRuleTask id="n3" name="Verifier jury: calculation" rootnia:aiTask="true"/> <bpmn:userTask id="n4" name="Approve"/> <bpmn:serviceTask id="n5" name="Save + notify"/> <bpmn:sequenceFlow sourceRef="n3" targetRef="n4" name="closes"> <bpmn:conditionExpression>closes</bpmn:conditionExpression> </bpmn:sequenceFlow> <!-- boundary error event on n2/n3 → error handler; BPMNDI diagram omitted --> </bpmn:process>
What it already meets as a BPM
| BPM capability | Status | How |
|---|---|---|
| Process as a graph (nodes + edges) | ✅ | Topological DAG execution |
| Exclusive gateways | ✅ | condition / switch / router / branch |
| Parallel gateways (fork / join) | ✅ | Real threads, isolated contexts, serial merge |
| Loops / multi-instance | ✅ (as scope) | loop / for-each with a hard iteration cap |
| Start events (manual / timer / message) | ✅ | trigger subtypes |
| User tasks / approvals | ✅ | Suspends into WAITING_APPROVAL |
| Durable instance (suspend / resume) | ✅ ⭐ | Serialisable context, idempotent resume |
| Scheduler (cron / interval / once) | ✅ | With misfire handling (skip / run-once / catch-up) |
| Queue + worker pool + concurrency caps | ✅ | Atomic claim; global and per-workflow limits |
| Error handling (exception flow) | ✅ | Error edges, error handler / fallback |
| Retries (scope + execution level) | ✅ | retry scope and execution-level retry |
| Timeout / cancellation | ✅ | TIMED_OUT, cooperative cancel |
| Crash recovery | ✅ | Lease + heartbeat, orphan recovery → INTERRUPTED |
| Per-instance audit + metrics | ✅ | Per-node trace, execution metrics |
| Trigger idempotency | ✅ | Idempotency key with a unique index |
| Inclusive gateway (OR-split / OR-join) | ✅ | OR-join waits for exactly the activated branches |
| Parallel join + suspend inside a branch | ✅ | Join waits for all live branches; an approval inside a parallel branch suspends and resumes idempotently |
| Durable, non-blocking timers | ✅ | wait suspends without blocking a worker; boundary reminders (non-interrupting) and deadlines (interrupting) on human tasks |
| Human tasks: claim / delegate / SLA | ✅ | Competitive claim/unclaim/delegate (exactly-once), candidate users/groups, timeout → escalate |
| Rework cycles (review → correction) | ✅ | Durable, idempotent review→change→correction loop with a round cap |
| Document fan-out (per-document suspend/resume) | ✅ | Run once per document; selective resume of only the pending sub-runs, no re-extraction |
| Graph-semantics validation | ✅ | Static rejection of cycles / invalid edges; convergences synchronised by topological order |
| BPMN import (lossless round-trip) | ✅ | Preserves config, handles, mapping and timers; XSD-valid; secrets redacted; XXE and executable script rejected |
| Operations console (in-flight instances) | ✅ | Queue / history, concurrency semaphore, cancel / retry, drill-down to lifecycle + per-node trace |
| Process analytics (from real runs only) | ✅ | SLA-overdue, p50/p95 duration, error and rework rates, human-wait and verdict frequency — derived only from persisted executions, never fabricated |
| AI tasks (service task = agent) | ✅ ⭐ | ~28 AI subtypes within a ~110-node palette |
| Verified business rule (verifier jury) | ✅ ⭐ | Multi-dimension deterministic check → businessRuleTask |
⭐ marks the AI-native differentials. Everything else is ordinary, expected BPM machinery — which is the point: the backbone is real, not aspirational.
Gaps and the roadmap to a full iBPMS
Several items from the earlier roadmap are now implemented (see the checklist above). The gaps that remain are, deliberately, the heavy long-running-process plumbing that a host BPM engine already provides — so they are left to a host rather than rebuilt from scratch.
| Gap | Today | Direction | Effort |
|---|---|---|---|
| Long-running durable timers | Short waits and boundary timers on human tasks are durable — they suspend and a sweeper re-wakes them, without blocking a worker | Multi-day timers deferred to a host BPM rather than rebuilt | Host + add-on |
| Event correlation (broker) | Polling with a watermark per run; no permanent subscription | Wait-for-message by correlation key | Host + add-on |
| Compensation / sagas | COMPLETED_WITH_SKIPS is honest, but there is no rollback of effects already applied | Compensation handlers to undo upstream effects | Host + add-on |
| Reusable call-activity | Inline scopes (loops, rework, document fan-out) | A referenceable sub-process as a first-class citizen | Medium |
| Multi-pod operation | The timer sweeper and the human-task claim are atomic within one instance | A distributed lock for true multi-pod operation | Medium |
The deliberate line: the moat is the agentic layer plus the verifier jury, not re-implementing commodity BPM plumbing. The engine competes on its own backbone — durable instances, gateways, human tasks, a scheduler with a durable queue — and leaves the remaining long-running-process machinery to a host BPM when a deployment needs it. Measured against classical suites (Oracle BPM Suite, IBM Business Automation Workflow, Camunda), it matches them on that backbone, with honest gaps a host covers, and is alone on the agentic rows: AI service tasks and a deterministic verifier on their output.
The BPMN export layer
The export is a new, non-destructive module: it only reads process definitions and never changes the engine’s behaviour. Generation needs no external dependencies (standard-library XML); validation uses an optional library.
- ▸A taxonomy that maps each (type, subtype) to its BPMN element, AI flag, event definition (timer / message / none) and marker (loop / multi-instance / fork / join) — the table in the mapping section above.
- ▸A converter from the builder’s node-and-edge graph into an intermediate process model (elements, flows, warnings, start and end ids), exposing an AI-task count and a summary.
- ▸A renderer: a mermaid flowchart for the front-end and documentation, and conformant BPMN 2.0 XML — correct element tags, boundary events for error handling, an AI-task extension attribute, and a full BPMN diagram (BPMNDI) with auto-layout so it renders in bpmn.io or Camunda Modeler.
- ▸A validator against the official OMG BPMN 2.0 XSD bundle, so the generated XML is checked, not merely well-formed.
- ▸An importer that reads standard or rootnia-extended BPMN back into a runnable node-and-edge graph — a lossless round-trip for the implemented subset, hardened against XXE and executable script, with secrets never embedded.
This is wired as a real product capability, not a loose library: the workflow builder has a one-click BPMN export, and the workflow API returns any saved workflow as its process in BPMN XML and mermaid, with a summary (counts of AI tasks, gateways, human tasks, error flows). It is read-only and ownership-checked (identity from the token, the same anti-IDOR pattern as the rest of the API) — it never touches the engine.
Honest status: the mapping, the mermaid and BPMN 2.0 renderers (BPMNDI, boundary events), validation against the bundled official XSD, and the builder/API wiring are implemented and covered by tests. BPMN import is now a lossless round-trip for the implemented subset — it preserves node config, the exact branch handles, data mapping and timers in a versioned rootnia extension, stays XSD-valid, redacts secrets and rejects executable script and XXE — but it does not yet cover pools, lanes or collaboration.
References
- [1]OMG — Business Process Model and Notation (BPMN) 2.0
- [2]OMG — Decision Model and Notation (DMN)
- [3]Gartner — Intelligent Business Process Management Suites (iBPMS)
- [4]Camunda, jBPM, Activiti — classical BPMN process engines
- [5]Kahn (1962) — topological sorting of a directed acyclic graph
- [6]Double-entry bookkeeping and the accounting equation (calculation invariants)
References point to public, well-known technical sources for further study.
Related articles
