Skip to content

fix(bpmn): keep a top-level call activity's fire-and-forget flag through the document PUT - #8082

Merged
sfmskywalker merged 3 commits into
mainfrom
claude/bpmn-8075-call-options
Sep 12, 2026
Merged

sfmskywalker merged 3 commits into
mainfrom
claude/bpmn-8075-call-options

Conversation

@sfmskywalker

Copy link
Copy Markdown
Member

Closes #8075. Part of #7909.

Problem

A top-level callActivity authored as fire-and-forget (vw:waitForCompletion="false") lost that flag on every PUT bpmn/definitions/{id}/document. In Bpmn.Interchange 0.2.0 the flag lives only on the reader's BpmnWorkBinding.CallProcess, and the BpmnDefinitions document cannot carry it. The PUT wrote top-level content without reusing stored bindings, so re-import bound a waiting call. Studio's Performed-by panel PUTs the whole document on any binding edit, so editing an unrelated task was enough to change runtime behaviour silently.

Fix: option 2, reuse the stored call options

This mirrors how #8077 reuses kept-subprocess bindings. ImportDocumentAsync reads and parses the stored BPMN source once (ReadStoredSource) and passes the result to both reuse helpers. For each top-level call activity in the posted document, it reuses the stored CallProcess binding, re-keyed to the posted element's bindingRef, only when:

  • the element id matches, and
  • calledElement is unchanged.

A changed calledElement, or a removed or retyped element, binds fresh with the default waiting behaviour, so nothing a client deliberately changed is brought back. CallProcess carries no other options besides CalledElement and WaitForCompletion, so nothing is dropped. A posted call activity without a bindingRef is skipped rather than refused, since the worst case is the pre-existing default rather than data loss. Without that skip the writer would throw on a null key.

There is no contract change: the GET shape, the ETag and the codes are the same, so Studio (elsa-workflows/elsa-studio#1031, #1033) is unaffected.

Tests

  • The D4 round-trip gains top-level-call-activity.bpmn: GET, PUT unchanged, export, and the flag is still false.
  • Editing another task's binding keeps the flag.
  • A changed calledElement binds fresh.
  • A call activity without a bindingRef is skipped.
  • The no-op PUT ETag-stability theory includes the new fixture, and the ETag stays stable.
  • Mutation-tested: disabling the reuse, the calledElement check, or the null-bindingRef guard each turns its test red.
  • Local runs:
    • Elsa.Bpmn.UnitTests: 30/30
    • Elsa.Bpmn.Interchange.UnitTests: 14/14
    • Elsa.Bpmn.IntegrationTests: 59/59
    • Elsa.Bpmn.Interchange.IntegrationTests: 108/108

The wiki documents the guarantee next to the "Nested scopes" section.

🤖 Generated with Claude Code

@greptile-apps

greptile-apps Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

Safe to merge.

Summary

  • This change preserves a top-level BPMN call activity’s stored fire-and-forget option when an unrelated document edit is saved. It reads the stored BPMN source once, reuses call options only when the call activity’s element ID and target remain unchanged, adds focused round-trip and endpoint coverage, and documents the behavior.

Reviews (4) · Last reviewed commit: "refactor(bpmn): filter call activities w..."

@sfmskywalker
sfmskywalker force-pushed the claude/bpmn-8075-call-options branch from c2f523a to fefe720 Compare September 12, 2026 18:08
sfmskywalker and others added 3 commits September 12, 2026 20:27
…ugh the document PUT

A call activity's call options (vw:waitForCompletion) live only on its CallProcess
work binding, which the bpmnDefinitions document cannot carry, so the document PUT
rebuilt every call activity fresh and silently turned a fire-and-forget call into a
waiting one. Reuse the stored options for a top-level call activity whose element id
and calledElement are unchanged, mirroring how a kept subprocess already carries its
own bindings across; a changed calledElement still binds fresh rather than inheriting
options authored for a different process.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ImportDocumentAsync's two carry-across helpers each independently
looked up SourceXmlCustomPropertyKey and parsed it, so every document
PUT read and parsed the stored BPMN source twice. Read and parse it
once in ImportDocumentAsync and pass the result to both helpers.

Also extends the no-op PUT ETag-stability theory to the
top-level-call-activity.bpmn fixture, which was not previously covered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sfmskywalker
sfmskywalker force-pushed the claude/bpmn-8075-call-options branch from 5bd44be to 16f4115 Compare September 12, 2026 18:29
@sfmskywalker
sfmskywalker merged commit 9cce8d9 into main Sep 12, 2026
14 checks passed
@sfmskywalker
sfmskywalker deleted the claude/bpmn-8075-call-options branch September 12, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BPMN W33: the document PUT drops a top-level call activity's fire-and-forget flag

1 participant