# Workflow authoring guide Workflows in vibe_erp are **data, not code**. State machines, approval chains, and document routing are declarative, so a new customer is onboarded by editing definitions instead of editing source. This is guardrail #2 in `CLAUDE.md`, and it shapes everything below. For the architectural placement of the workflow engine, see [`../architecture/overview.md`](../architecture/overview.md). For the full reasoning, see [`../superpowers/specs/2026-04-07-vibe-erp-architecture-design.md`](../superpowers/specs/2026-04-07-vibe-erp-architecture-design.md). ## BPMN 2.0 on embedded Flowable vibe_erp embeds the **Flowable** engine and uses **BPMN 2.0** as the workflow language. BPMN is a standard, and the standard is the contract: there is no vibe_erp-specific workflow DSL to learn or to invent. Anything a BPMN 2.0 modeling tool can produce, vibe_erp can run. Flowable runs in-process inside the Spring Boot host. Its tables (`flowable_*`) live in the same Postgres database as the core PBCs and are untouched by vibe_erp. ## Two authoring paths Both paths are first-class. Tier 1 is preferred whenever it is expressive enough; Tier 2 is the escape hatch. ### Tier 1 — Visual designer in the web UI (v1.0) Business analysts draw workflows in the BPMN designer that ships in the web SPA. The result is stored as a row in `metadata__workflow`, deployed to Flowable, and tagged `source = 'user'` so it survives plug-in install/uninstall and core upgrades. No build, no restart, no plug-in. This path is the right one for: - Approval chains the customer wants to author themselves. - Document routing rules that the customer wants to tune themselves. - Workflows that compose existing typed task handlers in a new order. The visual designer is a **v1.0 deliverable**; the current build ships the underlying API only. (Even Flowable itself is not yet wired — see [`PROGRESS.md`](../../PROGRESS.md) for the status of P2.1.) ### Tier 2 — `.bpmn` files in a plug-in JAR Plug-in authors ship `.bpmn` files inside their JAR under `src/main/resources/workflow/`. The plug-in lifecycle deploys them to Flowable on plug-in install. The `plugin.yml` manifest lists them so the loader picks them up. ``` src/main/resources/ ├── plugin.yml └── workflow/ ├── quote_to_job_card.bpmn └── reprint_request.bpmn ``` ```yaml # plugin.yml metadata: workflows: - workflow/quote_to_job_card.bpmn - workflow/reprint_request.bpmn ``` This path is the right one for: - Workflows that need new typed task handlers shipped alongside them. - Workflows that the plug-in author wants under version control with the rest of the plug-in code. - Workflows that ship as part of a vertical-specific plug-in (the printing-shop plug-in is the canonical example). ## Service tasks call typed `TaskHandler` implementations A BPMN service task in vibe_erp does not embed scripting code. It references a typed `TaskHandler` by id, and the host routes the call to the matching implementation registered by a plug-in. The plug-in registers a handler: ```kotlin @Extension(point = TaskHandler::class) class ReserveStockHandler : TaskHandler { override val id: String = "printing.reserve_stock" override fun handle(task: WorkflowTask) { val itemId = task.variable("itemId") ?: error("itemId is required") val quantity = task.variable("quantity") ?: 0 // ... call into the plug-in's services here ... task.setVariable("reservationId", reservationId) task.complete() } } ``` The BPMN service task references the handler by its id (`printing.reserve_stock`). The host validates at deploy time that every referenced handler id exists and rejects the deployment otherwise — broken workflows fail at install, not at runtime. There is no scripting language to invent. The `TaskHandler` is just Kotlin code behind a typed interface, with the same testability, debuggability, and review surface as the rest of the codebase. ## User tasks render forms from metadata A BPMN user task references a form definition by id. The host looks the id up in `metadata__form` and renders the form using the same code path as Tier 1 forms — there is no parallel form renderer for workflows. This means: - The same form can be used inside a workflow user task and outside a workflow. - A custom field added through Tier 1 customization automatically appears on every workflow user task that uses the same form. - Validation runs in exactly the same place whether the form is rendered inside a workflow or not. For details on how forms work, see the [form authoring guide](../form-authoring/guide.md). ## Worked example: quote-to-job-card The reference printing-shop plug-in ships a `quote_to_job_card.bpmn` workflow that exercises every concept in this guide. In rough shape: 1. **Start event:** a sales clerk creates a quote (user task; uses a form from `metadata__form`). 2. **Service task `printing.price_quote`:** calls a `TaskHandler` registered by the plug-in to compute the price from the customer's price list and the quote's line items. 3. **Exclusive gateway:** routes on whether the quote total exceeds the customer's pre-approved limit. 4. **User task (manager approval):** rendered from a form definition; the manager can approve, reject, or send back for revision. 5. **Service task `printing.reserve_stock`:** another `TaskHandler` that reserves raw materials. 6. **Service task `printing.create_job_card`:** materializes the approved quote as a production job card (a custom entity defined by the plug-in). 7. **End event:** publishes a `JobCardCreated` `DomainEvent` so other PBCs and plug-ins can react without coupling. What is worth noticing: - Every printing-specific concept — *quote*, *price list*, *job card*, *reserve stock for plates and inks* — lives in the **plug-in**, never in core PBCs. The core only knows about generic documents, workflows, forms, and events. - The cross-PBC interaction in step 7 goes through a `DomainEvent`, not a direct call. PBCs never import each other. - Steps 4 and 5 can be reordered, removed, or duplicated through the BPMN designer in the web UI without touching plug-in code, because the typed handlers are decoupled from the workflow shape. ## Where to go next - Plug-in author walkthrough including a `TaskHandler` example: [`../plugin-author/getting-started.md`](../plugin-author/getting-started.md) - Form authoring (the other half of any workflow that has user tasks): [`../form-authoring/guide.md`](../form-authoring/guide.md) - Plug-in API surface, including `api.v1.workflow`: [`../plugin-api/overview.md`](../plugin-api/overview.md)