Skip to content

feat(microflow): @position on a flow parameter, and stop moving hand-placed ones - #370

Merged
ako merged 2 commits into
mainfrom
claude/mxcli-findings-nnl181
Sep 1, 2026
Merged

feat(microflow): @position on a flow parameter, and stop moving hand-placed ones#370
ako merged 2 commits into
mainfrom
claude/mxcli-findings-nnl181

Conversation

@ako

@ako ako commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Closes the gap reported upstream as mendixlabs/mxcli#993: a parameter is a stored node with real geometry that Studio Pro lets you drag, and no MDL annotation reached it.

The report was half of the story

Filed as a feature request — a generated flow's parameter block lands wherever the writer puts it. Measuring it turned up the more serious half: an existing hand-aligned flow was moved, by a describe → exec of mxcli's own output.

before   Feedback@-77;0     (placed directly above its start event)
after    Feedback@200;53

200+idx*100;53 was mxcli's derived grid, so anything off it was placed by a person: 20 of 28 parameters, in 20 of 27 flows of a real 1971-unit project. Every one was moved by any rewrite.

Cause was a four-point chain — the semantic type had no position field, so neither reader carried one, so both writers could only recompute the index formula inline, and the grammar had no slot to state one.

The placement rule is @start's, unchanged

Not a new policy: authoredStartPosition (mendixlabs#884, mendixlabs#951) already settled this for the StartEvent, and the reasoning transfers with the node family — including the trap. Carrying stored coordinates over unconditionally pins the node, so inserting a parameter would strand the existing ones on the old grid while the new one landed on top of them.

So: a parameter sitting exactly where the layout would have put it is mxcli's own arithmetic handed back, carries no intent, and is re-derived. One anywhere else was placed by a person, survives a rewrite, and is emitted by DESCRIBE. That is why an ordinary description does not grow a line per parameter.

Two decisions worth a reviewer's eye:

  • The arbitration lives in the readers, so a non-nil Position means intent everywhere downstream and no consumer has to re-derive.
  • Position is a pointer, because 0;0 is a coordinate a person can choose — two flows in the reference project use it. A zero-value check would have been a real bug, and TestAuthoredParameterPositionKeepsOrigin pins it.

Syntax

One grammar line (annotation* on microflowParameter), no ANTLR ambiguity, so the block-level @parameters(...) alternative the reporter offered as a fallback is unnecessary.

create or replace nanoflow MyFirstModule.NF_ParamPos (
  @position(300, 100)
  $A: Integer,
  @position(200, 100)
  $B: Integer
)
returns Integer as $R
begin
  @position(300, 200)
  declare $R Integer = $A + $B;
  @position(500, 200)
  return $R;
end;

@position is the only annotation a parameter takes. Anything else — a typo of it above all, which would otherwise parse and discard exactly the placement being asked for — is refused as MDL059 in check, exec and the LSP.

Covers microflows, nanoflows and rules (shared parameter grammar) on both engines. The four near-duplicate describers now share one helper, so the annotation cannot appear from one command and not another.

Verification

On mxbuild 11.13, against a real project:

  • authored positions stored verbatim (300;100, 200;100, 140;-60, 60;-40);
  • control — the unannotated flow still derives to 200;53 / 300;53, and DESCRIBE emits no line for it;
  • describe → exec reports Unchanged for all four flows: an exact fixed point;
  • mx check: 0 errors;
  • fix reverted (read-side carry stubbed, as the pre-fix code was): -77;0 → 200;53 returns and DESCRIBE emits nothing. Unit tests fail with the reported symptom.

Full suite green, gofmt clean, make check-findings passes.

Two limits, stated

  • Model storage and round-trip behaviour are verified; Studio Pro's rendering of a parameter at an arbitrary point is not. A real project keeping -2563;35 across saves is good evidence it honours them.
  • While measuring, the same nanoflow round trip was found to drop ExportLevel and reset a sequence flow's DestinationControlVector. Unrelated, pre-existing, not fixed here — worth its own issue. Noting it because an earlier read of this round trip treated Unchanged on a second exec as proof the first write changed only the position; it proves the round trip reaches a fixed point, not that.

Generated by Claude Code

claude and others added 2 commits September 1, 2026 16:32
…placed ones

A Microflows$MicroflowParameter is a stored node with real geometry
(RelativeMiddlePoint + Size 30;30) that Studio Pro lets you drag, but no
MDL annotation reached it. Two consequences, the second the more serious:
a generated flow's parameter block landed wherever the writer put it, and
an existing hand-aligned one was MOVED by any rewrite — including a
describe -> exec of mxcli's own output. Measured on a real nanoflow, a
parameter at -77;0 came back at 200;53; on a 1971-unit project, 20 of 28
parameters sat off the derived grid and so were moved.

The cause was a four-point chain: the semantic type had no position
field, so neither reader carried one, so both writers could only
recompute 200+idx*100;53 inline, and the grammar had no slot to state one.

The placement rule is @start's, unchanged (mendixlabs#884, mendixlabs#951): a parameter
sitting exactly where the layout would have put it is mxcli's own
arithmetic handed back, carries no intent, and is re-derived; one
anywhere else was placed by a person, survives a rewrite, and is emitted
by DESCRIBE. Carrying stored coordinates over unconditionally is the trap
that rule exists to avoid — inserting a parameter would strand the
existing ones on the old grid while the new one landed on top. The
arbitration lives in the readers, so a non-nil Position means intent
everywhere downstream; it is a pointer because 0;0 is a coordinate a
person can choose.

Syntax needed one grammar line (annotation* on microflowParameter), with
no ANTLR ambiguity, so the block-level @parameters(...) alternative is
unnecessary. @position is the only annotation a parameter takes; anything
else — a typo of it above all — is refused as MDL059 rather than parsing
and doing nothing, in check, exec and the LSP.

Covers microflows, nanoflows and rules (shared parameter grammar) on both
engines. The four near-duplicate describers now share one helper, so the
annotation cannot appear from one command and not another.

Verified on mxbuild 11.13: authored positions stored verbatim, the
unannotated control still deriving to 200;53/300;53, describe -> exec
reporting Unchanged, 0 errors. Control: with the read-side carry stubbed,
-77;0 -> 200;53 returns and DESCRIBE emits nothing.

Refs: ako/mxcli#993

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017s476QkXr9CFMvKspVzcvu
@ako
ako merged commit cb6ecba into main Sep 1, 2026
13 of 14 checks passed
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.

2 participants