Summary
The operations API round-trip silently discards every <bpmn:documentation> element.
compactify() does not carry documentation into the compact model, so expand() cannot restore
it — the text is gone from the exported XML with no error and no warning.
This is data loss on the API whose documented purpose is surgical edits that preserve everything
else ("parse → compactify → applyOperations → expand → export, rather than rebuilding"). A caller
who applies a single rename op to one element loses the documentation of all elements in the
file.
bpmn:documentation is not incidental content in Camunda 8:
- On an ad-hoc sub-process tool it is the tool description handed to the LLM — dropping it
changes runtime agent behaviour.
- On a start event it carries the process input contract (bpmnkit's own
pattern/start-no-documentation optimizer rule asks callers to add it).
- Toolchains layer review/uncertainty metadata into it.
The rest of the pipeline already handles it correctly: bpmn-parser.js reads documentation,
bpmn-model.d.ts declares documentation?: string, and bpmn-serializer.js writes it as the
first child. Only compact.js drops it.
Verified against @bpmnkit/core@0.1.1.
Reproduction
import { Bpmn, compactify, expand, applyOperations } from "@bpmnkit/core";
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" id="d" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="P">
<bpmn:startEvent id="S"><bpmn:outgoing>F1</bpmn:outgoing></bpmn:startEvent>
<bpmn:userTask id="Task_X" name="X">
<bpmn:documentation>Call this to look up a user by ID.</bpmn:documentation>
<bpmn:incoming>F1</bpmn:incoming>
</bpmn:userTask>
<bpmn:sequenceFlow id="F1" sourceRef="S" targetRef="Task_X"/>
</bpmn:process>
</bpmn:definitions>`;
// No-op operation list — nothing should change.
const out = Bpmn.export(expand(applyOperations(compactify(Bpmn.parse(xml)), [])));
console.log(xml.includes("Call this to look up a user by ID.")); // true
console.log(out.includes("Call this to look up a user by ID.")); // false ← lost
Parse alone is fine, which localises the bug to the compact model:
const parsed = Bpmn.parse(xml);
console.log(parsed.processes[0].flowElements.find((e) => e.id === "Task_X").documentation);
// → 'Call this to look up a user by ID.' ← survives parse
console.log(Bpmn.export(parsed).includes("Call this to look up"));
// → true ← survives parse → export
grep -r documentation dist/bpmn/compact.js returns nothing, while bpmn-parser.js,
bpmn-model.d.ts and bpmn-serializer.js all reference it.
Current workaround
None that keeps the operations API. Callers must avoid compactify/expand entirely on any file
carrying documentation and fall back to rebuilding from the source builder script, or to text
surgery on the exported XML.
Proposed fix
Carry documentation through the compact model like any other scalar element property:
compact.js — copy documentation onto the compact element when compactifying, and back onto
the expanded flow element in expand().
- Optionally accept
documentation in the update op's patch (it already works by accident via
Object.assign, once the round-trip stops dropping it).
Acceptance criteria
Summary
The operations API round-trip silently discards every
<bpmn:documentation>element.compactify()does not carrydocumentationinto the compact model, soexpand()cannot restoreit — the text is gone from the exported XML with no error and no warning.
This is data loss on the API whose documented purpose is surgical edits that preserve everything
else ("parse → compactify → applyOperations → expand → export, rather than rebuilding"). A caller
who applies a single
renameop to one element loses the documentation of all elements in thefile.
bpmn:documentationis not incidental content in Camunda 8:changes runtime agent behaviour.
pattern/start-no-documentationoptimizer rule asks callers to add it).The rest of the pipeline already handles it correctly:
bpmn-parser.jsreadsdocumentation,bpmn-model.d.tsdeclaresdocumentation?: string, andbpmn-serializer.jswrites it as thefirst child. Only
compact.jsdrops it.Verified against
@bpmnkit/core@0.1.1.Reproduction
Parse alone is fine, which localises the bug to the compact model:
grep -r documentation dist/bpmn/compact.jsreturns nothing, whilebpmn-parser.js,bpmn-model.d.tsandbpmn-serializer.jsall reference it.Current workaround
None that keeps the operations API. Callers must avoid
compactify/expandentirely on any filecarrying documentation and fall back to rebuilding from the source builder script, or to text
surgery on the exported XML.
Proposed fix
Carry
documentationthrough the compact model like any other scalar element property:compact.js— copydocumentationonto the compact element when compactifying, and back ontothe expanded flow element in
expand().documentationin theupdateop'spatch(it already works by accident viaObject.assign, once the round-trip stops dropping it).Acceptance criteria
expand(compactify(parse(xml)))→exportpreserves every<bpmn:documentation>, on allelement types, including elements nested inside sub-processes and ad-hoc sub-processes.
{ op: "update", id, patch: { documentation: "…" } }sets it.rename/redirect_flowop.