Guide / Checking
Every rule
One page for each check Penstock makes: why it matters and how to fix it.
How to read a rule page
Every check Penstock makes has a page. It says why it matters, how to fix it, and where you see it. Select a finding in the Problems tab and its link opens the page. In penstock check --format json, SARIF and GitHub annotations, every finding carries the rule id and the address of its page.
| Level | Meaning |
|---|---|
| Error | The diagram or the process is wrong. penstock check fails on it. |
| Advice | It works, and reads or runs better without it. It is shown as a note and does not fail the check. |
| Off by default | The rule exists but does nothing until you turn it on. |
152 rules in 8 groups: modelling rules, engine compatibility, drawing, process flow, decision tables, forms, diagrams and code, modelling conventions. The groups process flow, decision tables, diagrams and code, and modelling conventions are part of Pro. The rest is free.
Modelling rules
The bpmnlint rules that run on every BPMN diagram, in the editor and in penstock check. Which ones run, and how strictly, is set in .bpmnlintrc.
| Rule | What | Level |
|---|---|---|
bpmnlint/ad-hoc-sub-process | Ad-hoc sub-process with a start or end event | Error |
bpmnlint/conditional-event | Conditional event without a condition | Off by default |
bpmnlint/conditional-flows | Conditional flow without a condition | Error |
bpmnlint/end-event-required | Process or sub-process without an end event | Error |
bpmnlint/event-based-gateway | Event-based gateway used wrongly | Error |
bpmnlint/event-sub-process-typed-start-event | Event sub-process start without an event | Error |
bpmnlint/fake-join | Several flows enter a task that does not join them | Advice |
bpmnlint/global | Global element without a name, unused or not unique | Advice |
bpmnlint/label-required | Element without a label | Error |
bpmnlint/link-event | Link event without a name or without its counterpart | Error |
bpmnlint/no-bpmndi | Element without a place in the diagram | Error |
bpmnlint/no-complex-gateway | Complex gateway | Error |
bpmnlint/no-disconnected | Element with no incoming or outgoing flow | Error |
bpmnlint/no-duplicate-sequence-flows | Two identical flows between the same elements | Error |
bpmnlint/no-gateway-join-fork | Gateway that joins and forks | Error |
bpmnlint/no-implicit-end | Path that ends without an end event | Error |
bpmnlint/no-implicit-split | Task that forks the flow without a gateway | Error |
bpmnlint/no-implicit-start | Path that starts without a start event | Error |
bpmnlint/no-inclusive-gateway | Inclusive gateway | Advice |
bpmnlint/no-overlapping-elements | Element overlaps another or leaves its parent | Advice |
bpmnlint/single-blank-start-event | More than one blank start event | Error |
bpmnlint/single-event-definition | Event with several event definitions | Error |
bpmnlint/standard-size | Element not in its standard size | Off by default |
bpmnlint/start-event-required | Process or sub-process without a start event | Error |
bpmnlint/sub-process-blank-start-event | Sub-process start event with a type | Error |
bpmnlint/superfluous-gateway | Gateway with one way in and one way out | Advice |
bpmnlint/superfluous-label | Label on a flow that needs none | Advice |
bpmnlint/superfluous-termination | Terminate end event that terminates nothing | Advice |
Engine compatibility
What the engine the diagram is written for cannot run: a missing job type, an element or an expression the chosen version does not support. They run on Camunda 7 and Camunda 8 diagrams, in the editor and in penstock check, against the version in the file's modeler:executionPlatformVersion, or the newest one without it. Operaton and CIB seven are checked as Camunda 7. A finding is called compat/<rule> here and in penstock check; a rule is turned off in .bpmnlintrc as camunda-compat/<rule> or, the same, compat/<rule>.
| Rule | What | Level |
|---|---|---|
compat/ad-hoc-sub-process | Ad-hoc sub-process without an activity | Error |
compat/agent-fromai-contract | fromAi() key that is not a plain name under toolCall | Error |
compat/agent-tool-documentation | Tool of an AI agent without documentation | Advice |
compat/agent-tool-output-key | Tool of an AI agent that returns nothing to it | Advice |
compat/before-all-execution-listener | beforeAll listener outside a multi-instance element | Error |
compat/element-type | Element the engine version cannot run | Error |
compat/cancel-execution-listener | cancel listener on an element that cannot be cancelled | Error |
compat/called-element | Call activity without a process to call | Error |
compat/collapsed-subprocess | Collapsed sub-process | Error |
compat/connector-properties | Connector with a property this version does not have | Advice |
compat/duplicate-execution-listener-headers | Two headers of a listener with the same key | Error |
compat/duplicate-execution-listeners | Two execution listeners that are the same | Error |
compat/duplicate-task-headers | Two task headers with the same key | Error |
compat/error-reference | Error event without an error or an error code | Error |
compat/escalation-boundary-event-attached-to-ref | Escalation boundary event on the wrong element | Error |
compat/escalation-reference | Escalation event without an escalation or a code | Error |
compat/event-based-gateway-target | Receive task after an event-based gateway | Error |
compat/executable-process | No executable process | Error |
compat/execution-listener | Execution listener without an event type or a job type | Error |
compat/feel-compatibility | FEEL function the engine version does not have | Error |
compat/feel | FEEL expression that is not valid | Error |
compat/history-time-to-live | Process without a history time to live | Advice |
compat/implementation | Task or event that has nothing to run | Error |
compat/inclusive-gateway | Inclusive gateway with more than one way in | Error |
compat/link-event | Link events that do not match | Error |
compat/loop-characteristics | Multi-instance without what it needs | Error |
compat/io-mapping | Input or output mapping that is incomplete | Error |
compat/message-reference | Message event without a message or its name | Error |
compat/no-binding-type | Binding type that this version does not have | Error |
compat/no-business-id | Business id that this version does not have | Error |
compat/no-before-all-execution-listener | beforeAll listener that this version does not have | Error |
compat/no-candidate-users | Candidate users that this version does not have | Error |
compat/no-cancel-execution-listener | cancel listener that this version does not have | Error |
compat/no-execution-listener-headers | Listener headers that this version does not have | Error |
compat/no-execution-listeners | Execution listeners that this version does not have | Error |
compat/no-expression | Expression this version does not support | Error |
compat/no-interrupting-event-subprocess | Interrupting event sub-process in an ad-hoc sub-process | Error |
compat/no-job-priority-definition | Job priority that this version does not have | Error |
compat/no-loop | Loop that never waits | Error |
compat/no-multiple-none-start-events | More than one blank start event in Camunda 8 | Error |
compat/no-priority-definition | Priority that this version does not have | Error |
compat/no-propagate-all-parent-variables | Propagate all parent variables that this version does not have | Error |
compat/no-signal-event-sub-process | Signal event sub-process that this version does not have | Error |
compat/no-task-schedule | Task schedule that this version does not have | Error |
compat/no-task-listeners | Task listeners that this version does not have | Error |
compat/no-template | Element template that this version does not have | Error |
compat/no-version-tag | Version tag that this version does not have | Error |
compat/no-zeebe-properties | Zeebe properties that this version does not have | Error |
compat/no-zeebe-user-task | Camunda user task that this version does not have | Error |
compat/priority-definition | User task priority that is not valid | Error |
compat/zeebe-user-task | Job worker user task | Advice |
compat/secrets | Secret written in a form the engine no longer reads | Advice |
compat/sequence-flow-condition | Condition on a flow that cannot have one | Error |
compat/signal-reference | Signal event without a signal or its name | Error |
compat/start-event-form | Start event form that this version cannot show | Error |
compat/start-event-form-embedded | Embedded form on a start event | Advice |
compat/subscription | Message without a correlation key | Error |
compat/task-listener | Task listener without an event type or a job type | Error |
compat/task-schedule | Task schedule that is not a date | Error |
compat/timer | Timer that is not valid | Error |
compat/unresolvable-secret-reference | Secret used in a way the engine cannot resolve | Error |
compat/user-task-definition | User task without a form | Advice |
compat/user-task-form | User task form that is not filled in | Error |
compat/variable-name | Variable name that is not valid | Error |
compat/version-tag | Version tag that is empty | Error |
compat/wait-for-completion | Compensation that does not wait | Error |
Drawing
What is wrong with how a diagram is drawn, found by the check card in the editor and by the first section of penstock check. The errors break the picture; the others make it harder to read.
| Rule | What | Level |
|---|---|---|
drawing/dangling-connection | Connection without both ends | Error |
drawing/lost-connection | Connection in the file but not on the canvas | Error |
drawing/containment | Element outside the container it belongs to | Error |
drawing/overlap | Shapes sit on top of each other | Error |
drawing/edge-through-node | Connection runs through a shape | Error |
drawing/edge-through-boundary | Connection runs through a boundary event | Error |
drawing/crossing | Connections cross | Advice |
drawing/detached-edge | Connection does not reach the shape it connects | Error |
drawing/boundary-off-host | Boundary event is off its host | Error |
drawing/label-adrift | Label sits far from what it names | Advice |
drawing/diagonal-edge | Connection is not drawn at right angles | Advice |
drawing/label-overlap | Labels cover each other | Error |
drawing/merged-edge | Connections run along the same line | Advice |
drawing/note-adrift | Annotation sits far from what it describes | Advice |
drawing/semantic-drift | The diagram means something different | Error |
drawing/backtrack | Connection doubles back along itself | Error |
drawing/edge-through-label | Connection crosses a label | Advice |
drawing/edge-along-shape | Connection runs along the border of a shape | Advice |
drawing/detour | Connection takes a long way round | Advice |
drawing/jog | Connection sidesteps for no reason | Advice |
Process flow
What the way a process is wired can do when it runs. Part of Penstock Pro, and Team in a pipeline.
| Rule | What | Level |
|---|---|---|
flow/deadlock | The process can get stuck | Error |
flow/no-synchronization | A part of the process can run twice | Error |
flow/dead-element | An element can never run | Error |
flow/never-finishes | The process may never finish | Error |
flow/too-complex | Only part of the process was analysed | Advice |
flow/no-way-out | A decision has no way out for some values | Error |
flow/overlap | Two ways out of a decision can both hold | Error |
flow/no-default | A decision may have no way out | Advice |
flow/lookalike | Two variables look like one | Advice |
Decision tables
What a DMN decision table does with the inputs it can be given. Part of Penstock Pro, and Team in a pipeline.
| Rule | What | Level |
|---|---|---|
table/rule-overlap | Two rules match the same input | Error |
table/rule-gap | No rule matches some input | Advice |
table/table-unread | A decision table could not be checked | Advice |
Forms
What a Camunda Form does with what the user enters. The form check is lint, so it is free.
| Rule | What | Level |
|---|---|---|
form/key-clash | Two fields write the same variable | Error |
form/no-key | A field writes no variable | Error |
form/repeated-option | An option list repeats a value | Error |
form/no-label | A field has no label | Advice |
Diagrams and code
Where a diagram and the code that runs it disagree: a step nothing implements, a worker no diagram uses, a variable read under another name than it is set.
| Rule | What | Level |
|---|---|---|
code/missing-implementation | Nothing in the code runs a step | Advice |
code/never-thrown | Nothing in the code throws the error a step catches | Advice |
code/variable-typo | A variable is read, but only a similar one is set | Advice |
code/worker-fetch | A worker reads a variable it does not fetch | Advice |
code/writer-unread | A worker sets a variable no step after it reads | Advice |
code/lost-update | Every parallel instance writes the same result collection | Advice |
code/late-read | A worker reads a variable no step before it sets | Advice |
code/near-name | A job type, topic, bean, message or error code is one letter off the code | Advice |
code/mapping-order | A mapping line reads a value an earlier line was meant to set | Advice |
code/implicit-job-type | A job worker takes its job type from the method name | Advice |
code/no-worker | No worker handles a job type | Error |
code/no-topic-worker | No external task worker subscribes to a topic | Error |
code/class-missing | A class named in the diagram is not in the project | Error |
code/bean-missing | No Spring bean has the name a diagram uses | Error |
code/unknown-process | A call activity calls a process no diagram defines | Error |
code/unknown-decision | A business rule task calls a decision no file defines | Error |
code/unknown-form | A user task shows a form no file defines | Error |
code/unknown-embedded-form | A user task shows an embedded form the diagram does not contain | Error |
code/unused-worker | A worker for a job type no diagram uses | Advice |
code/unused-subscription | A subscription to a topic no diagram uses | Advice |
Modelling conventions
Conventions the editor applies while modelling, so a team does not have to remember them. They are set under conventions in penstock.yaml.
| Rule | What | Level |
|---|---|---|
convention/id-from-name | Ids follow names | Off by default |
convention/defaults | Defaults for new elements | Off by default |