Summary
ElementOptions has no documentation field, so the fluent builder cannot set
<bpmn:documentation> on any element — even though the rest of the library fully supports it:
bpmn-model.d.ts declares documentation?: string on flow elements, bpmn-parser.js reads it,
and bpmn-serializer.js serializes it as the element's first child.
The result is that any builder-authored process needing documentation must reach into the built
model and mutate it before export, i.e. drop out of the fluent API for a property the model already
has.
Where this bites, in order of severity:
- Ad-hoc sub-process tools (Camunda 8 agentic AI). A tool's
<bpmn:documentation> is the
tool description handed to the LLM — it is required content, not a nicety. Camunda's
compatibility checks warn Tool documentation is missing for every tool without it, so a
builder-authored agent is never clean without a post-build mutation pass.
- Start events. bpmnkit's own optimizer emits
pattern/start-no-documentation ("Start event
… has no documentation describing expected input variables"), advice the builder gives no way
to follow.
Verified against @bpmnkit/core@0.1.1.
Reproduction
import { Bpmn } from "@bpmnkit/core";
const defs = Bpmn.createProcess("P")
.startEvent("S")
.userTask("Task_Y", { name: "Y", documentation: "Some docs." }) // option silently ignored
.endEvent("E")
.build();
console.log(Bpmn.export(defs).includes("Some docs.")); // false ← option dropped
// The model supports it — only the builder entry point is missing:
defs.processes[0].flowElements.find((e) => e.id === "Task_Y").documentation = "Some docs.";
console.log(Bpmn.export(defs).includes("<bpmn:documentation>Some docs.</bpmn:documentation>"));
// → true
Note the option is silently ignored rather than rejected, which is its own small trap.
Current workaround
Post-build model mutation, which for nested elements means walking into container
flowElements:
const defs = builder.withAutoLayout().build();
const ahsp = defs.processes[0].flowElements.find((e) => e.id === "AIAgent_H");
for (const tool of ahsp.flowElements) {
tool.documentation = descriptions[tool.id]; // LLM-facing tool description
}
writeFileSync(out, Bpmn.export(defs));
Workable, but it splits one element's definition across the fluent chain and a later imperative
loop, keyed by id — exactly what the typed builder exists to avoid.
Proposed API
Add documentation?: string to ElementOptions, so it is available on every element the builder
can create (including inside SubProcessContentBuilder):
.userTask("Task_Y", { name: "Y", documentation: "Approve if the amount exceeds €500." })
.adHocSubProcess("AIAgent_H", (s) => {
s.serviceTask("Tool_ListUsers", {
name: "List users",
taskType: "io.camunda:http-json:1",
documentation: "Call this to retrieve all users. Returns id, name, email.",
});
}, { name: "Handle request" })
No serializer change needed — it already writes documentation as the first child.
Acceptance criteria
Summary
ElementOptionshas nodocumentationfield, so the fluent builder cannot set<bpmn:documentation>on any element — even though the rest of the library fully supports it:bpmn-model.d.tsdeclaresdocumentation?: stringon flow elements,bpmn-parser.jsreads it,and
bpmn-serializer.jsserializes it as the element's first child.The result is that any builder-authored process needing documentation must reach into the built
model and mutate it before export, i.e. drop out of the fluent API for a property the model already
has.
Where this bites, in order of severity:
<bpmn:documentation>is thetool description handed to the LLM — it is required content, not a nicety. Camunda's
compatibility checks warn
Tool documentation is missingfor every tool without it, so abuilder-authored agent is never clean without a post-build mutation pass.
pattern/start-no-documentation("Start event… has no documentation describing expected input variables"), advice the builder gives no way
to follow.
Verified against
@bpmnkit/core@0.1.1.Reproduction
Note the option is silently ignored rather than rejected, which is its own small trap.
Current workaround
Post-build model mutation, which for nested elements means walking into container
flowElements:Workable, but it splits one element's definition across the fluent chain and a later imperative
loop, keyed by id — exactly what the typed builder exists to avoid.
Proposed API
Add
documentation?: stringtoElementOptions, so it is available on every element the buildercan create (including inside
SubProcessContentBuilder):No serializer change needed — it already writes
documentationas the first child.Acceptance criteria
documentationonElementOptions, honoured by every builder method onProcessBuilder,SubProcessContentBuilderandBranchBuilder.<bpmn:extensionElements>.subProcess/adHocSubProcess), not only tasks.parse → exportunchanged.companion
compactify/expandissue. Both must hold for builder-set documentation tosurvive a later surgical edit.