From cfd465b1023fb480d496f56857dbefec902c88c2 Mon Sep 17 00:00:00 2001 From: Henrique Costa Date: Sun, 26 Apr 2026 10:32:16 +0200 Subject: [PATCH 1/2] fix(writer): drop synthetic trailing newline from EndEvent ReturnValue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pristine Mx 9 EndEvents store the return expression in `ReturnValue` without a trailing line break. The mxcli writer was appending "\n" whenever `ReturnValue != ""`, which: 1. Diverged from Studio Pro's pristine on-disk shape, breaking byte-equality on roundtrip diffs. 2. Made Studio Pro reject some list-return EndEvents with CE0117 "Error in expression" — the Mx parser tolerated the trailing newline in scalar return expressions but not in list-return contexts. The append predates a clear understanding of which fields are pristine. Empirically the parser does not need the trailing newline either: mxcli's own MPR parser already accepted the value without it. Drop the conditional newline and pass `o.ReturnValue` through directly. Tests added in sdk/mpr/writer_microflow_version_test.go: - `TestSerializeEndEvent_EmptyReturnValueHasNoTrailingLineBreak` — pins the void-microflow case (`empty`). - `TestSerializeEndEvent_NonEmptyReturnValueHasNoSyntheticLineBreak` — pins the value-return case (`$Result`). Both go through `serializeMicroflowObject` and assert the on-disk ReturnValue matches the input exactly. --- sdk/mpr/writer_microflow.go | 11 +++----- sdk/mpr/writer_microflow_version_test.go | 32 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/sdk/mpr/writer_microflow.go b/sdk/mpr/writer_microflow.go index b9cdf507..c09120c1 100644 --- a/sdk/mpr/writer_microflow.go +++ b/sdk/mpr/writer_microflow.go @@ -466,13 +466,10 @@ func serializeMicroflowObject(obj microflows.MicroflowObject) bson.D { } case *microflows.EndEvent: - // Pristine EndEvents always carry `ReturnValue` (empty string for void - // microflows; expression + "\n" when a value is returned). Omitting it - // diverges from the pristine key set on Mx 9 roundtrips. - returnValue := "" - if o.ReturnValue != "" { - returnValue = o.ReturnValue + "\n" - } + // Pristine Mx 9 EndEvents carry `ReturnValue` but not a synthetic trailing + // line break. Adding one can make Studio Pro reject list-return EndEvents + // with CE0117 even though mxcli's parser accepts the expression. + returnValue := o.ReturnValue doc := bson.D{ {Key: "$ID", Value: idToBsonBinary(string(o.ID))}, {Key: "$Type", Value: "Microflows$EndEvent"}, diff --git a/sdk/mpr/writer_microflow_version_test.go b/sdk/mpr/writer_microflow_version_test.go index 783d2eac..fd3c298a 100644 --- a/sdk/mpr/writer_microflow_version_test.go +++ b/sdk/mpr/writer_microflow_version_test.go @@ -79,6 +79,38 @@ func TestSerializeSequenceFlow_Mx10_UsesModernShape(t *testing.T) { } } +func TestSerializeEndEvent_EmptyReturnValueHasNoTrailingLineBreak(t *testing.T) { + end := µflows.EndEvent{ + BaseMicroflowObject: microflows.BaseMicroflowObject{ + BaseElement: model.BaseElement{ID: "end-empty"}, + Position: model.Point{X: 10, Y: 20}, + Size: model.Size{Width: 20, Height: 20}, + }, + ReturnValue: "empty", + } + + doc := serializeMicroflowObject(end) + if got := bsonGetKey(doc, "ReturnValue"); got != "empty" { + t.Fatalf("ReturnValue = %q, want %q", got, "empty") + } +} + +func TestSerializeEndEvent_NonEmptyReturnValueHasNoSyntheticLineBreak(t *testing.T) { + end := µflows.EndEvent{ + BaseMicroflowObject: microflows.BaseMicroflowObject{ + BaseElement: model.BaseElement{ID: "end-result"}, + Position: model.Point{X: 10, Y: 20}, + Size: model.Size{Width: 20, Height: 20}, + }, + ReturnValue: "$Result", + } + + doc := serializeMicroflowObject(end) + if got := bsonGetKey(doc, "ReturnValue"); got != "$Result" { + t.Fatalf("ReturnValue = %q, want %q", got, "$Result") + } +} + func TestSerializeAnnotationFlow_VersionShapes(t *testing.T) { af := µflows.AnnotationFlow{ BaseElement: model.BaseElement{ID: "af-1"}, From d572e50954f0af5adfc1f0726b5464d6693a0ca0 Mon Sep 17 00:00:00 2001 From: Henrique Costa Date: Sun, 26 Apr 2026 23:58:42 +0200 Subject: [PATCH 2/2] test: add bug-test reproducer for EndEvent trailing-newline fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an MDL script under mdl-examples/bug-tests/ exercising both non-empty and empty EndEvent ReturnValue serialization. The describe → exec → describe fixpoint confirms no synthetic trailing newline drift in serialized BSON. Co-Authored-By: Claude Opus 4.7 --- .../316-endevent-no-trailing-newline.mdl | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 mdl-examples/bug-tests/316-endevent-no-trailing-newline.mdl diff --git a/mdl-examples/bug-tests/316-endevent-no-trailing-newline.mdl b/mdl-examples/bug-tests/316-endevent-no-trailing-newline.mdl new file mode 100644 index 00000000..4c5de79c --- /dev/null +++ b/mdl-examples/bug-tests/316-endevent-no-trailing-newline.mdl @@ -0,0 +1,44 @@ +-- ============================================================================ +-- Bug #316: Synthetic trailing newline appended to EndEvent ReturnValue +-- ============================================================================ +-- +-- Symptom (before fix): +-- The microflow writer appended `"\n"` to every non-empty `ReturnValue` +-- when serializing EndEvent objects. Pristine Studio Pro MPRs do not +-- store that trailing newline, so any roundtrip through mxcli left a +-- detectable BSON drift versus the original .mpr — visible in `mx check` +-- diffs and in `mxcli diff-local` output even when no semantic change +-- had been authored. +-- +-- After fix: +-- `writer_microflow.go` passes `ReturnValue` through directly. Empty +-- return values stay empty; non-empty ones are written without an +-- extra trailing newline. +-- +-- Usage: +-- mxcli exec mdl-examples/bug-tests/316-endevent-no-trailing-newline.mdl -p app.mpr +-- mxcli -p app.mpr -c "describe microflow BugTest316.MF_ReturnExpr" +-- The describe output must round-trip cleanly under describe → exec → describe +-- and (when applied to a pristine Studio Pro project) `mx check` must +-- not report any cosmetic diff in the EndEvent ReturnValue field. +-- ============================================================================ + +create module BugTest316; + +-- Microflow with a non-empty return value: the EndEvent's ReturnValue must +-- not pick up a synthetic trailing newline on serialization. +create microflow BugTest316.MF_ReturnExpr ( + $value: integer +) +returns boolean as $isPositive +begin + return $value > 0; +end; +/ + +-- Microflow with no return value (procedure): EndEvent ReturnValue stays empty. +create microflow BugTest316.MF_NoReturn () +begin + log info node 'BugTest316' 'side effect only'; +end; +/