Camunda 7: assemble and fix executable BPMN
What this skill is for
A Camunda 7 .bpmn is a program the engine runs. The main job here is to
assemble a business process by wiring together activities that already exist —
reusable service tasks implemented as Java delegates or external-task topics — and
add the control flow (gateways, user tasks, DMN, timers, error handling, async)
that makes it deploy and run on Camunda Platform 7 (and the API-compatible CIB
seven fork).
This skill composes processes; it does not write activity implementations.
Writing a new activity (a delegate bean, a worker) is a separate task. When the
process needs a step that has no existing activity, you draw a deployable stub
(its name and input/output contract) and record it in a follow-up spec for
that separate task — you do not invent the Java/worker code here.
The four jobs people bring:
- Assemble a process (from text or a sketch) out of existing activities.
- Make a plain BPMN executable by wiring its tasks to existing activities (and
stubbing the rest).
- Edit / extend a Camunda process — add steps, branches, timers, error
handling — wiring existing activities and stubbing new ones.
- Validate & fix — find why it won't deploy or misbehaves, and repair it.
Division of labour:
- You decide the process shape and map each step to an existing activity
from the catalog, or — if none fits — to a stub plus a spec entry.
- The bundled
camunda-tool.mjs parses without losing camunda: data,
regenerates layout, validates structure, and lints execution readiness.
Inputs & outputs
What you give the skill:
- The task — a process description (free text) or a path to an existing
.bpmn to make executable / extend / fix.
- The project catalog — the reusable inventory + declarations to assemble
from. The canonical form is one JSON document (schema + template in
references/wiring-and-stubs.md and assets/catalog.template.json), but the
skill is flexible: it also accepts the catalog inline in the prompt, as
YAML, or as a Markdown table/list, and normalizes it to that schema.
All sections are optional. If no catalog is given, say so and infer one
from the repo — scan for JavaDelegate/@Component bean names,
camunda:topic strings, .dmn decision ids, .form files, existing
<bpmn:message>/<bpmn:error> declarations, and group names — then confirm the
inferred catalog with the user before building.
- Conventions — a
conventions block in the catalog, or inferred from the
project, or asked (see "Establish project conventions first").
What the skill returns:
- the executable
.bpmn (semantics + camunda: + regenerated DI), passing
validate and lint;
- a
<process>-activities-spec.md whenever anything was stubbed (activities /
forms / DMN decisions);
.form / .dmn files only if you explicitly ask the skill to build them
(normally those are referenced from the catalog or stubbed).
Whatever form the catalog arrives in, treat the JSON schema as the internal
model: map every entry onto it before wiring.
Reconciling the prompt with the catalog
The prompt is itself a catalog source — the user often names activities,
variables, messages, errors, roles or conventions inline. Merge those with the
catalog file, with two rules:
- Extra context in the prompt (something the prompt describes that the catalog
doesn't list): treat it as part of the catalog and use it — wire the
activity the user says exists (don't stub what they've already specified),
declare the message/error, use the variable names. Note in the follow-up that it
came from the prompt and suggest adding it to the catalog so it's reusable.
- Conflict (the prompt and the catalog describe the same entry
differently — different implementation type/ref/topic, variable type, message or
error code): do not silently pick one and move on, and never emit both. Use
the catalog as the source of truth for reusable definitions (it reflects what
actually exists in the project), keep the model internally consistent, and
surface the conflict explicitly in the follow-up — state both values, say
which you used and why, and ask the user to confirm (interactive) or flag it for
them to correct (non-interactive). The exception is when the user is clearly
overriding on purpose ("our X is actually a delegate now") — then prefer the
prompt, but still call out that it diverges from the catalog.
Three layers, and why generic tooling breaks Camunda files
A generic .bpmn has semantics (bpmn:process) and DI (bpmndi: — the
picture). A Camunda 7 file adds a third, the point of it all:
- Execution — the
camunda: attributes that bind each node to how it runs:
camunda:delegateExpression / camunda:type="external"+camunda:topic,
camunda:inputOutput, camunda:assignee, camunda:decisionRef,
camunda:asyncBefore, camunda:historyTimeToLive.
Consequences:
- Never hand-write DI. Edit semantics + execution; let
layout redraw it.
- Never round-trip a Camunda file through generic BPMN tooling. Plain
bpmn-moddle / bpmn-js / bpmn-auto-layout don't know camunda: and
silently delete every extension on save, turning an executable process back
into a picture. The bundled tool registers camunda-bpmn-moddle and grafts
fresh DI onto the extension-preserving XML, so execution data is never touched.
Setup (once per machine)
npm install --prefix "<SKILL_DIR>"
<SKILL_DIR> is the folder containing this SKILL.md. "Cannot find package" later
means the install was skipped — run it and retry.
Establish project conventions first
Before authoring, pin down the conventions that shape every node — the
defaults the whole process must follow. Determine them in this order, preferring
inference over interrogation:
- The catalog's
conventions block, if present (form mechanism,
service-task style, expression language, async/HTTL/retry defaults, naming).
- Project config & docs —
CLAUDE.md/README for house rules; Spring Boot
application.yaml (camunda.bpm.*) for the engine's HTTL default and history
level.
- Existing
.bpmn in the repo — run summarize on one or two and copy the
house style: camunda:formRef or generated formData? delegates or external
tasks? what id/topic/bean naming?
If a convention that materially changes the output still can't be inferred,
ask — briefly and batched. The two that matter most:
- Form mechanism: Camunda Forms (
.form via formRef, recommended on 7.15+)
/ embedded HTML (formKey="embedded:…") / generated formData / external form key.
- Service-task style for new/stub steps: external task / delegate / expression
/ connector.
(Also confirm, if unclear: expression conventions, async policy, the
historyTimeToLive and failedJobRetryTimeCycle defaults, naming.)
If you see a better approach than what's implied — e.g. the repo hand-writes
formData but Camunda Forms .form is cleaner on a 7.15+ engine, or a delegate
is simpler than an external task for in-process logic — say so in one line and
recommend it; let the user decide. In a non-interactive run, state the
conventions you assumed instead of blocking. State the conventions up front, then
apply them consistently to every node (and to stubs).
The reliable loop
For every job: establish conventions → get the catalog → map steps to catalog
entries (or stubs) → write semantics + execution → layout → validate → lint →
spec → fix.
Understand the inputs. For an existing file, run summarize (it shows each
node's implementation, async flags, forms, decisionRef, historyTTL, and
which nodes are stubs):
node "<SKILL_DIR>/scripts/camunda-tool.mjs" summarize path/to/file.bpmn
Get the project catalog — the reusable inventory and declarations to build
from: activities (delegates/topics), forms, variables (the data
dictionary), messages/signals/errors/escalations (event declarations),
decisions (DMN), roles (candidate groups/users), and conventions. The
user provides it (prompt or file); if none is given, ask for it, and only as a
last resort infer it from the repo. Reuse the catalog's exact names everywhere
(variable names, error codes, message names, group names) so the model is
consistent. The full catalog schema is in references/wiring-and-stubs.md.
Map each process step to an activity, then write semantics + camunda:.
For each service step: if the catalog has a matching activity, wire it
exactly (its delegate bean name or external topic, plus a
camunda:inputOutput mapping for the data it consumes/produces). If nothing
fits, make it a stub (next section). Hand-author a semantics-only document
(no bpmndi:) with the extensions inline. Always set isExecutable="true" and
camunda:historyTimeToLive. Get exact XML from the references — don't guess.
Regenerate layout (preserves camunda: extensions; non-destructive — it
re-syncs existing DI and only adds/reroutes what changed). Add --rebuild to
regenerate the whole diagram from scratch after big structural edits:
node "<SKILL_DIR>/scripts/camunda-tool.mjs" layout in.bpmn [out.bpmn] [--rebuild]
Validate structure:
node "<SKILL_DIR>/scripts/camunda-tool.mjs" validate out.bpmn
Lint execution + control flow:
node "<SKILL_DIR>/scripts/camunda-tool.mjs" lint out.bpmn
ERROR = won't deploy / will throw; WARN = deploys but misbehaves; INFO =
advisory (it also lists any stub activities so you remember the spec).
Write the follow-up spec for every stub (next section but one).
Don't claim done until validate and lint both pass (exit 0), and every stub
is in the spec.
Assemble from existing activities; stub the rest
This is the core behaviour, and the thing to get right:
Prefer existing activities. Each service/business-rule step should reuse a
catalog activity. Wire it with the catalog's exact reference —
camunda:delegateExpression="${beanName}" for a delegate, or
camunda:type="external" camunda:topic="the-topic" for an external task — and
add a camunda:inputOutput mapping for the variables it reads and writes, taken
from the catalog entry. Match the catalog's implementation style; don't convert
a delegate activity into an external one or vice-versa.
Do NOT write activity implementations. Building a delegate bean or a worker
is a separate task. While assembling/fixing a process you produce the .bpmn
(+ DMN/forms if asked) and, for new activities, a spec — never the .java/
worker code. If the user explicitly asks you to also implement an activity,
treat that as the separate task and only then use the implementation patterns in
references/wiring-and-stubs.md.
Stub a missing activity instead of inventing it. A stub is a real,
deployable node so the whole process can be laid out, validated and even
deployed as a skeleton — but it is clearly marked as "to build". Represent it as
a service task that is:
- named for the business step,
- bound as an external task with a clear
camunda:topic (kebab-case of the
name) — the most decoupled "awaiting a worker" placeholder; or, if the
project's catalog is delegate-based, camunda:delegateExpression="${proposedBeanName}",
- given a
camunda:inputOutput contract (the inputs it will consume and the
outputs it will produce — this is the interface the spec is written against),
- marked as a stub so tooling and humans can see it:
<bpmn:serviceTask id="SendRejection" name="Send rejection notice"
camunda:type="external" camunda:topic="send-rejection-notice">
<bpmn:documentation>STUB — to implement (see activities spec). In: applicantId, reason. Out: notificationId.</bpmn:documentation>
<bpmn:extensionElements>
<camunda:properties><camunda:property name="stub" value="true" /></camunda:properties>
<camunda:inputOutput>
<camunda:inputParameter name="applicantId">${applicantId}</camunda:inputParameter>
<camunda:inputParameter name="reason">${rejectionReason}</camunda:inputParameter>
<camunda:outputParameter name="notificationId">${notificationId}</camunda:outputParameter>
</camunda:inputOutput>
</bpmn:extensionElements>
</bpmn:serviceTask>
summarize then tags it {STUB} and lint lists it under INFO. Tell the user,
in plain language, which steps you wired from the catalog and which you stubbed.
Be honest that a stub is a deployable skeleton, not a runnable process: the
engine will wait forever at an external-task stub (no worker subscribed), and
a stub formRef shows "form not found" in Tasklist. It deploys and validates;
it won't run end-to-end until the stub is built. Say this in the follow-up.
Every user task gets assignment AND a form. Forms work exactly like
activities: a user task is wired to a form from the catalog's forms (set
camunda:formRef="<formId>" camunda:formRefBinding="latest", or a
camunda:formKey). If no catalog form fits, form-stub it — point
camunda:formRef at a proposed form id, mark it
<camunda:property name="formStub" value="true"> with a
<bpmn:documentation>FORM STUB — fields: …</bpmn:documentation>, and add the
form (its id + fields, derived from the task's decision/data) to the follow-up
spec. lint warns about any user task with no form and lists form-stubs under
INFO. Don't leave a user task formless.
Reuse the catalog's declarations and variable names. Wire user-task
assignment to catalog roles (candidate groups/users); business rule tasks to
catalog decisions (camunda:decisionRef); and message/signal/error/escalation
events to catalog declarations — creating the matching root <bpmn:message> /
<bpmn:signal> / <bpmn:error errorCode> / <bpmn:escalation> from the catalog
entry (these are declarations you write directly, not implementations to stub).
Name process variables and every camunda:inputOutput parameter from the
catalog's variables dictionary, not invented synonyms — consistent names are
what make the steps actually wire together. A referenced message/error/role not
in the catalog: add it and flag it. A missing DMN decision is stubbed like
an activity (business rule task pointing at a proposed decisionRef + a spec
entry).
The follow-up spec for new activities & forms
For every stub activity and every form-stub, write (or append to) a spec — a
Markdown file beside the .bpmn, e.g. <process>-activities-spec.md. It is the
handoff for the separate implementation task. Use the template
assets/activity-spec.template.md. Per activity entry: name, the BPMN node
id/topic, business purpose, inputs (name / type / source variable), outputs
(name / type), error outcomes (BPMN error codes), and the suggested
implementation type (delegate vs external worker) consistent with the catalog.
Per form entry: the form id (the one you put in camunda:formRef), the user
task it belongs to, and its fields (key / label / type, required?). End with a
checklist. Keep it precise enough to build each activity/form from the spec alone
without re-reading the diagram.
Report back: the follow-up
End every run with a short, skimmable follow-up — a handoff, not a wall of text.
Three sections; omit any that are empty (except Done); keep each bullet to one
line; the detail lives in the spec file, not here. Match the user's language.
Done — what you assembled/changed and that it's verified: the result file +
validate/lint pass; which steps you wired from the catalog; what you added
(gateways, error handling, timers, forms); the conventions applied.
Needs you — concrete asks, highest-impact first: stub activities to implement
and form-stubs to create (→ <process>-activities-spec.md); data gaps (a
variable a step needs that nothing upstream produces yet); conventions/assumptions
to confirm; deploy steps (e.g. deploy the .form/.dmn alongside the .bpmn). If
there's nothing, say "Deployable as is."
Watch out — narrow spots / risks that could bite at runtime: runtime deps
(e.g. a Groovy script engine), interrupting vs non-interrupting choices,
async/transaction trade-offs, anything you assumed rather than knew. Omit if none.
Aim for a handful of bullets total. If you stubbed things, the single most useful
line is the pointer to the spec.
Reference files — read the one you need, don't guess XML
| When you're… |
Read |
| Wiring existing activities and forms, representing stubs/form-stubs, the catalog format, or the spec |
references/wiring-and-stubs.md |
| Writing the exact XML for service/user/rule tasks, listeners, I/O, async, events, call activity, process attrs |
references/camunda-extensions.md |
| Diagnosing a deploy/runtime failure, or want the anti-pattern checklist + C7-vs-C8 cheat-sheet |
references/execution-checklist.md |
| Form mechanics (formRef/.form, formKey, formData) or DMN decision tables |
references/forms-and-dmn.md |
| Deploying (REST / Spring Boot / Modeler) or writing JUnit 5 + camunda-bpm-assert tests |
references/deployment-and-testing.md |
assets/ has templates: an executable process skeleton (a wired activity + a
stub), a DMN table, a Camunda Form, the catalog format, the activities spec, and —
for the separate implementation task only — JavaDelegate / external-worker
skeletons.
Modeling for the engine, not just the picture
- Always
isExecutable="true" + camunda:historyTimeToLive (e.g. P30D) on
a runnable process — since 7.20 the engine rejects deployment without TTL.
DMN <decision>s need it too.
- Expressions are JUEL (
${...}) in BPMN attributes; FEEL only in DMN.
- Pair and guard gateways. Give a diverging exclusive/inclusive gateway a
default flow; never put conditions on a parallel gateway's outgoing flows.
- Transaction boundaries where failure must be isolated:
camunda:asyncBefore="true" on external/long activities, loops, before waits;
add camunda:failedJobRetryTimeCycle. Keep camunda:exclusive="true".
- Declare what you reference (
message/signal/error roots; errors need an
errorCode). Correlation is by name, not id.
- Camunda 7, never 8. Use
camunda: (URI http://camunda.org/schema/1.0/bpmn),
never zeebe:, never executionPlatform="Camunda Cloud". If the user actually
wants Zeebe, say so and stop.
Layout: non-destructive by default
layout preserves camunda: extensions on every path. It is also
non-destructive: if the file already has DI it re-syncs — keeps your
existing geometry, drops shapes for deleted elements, auto-places new ones, and
reroutes only stale edges (so hand-tuned diagrams survive an edit). Pass
--rebuild to discard all DI and regenerate from scratch (use after big
structural changes, or when a diagram is messy). It lays out single processes,
collaborations (stacked pools + message flows), lanes (swimlane bands),
and data objects / text annotations / associations; sub-processes are kept as
drill-down planes. Only groups are not auto-placed. If a request leans on
groups, place them in a modeler or say so.
Bundled tools
| Command |
Purpose |
summarize <file> [--json] |
Outline + per-node implementation, stub markers, async, forms, decisionRef, historyTTL |
layout <in> [out] [--rebuild] |
Safe layout: re-sync existing DI (or generate); always preserves camunda: extensions |
validate <file> |
Parse, flag missing shapes / overlaps / parse warnings (plane-aware) |
lint <file> |
Control-flow + structural + Camunda execution problems (ERROR/WARN/INFO); lists stubs |
diff <a> <b> |
Semantic + implementation diff (added/removed/renamed/retyped/rewired/impl-changed) |
find <file> <term> |
Find flow elements by name/type |
All live in scripts/camunda-tool.mjs (engine in scripts/lib.mjs; tests:
npm test).
1---2name: camunda73description: Use this skill when a Camunda Platform 7 `.bpmn` must actually deploy and run on the engine — not just look right as a diagram. Covers the C7 engine, the CIB seven fork, and Spring Boot camunda-bpm starters. Reach for it to: assemble an executable process by wiring existing service tasks (Java delegates, external-task topics) from a catalog and stubbing steps that don't exist yet; make a plain diagram deployable; add or edit gateways, user tasks + forms, DMN, boundary/timer & escalation events, error handling, async, candidate groups, historyTimeToLive; or fix why a process won't deploy or misbehaves at runtime (historyTimeToLive cannot be null, a service task with no implementation, broken delegateExpression wiring, a gateway running everything twice after a merge). Signals: Camunda 7, camunda-bpm, CIB seven, camunda:*, JavaDelegate, external task, business rule task. Not for engine-agnostic diagrams (use bpmn) or Camunda 8 / Zeebe (zeebe:, FEEL).4---56# Camunda 7: assemble and fix executable BPMN78## What this skill is for910A Camunda 7 `.bpmn` is a program the engine runs. The main job here is to11**assemble a business process by wiring together activities that already exist** —12reusable service tasks implemented as Java delegates or external-task topics — and13add the control flow (gateways, user tasks, DMN, timers, error handling, async)14that makes it deploy and run on Camunda Platform 7 (and the API-compatible CIB15seven fork).1617**This skill composes processes; it does not write activity implementations.**18Writing a new activity (a delegate bean, a worker) is a *separate task*. When the19process needs a step that has no existing activity, you draw a deployable **stub**20(its name and input/output contract) and record it in a **follow-up spec** for21that separate task — you do not invent the Java/worker code here.2223The four jobs people bring:2425- **Assemble a process** (from text or a sketch) out of existing activities.26- **Make a plain BPMN executable** by wiring its tasks to existing activities (and27 stubbing the rest).28- **Edit / extend** a Camunda process — add steps, branches, timers, error29 handling — wiring existing activities and stubbing new ones.30- **Validate & fix** — find why it won't deploy or misbehaves, and repair it.3132Division of labour:33- **You** decide the process shape and **map each step to an existing activity34 from the catalog**, or — if none fits — to a stub plus a spec entry.35- **The bundled `camunda-tool.mjs`** parses without losing `camunda:` data,36 regenerates layout, validates structure, and lints execution readiness.3738## Inputs & outputs3940**What you give the skill:**41- **The task** — a process description (free text) *or* a path to an existing42 `.bpmn` to make executable / extend / fix.43- **The project catalog** — the reusable inventory + declarations to assemble44 from. The **canonical form is one JSON document** (schema + template in45 `references/wiring-and-stubs.md` and `assets/catalog.template.json`), but the46 skill is flexible: it also accepts the catalog **inline in the prompt**, as47 **YAML**, or as a **Markdown table/list**, and normalizes it to that schema.48 All sections are optional. If **no catalog is given**, say so and **infer one49 from the repo** — scan for `JavaDelegate`/`@Component` bean names,50 `camunda:topic` strings, `.dmn` decision ids, `.form` files, existing51 `<bpmn:message>`/`<bpmn:error>` declarations, and group names — then confirm the52 inferred catalog with the user before building.53- **Conventions** — a `conventions` block in the catalog, or inferred from the54 project, or asked (see "Establish project conventions first").5556**What the skill returns:**57- the executable **`.bpmn`** (semantics + `camunda:` + regenerated DI), passing58 `validate` and `lint`;59- a **`<process>-activities-spec.md`** whenever anything was stubbed (activities /60 forms / DMN decisions);61- `.form` / `.dmn` files only if you explicitly ask the skill to build them62 (normally those are referenced from the catalog or stubbed).6364Whatever form the catalog arrives in, treat the JSON schema as the internal65model: map every entry onto it before wiring.6667## Reconciling the prompt with the catalog6869The prompt is itself a catalog source — the user often names activities,70variables, messages, errors, roles or conventions inline. Merge those with the71catalog file, with two rules:7273- **Extra context in the prompt** (something the prompt describes that the catalog74 doesn't list): treat it as part of the catalog and **use it** — wire the75 activity the user says exists (don't stub what they've already specified),76 declare the message/error, use the variable names. Note in the follow-up that it77 came from the prompt and suggest adding it to the catalog so it's reusable.78- **Conflict** (the prompt and the catalog describe the **same** entry79 differently — different implementation type/ref/topic, variable type, message or80 error code): do **not** silently pick one and move on, and never emit both. Use81 the **catalog as the source of truth** for reusable definitions (it reflects what82 actually exists in the project), keep the model internally consistent, and83 **surface the conflict explicitly** in the follow-up — state both values, say84 which you used and why, and ask the user to confirm (interactive) or flag it for85 them to correct (non-interactive). The exception is when the user is clearly86 overriding on purpose ("our X is actually a delegate now") — then prefer the87 prompt, but still call out that it diverges from the catalog.8889## Three layers, and why generic tooling breaks Camunda files9091A generic `.bpmn` has **semantics** (`bpmn:process`) and **DI** (`bpmndi:` — the92picture). A Camunda 7 file adds a third, the point of it all:93943. **Execution** — the `camunda:` attributes that bind each node to how it runs:95 `camunda:delegateExpression` / `camunda:type="external"`+`camunda:topic`,96 `camunda:inputOutput`, `camunda:assignee`, `camunda:decisionRef`,97 `camunda:asyncBefore`, `camunda:historyTimeToLive`.9899Consequences:100- **Never hand-write DI.** Edit semantics + execution; let `layout` redraw it.101- **Never round-trip a Camunda file through generic BPMN tooling.** Plain102 `bpmn-moddle` / `bpmn-js` / `bpmn-auto-layout` don't know `camunda:` and103 **silently delete every extension on save**, turning an executable process back104 into a picture. The bundled tool registers `camunda-bpmn-moddle` and grafts105 fresh DI onto the extension-preserving XML, so execution data is never touched.106107## Setup (once per machine)108109```bash110npm install --prefix "<SKILL_DIR>"111```112`<SKILL_DIR>` is the folder containing this SKILL.md. "Cannot find package" later113means the install was skipped — run it and retry.114115## Establish project conventions first116117Before authoring, pin down the **conventions** that shape every node — the118defaults the whole process must follow. Determine them in this order, preferring119inference over interrogation:1201211. **The catalog's `conventions` block**, if present (form mechanism,122 service-task style, expression language, async/HTTL/retry defaults, naming).1232. **Project config & docs** — `CLAUDE.md`/README for house rules; Spring Boot124 `application.yaml` (`camunda.bpm.*`) for the engine's HTTL default and history125 level.1263. **Existing `.bpmn` in the repo** — run `summarize` on one or two and copy the127 house style: `camunda:formRef` or generated `formData`? delegates or external128 tasks? what id/topic/bean naming?129130If a convention that materially changes the output still can't be inferred,131**ask** — briefly and batched. The two that matter most:132- **Form mechanism**: Camunda Forms (`.form` via `formRef`, recommended on 7.15+)133 / embedded HTML (`formKey="embedded:…"`) / generated `formData` / external form key.134- **Service-task style for new/stub steps**: external task / delegate / expression135 / connector.136(Also confirm, if unclear: expression conventions, async policy, the137`historyTimeToLive` and `failedJobRetryTimeCycle` defaults, naming.)138139If you see a **better approach** than what's implied — e.g. the repo hand-writes140`formData` but Camunda Forms `.form` is cleaner on a 7.15+ engine, or a delegate141is simpler than an external task for in-process logic — say so in one line and142recommend it; let the user decide. In a non-interactive run, state the143conventions you assumed instead of blocking. State the conventions up front, then144apply them consistently to every node (and to stubs).145146## The reliable loop147148For every job: **establish conventions → get the catalog → map steps to catalog149entries (or stubs) → write semantics + execution → layout → validate → lint →150spec → fix**.1511521. **Understand the inputs.** For an existing file, run `summarize` (it shows each153 node's implementation, async flags, forms, decisionRef, historyTTL, **and154 which nodes are stubs**):155 ```bash156 node "<SKILL_DIR>/scripts/camunda-tool.mjs" summarize path/to/file.bpmn157 ```158 **Get the project catalog** — the reusable inventory and declarations to build159 from: `activities` (delegates/topics), `forms`, `variables` (the data160 dictionary), `messages`/`signals`/`errors`/`escalations` (event declarations),161 `decisions` (DMN), `roles` (candidate groups/users), and `conventions`. The162 user provides it (prompt or file); if none is given, ask for it, and only as a163 last resort infer it from the repo. Reuse the catalog's exact names everywhere164 (variable names, error codes, message names, group names) so the model is165 consistent. The full catalog schema is in `references/wiring-and-stubs.md`.1661672. **Map each process step to an activity, then write semantics + `camunda:`.**168 For each service step: if the catalog has a matching activity, **wire it169 exactly** (its delegate bean name or external topic, plus a170 `camunda:inputOutput` mapping for the data it consumes/produces). If nothing171 fits, make it a **stub** (next section). Hand-author a semantics-only document172 (no `bpmndi:`) with the extensions inline. Always set `isExecutable="true"` and173 `camunda:historyTimeToLive`. Get exact XML from the references — don't guess.1741753. **Regenerate layout** (preserves `camunda:` extensions; non-destructive — it176 re-syncs existing DI and only adds/reroutes what changed). Add `--rebuild` to177 regenerate the whole diagram from scratch after big structural edits:178 ```bash179 node "<SKILL_DIR>/scripts/camunda-tool.mjs" layout in.bpmn [out.bpmn] [--rebuild]180 ```1811824. **Validate** structure:183 ```bash184 node "<SKILL_DIR>/scripts/camunda-tool.mjs" validate out.bpmn185 ```1861875. **Lint** execution + control flow:188 ```bash189 node "<SKILL_DIR>/scripts/camunda-tool.mjs" lint out.bpmn190 ```191 ERROR = won't deploy / will throw; WARN = deploys but misbehaves; INFO =192 advisory (it also lists any **stub** activities so you remember the spec).1931946. **Write the follow-up spec** for every stub (next section but one).195196Don't claim done until `validate` and `lint` both pass (exit 0), and every stub197is in the spec.198199## Assemble from existing activities; stub the rest200201This is the core behaviour, and the thing to get right:202203- **Prefer existing activities.** Each service/business-rule step should reuse a204 catalog activity. Wire it with the catalog's exact reference —205 `camunda:delegateExpression="${beanName}"` for a delegate, or206 `camunda:type="external" camunda:topic="the-topic"` for an external task — and207 add a `camunda:inputOutput` mapping for the variables it reads and writes, taken208 from the catalog entry. Match the catalog's implementation style; don't convert209 a delegate activity into an external one or vice-versa.210211- **Do NOT write activity implementations.** Building a delegate bean or a worker212 is a separate task. While assembling/fixing a process you produce the `.bpmn`213 (+ DMN/forms if asked) and, for new activities, a spec — never the `.java`/214 worker code. If the user explicitly asks you to also implement an activity,215 treat that as the separate task and only then use the implementation patterns in216 `references/wiring-and-stubs.md`.217218- **Stub a missing activity** instead of inventing it. A stub is a real,219 deployable node so the whole process can be laid out, validated and even220 deployed as a skeleton — but it is clearly marked as "to build". Represent it as221 a service task that is:222 - named for the business step,223 - bound as an **external task** with a clear `camunda:topic` (kebab-case of the224 name) — the most decoupled "awaiting a worker" placeholder; *or*, if the225 project's catalog is delegate-based, `camunda:delegateExpression="${proposedBeanName}"`,226 - given a **`camunda:inputOutput` contract** (the inputs it will consume and the227 outputs it will produce — this is the interface the spec is written against),228 - **marked as a stub** so tooling and humans can see it:229 ```xml230 <bpmn:serviceTask id="SendRejection" name="Send rejection notice"231 camunda:type="external" camunda:topic="send-rejection-notice">232 <bpmn:documentation>STUB — to implement (see activities spec). In: applicantId, reason. Out: notificationId.</bpmn:documentation>233 <bpmn:extensionElements>234 <camunda:properties><camunda:property name="stub" value="true" /></camunda:properties>235 <camunda:inputOutput>236 <camunda:inputParameter name="applicantId">${applicantId}</camunda:inputParameter>237 <camunda:inputParameter name="reason">${rejectionReason}</camunda:inputParameter>238 <camunda:outputParameter name="notificationId">${notificationId}</camunda:outputParameter>239 </camunda:inputOutput>240 </bpmn:extensionElements>241 </bpmn:serviceTask>242 ```243 `summarize` then tags it `{STUB}` and `lint` lists it under INFO. Tell the user,244 in plain language, which steps you wired from the catalog and which you stubbed.245 **Be honest that a stub is a deployable *skeleton*, not a runnable process:** the246 engine will **wait forever** at an external-task stub (no worker subscribed), and247 a stub `formRef` shows "form not found" in Tasklist. It deploys and validates;248 it won't run end-to-end until the stub is built. Say this in the follow-up.249250- **Every user task gets assignment AND a form.** Forms work exactly like251 activities: a user task is wired to a form from the catalog's `forms` (set252 `camunda:formRef="<formId>" camunda:formRefBinding="latest"`, or a253 `camunda:formKey`). If no catalog form fits, **form-stub** it — point254 `camunda:formRef` at a proposed form id, mark it255 `<camunda:property name="formStub" value="true">` with a256 `<bpmn:documentation>FORM STUB — fields: …</bpmn:documentation>`, and add the257 form (its id + fields, derived from the task's decision/data) to the follow-up258 spec. `lint` warns about any user task with no form and lists form-stubs under259 INFO. Don't leave a user task formless.260261- **Reuse the catalog's declarations and variable names.** Wire user-task262 assignment to catalog `roles` (candidate groups/users); business rule tasks to263 catalog `decisions` (`camunda:decisionRef`); and message/signal/error/escalation264 events to catalog declarations — creating the matching root `<bpmn:message>` /265 `<bpmn:signal>` / `<bpmn:error errorCode>` / `<bpmn:escalation>` from the catalog266 entry (these are declarations you write directly, not implementations to stub).267 Name process variables and every `camunda:inputOutput` parameter from the268 catalog's `variables` dictionary, not invented synonyms — consistent names are269 what make the steps actually wire together. A referenced message/error/role not270 in the catalog: add it and flag it. A missing **DMN decision** is stubbed like271 an activity (business rule task pointing at a proposed `decisionRef` + a spec272 entry).273274## The follow-up spec for new activities & forms275276For every stub activity **and every form-stub**, write (or append to) a spec — a277Markdown file beside the `.bpmn`, e.g. `<process>-activities-spec.md`. It is the278handoff for the separate implementation task. Use the template279`assets/activity-spec.template.md`. Per **activity** entry: name, the BPMN node280id/topic, business purpose, inputs (name / type / source variable), outputs281(name / type), error outcomes (BPMN error codes), and the suggested282implementation type (delegate vs external worker) consistent with the catalog.283Per **form** entry: the form id (the one you put in `camunda:formRef`), the user284task it belongs to, and its fields (key / label / type, required?). End with a285checklist. Keep it precise enough to build each activity/form from the spec alone286without re-reading the diagram.287288## Report back: the follow-up289290End every run with a short, skimmable follow-up — a handoff, not a wall of text.291Three sections; **omit any that are empty** (except Done); keep each bullet to one292line; the detail lives in the spec file, not here. Match the user's language.293294**Done** — what you assembled/changed and that it's verified: the result file +295`validate`/`lint` pass; which steps you **wired from the catalog**; what you added296(gateways, error handling, timers, forms); the conventions applied.297298**Needs you** — concrete asks, highest-impact first: stub activities to implement299and form-stubs to create (→ `<process>-activities-spec.md`); **data gaps** (a300variable a step needs that nothing upstream produces yet); conventions/assumptions301to confirm; deploy steps (e.g. deploy the `.form`/`.dmn` alongside the `.bpmn`). If302there's nothing, say "Deployable as is."303304**Watch out** — narrow spots / risks that could bite at runtime: runtime deps305(e.g. a Groovy script engine), interrupting vs non-interrupting choices,306async/transaction trade-offs, anything you assumed rather than knew. Omit if none.307308Aim for a handful of bullets total. If you stubbed things, the single most useful309line is the pointer to the spec.310311## Reference files — read the one you need, don't guess XML312313| When you're… | Read |314|---|---|315| Wiring existing activities and forms, representing stubs/form-stubs, the catalog format, or the spec | `references/wiring-and-stubs.md` |316| Writing the exact XML for service/user/rule tasks, listeners, I/O, async, events, call activity, process attrs | `references/camunda-extensions.md` |317| Diagnosing a deploy/runtime failure, or want the anti-pattern checklist + C7-vs-C8 cheat-sheet | `references/execution-checklist.md` |318| Form mechanics (formRef/.form, formKey, formData) or DMN decision tables | `references/forms-and-dmn.md` |319| Deploying (REST / Spring Boot / Modeler) or writing JUnit 5 + camunda-bpm-assert tests | `references/deployment-and-testing.md` |320321`assets/` has templates: an executable process skeleton (a wired activity + a322stub), a DMN table, a Camunda Form, the catalog format, the activities spec, and —323for the *separate* implementation task only — JavaDelegate / external-worker324skeletons.325326## Modeling for the engine, not just the picture327328- **Always** `isExecutable="true"` + `camunda:historyTimeToLive` (e.g. `P30D`) on329 a runnable process — since 7.20 the engine **rejects deployment** without TTL.330 DMN `<decision>`s need it too.331- **Expressions are JUEL** (`${...}`) in BPMN attributes; **FEEL only in DMN**.332- **Pair and guard gateways.** Give a diverging exclusive/inclusive gateway a333 `default` flow; never put conditions on a parallel gateway's outgoing flows.334- **Transaction boundaries** where failure must be isolated:335 `camunda:asyncBefore="true"` on external/long activities, loops, before waits;336 add `camunda:failedJobRetryTimeCycle`. Keep `camunda:exclusive="true"`.337- **Declare what you reference** (`message`/`signal`/`error` roots; errors need an338 `errorCode`). Correlation is by name, not id.339- **Camunda 7, never 8.** Use `camunda:` (URI `http://camunda.org/schema/1.0/bpmn`),340 never `zeebe:`, never `executionPlatform="Camunda Cloud"`. If the user actually341 wants Zeebe, say so and stop.342343## Layout: non-destructive by default344345`layout` preserves `camunda:` extensions on every path. It is also346**non-destructive**: if the file already has DI it **re-syncs** — keeps your347existing geometry, drops shapes for deleted elements, auto-places new ones, and348reroutes only stale edges (so hand-tuned diagrams survive an edit). Pass349**`--rebuild`** to discard all DI and regenerate from scratch (use after big350structural changes, or when a diagram is messy). It lays out single processes,351**collaborations** (stacked pools + message flows), **lanes** (swimlane bands),352and data objects / text annotations / associations; sub-processes are kept as353drill-down planes. Only **groups** are not auto-placed. If a request leans on354groups, place them in a modeler or say so.355356## Bundled tools357358| Command | Purpose |359|---|---|360| `summarize <file> [--json]` | Outline + per-node implementation, **stub markers**, async, forms, decisionRef, historyTTL |361| `layout <in> [out] [--rebuild]` | Safe layout: re-sync existing DI (or generate); always **preserves `camunda:` extensions** |362| `validate <file>` | Parse, flag missing shapes / overlaps / parse warnings (plane-aware) |363| `lint <file>` | Control-flow + structural + Camunda execution problems (ERROR/WARN/INFO); lists stubs |364| `diff <a> <b>` | Semantic + implementation diff (added/removed/renamed/retyped/rewired/impl-changed) |365| `find <file> <term>` | Find flow elements by name/type |366367All live in `scripts/camunda-tool.mjs` (engine in `scripts/lib.mjs`; tests:368`npm test`).