Sync ako/mxcli: workflow authoring — end workflow, signature checks, rewrite guards - #1099
Merged
Merged
Conversation
A workflow user task hands work to two documents, and `check --references` resolved both by name only. A task page typed to the context entity, one with no parameters, or a targeting microflow missing the System.Workflow parameter passed check, was written by exec, and failed only in the native build. A team building workflows from MDL listed both among the reasons "a green CLI check is not evidence for workflows". Every rule is a row of an mxbuild measurement on 11.13.0, one user task per shape, verdict = the literal `mx check` line: task page, no parameters CE7410 task page, no WorkflowUserTask parameter CE7412 (multi-user too) task page, WorkflowUserTask + an extra parameter 0 errors targeting (System.Workflow, Ctx) / (Ctx, Workflow) 0 errors targeting (Workflow) / () / (Workflow, Ctx, String) CE6677 targeting (Workflow, generalization of Ctx) 0 errors targeting (Workflow, specialization of Ctx) CE6677 groups targeting same rule Three rows are clean shapes a reading of the error text would refuse — extra page parameters, reversed order, a generalization — and exec refuses on a check error, so a wrong rule would block a valid workflow rather than warn. An inheritance chain that leaves what can be resolved is "not proven", which is not a refusal. Covers CREATE WORKFLOW (nested and multi-user tasks) and ALTER WORKFLOW (inserted tasks, SET ACTIVITY PAGE / TARGETING MICROFLOW, with the context entity read off the stored workflow). Pages, flows and generalizations are resolved from the script first — they are ordinarily one script with the workflow — then the project; scriptContext now records page parameter entities and entity generalizations beside flowParams. Gated on Mendix 11, where it is measured. check --references reports before anything is written. exec calls the same function at the workflow statement, so the workflow is never written, though earlier statements in the script already are. Controls, one stub each: the page check, the targeting check, following the generalization, treating an unresolved chain as a mismatch, and the version gate — each fails exactly the cases it should. End to end on an 11.13 app: check --references reports exactly the six measured-bad shapes and none of the five clean ones, and the positive fixture execs and builds at 0 new errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ures Neither signature was anywhere in `mxcli syntax workflow`, so an author had nothing to copy and found both rules by failing a build. The user-task topic now states the page rule (a System.WorkflowUserTask parameter; others may sit alongside it) and the targeting topic the microflow rule (exactly System.Workflow plus the context entity or a generalization, either order), with the users / groups forms and a runnable example of each. Adds a `workflow.multi-user-task` topic. The grammar has accepted `multi user task` for a long time, and a team shipped one fanning out to eight assessors, but with no topic it read as unsupported. It says plainly that MDL has no clause for the completion settings. The skill and quick reference carry the same rules, including that `check --references` reports them before anything is written while exec refuses only the workflow statement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every exec / check --references error that named the workflow being created ended with "hint: M.W is defined later in this script — move its create statement before this one". annotateForwardRef draws its candidates from scriptContext.allNames, which includes workflows, and excludes the failing statement's own name and anything already created through scriptContext.has, which answered for every kind a script can create except workflows. Two lists over the same kinds, with nothing keeping them in step. It surfaced through the task page / targeting signature errors, which name their workflow on every refusal, but any workflow reference error had it. Test first: the own-name and already-created cases failed with the reported hint while the genuine forward reference was annotated as before; all three pass with the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MDL could not end a workflow from a user-task outcome, a decision branch or a
boundary-event path, and `describe workflow` dropped every End it met, so a
Studio Pro workflow whose Reject outcome ends the process described as an empty
outcome that falls through into the main flow. A team building native
workflows from MDL named it the one structural gap left after four projects.
The proposal measures Mendix's placement rules before designing syntax — a
throwaway spike placed End activities through the existing (never wired)
builder on mxbuild 11.13.0, both engines — and settles `end workflow [comment
'<caption>'];` in any `{ }` block, with illegal placements refused by check
rules rather than the grammar. It records the discussion behind the spelling:
`end workflow` is a microflow `return`'s counterpart, but `return;` inside a
block reads as "rejoin the main flow", which is the fault being fixed; and
`comment` writes a caption, a keyword issue left to a separate change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`end workflow [comment '<caption>'];` is legal in any `{ }` block of a workflow
and ends the whole workflow — the counterpart of a microflow's `return`, which
in a workflow body is refused with a pointer to it (MDL-WF11) and refused by
exec rather than dropped, since a dropped `return` falls through.
The first workflow grammar dropped the statement for colliding with the
closing END WORKFLOW. The collision only exists at the top level, the one
place Mendix refuses an explicit End, so the top-level body is its own rule
(workflowMainBody) and brace bodies are walked in child order.
Placement rules, each an mxbuild 11.13.0 measurement with the real syntax,
all checked without a project:
under a parallel split or a non-interrupting boundary, any depth CE1844 MDL-WF08
anything after it in its block CE6671 MDL-WF09
every path of an activity ends (End or jump, also via a nested
decision) with more after it or as the main flow's last activity,
and a main flow ending in a direct jump CE6689 MDL-WF10
Clean: the last statement of an outcome, decision branch, call-microflow
outcome or interrupting boundary path, at any depth. ALTER inserts get the
same rules, plus the stored workflow's ancestry for their target.
Also fixed on the way, each caught by a measurement:
- describe skipped every End as implicit; only the main flow's is. A nested
End now describes as `end workflow`, and describe -> exec -> describe is
stable.
- create or modify refuses a rewrite that deletes stored nested Ends it does
not restate (guard-don't-drop), so an older describe output cannot drop them.
- a nested End named "End" collided with the main flow's (CE0495); the
implicit Start/End names are reserved.
- an End's name was its caption, and a caption with a space built CE7247; the
name is now a fixed base name.
- two fixtures that claimed to be valid workflows ended their main flow in
`jump to` (CE6679 + CE6689, measured); their jumps now sit in outcomes.
Verified: tests written first and failing; nine controls, one stub each,
fail exactly their cases; the positive fixture execs and builds at 0 new
errors, describe round-trips identically, and a rewrite without its Ends is
refused with nothing written; doctype mx check on both engines.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…geting microflows CI's integration job failed four workflow round-trip tests on this PR: exec now checks a user task's page and targeting microflow signatures, and the shared fixtures had never been valid. createTaskPages made task pages with no parameters (CE7410 on mxbuild 11.13.0), and the Comprehensive test's targeting microflows took no parameters and returned a String (CE6677). The round-trip tests only compare describe output, so nothing had built them. The pages now take $WorkflowUserTask: System.WorkflowUserTask, and the targeting microflows take System.Workflow plus the workflow's context entity and return the candidate users. The unit suite could not have caught this: the rule is gated on the project version, and its mock projects have none. Verified locally on both engines with a real 11.13 project (-v shows all four running, not skipped); restoring a parameterless page reproduces CI's exact CE7410 refusal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…gured Measured on a Studio Pro reference workflow (ako/TestApp, Mendix 11.14.0, 0 errors under mxbuild): ALTER WORKFLOW ... REPLACE ACTIVITY with an identical user task reset the task's on-created microflow (MicroflowBasedEvent -> NoEvent) while exec reported "Altered workflow". SET ACTIVITY, which edits the stored document in place, kept it — the control that pins the loss on the rebuild. Every rebuild writes a default for what MDL has no spelling for: OnCreatedEvent = NoEvent, OnWorkflowEvent = an empty list, CompletionCriteria = Consensus on the first outcome; describe prints an AI agent task only as a comment. None of it was guarded, so `create or modify` would lose it the same way. create or modify now refuses a workflow holding any of these, listing every reason at once — the rule event sub-processes already had — and REPLACE ACTIVITY refuses an activity holding one (found in the raw unit by name, unique per CE0495). Consensus falling back to the first outcome is allowed: it is what the rebuild writes and what the reference stores. Also counts an event sub-process once: the substring match counted its start activity too, and the reference's single sub-process was reported as two. Tests written first and failing; controls for the REPLACE refusal and the Consensus allowance each fail their cases. On the reference: check --references and exec refuse the REPLACE with the unit byte-identical, SET ACTIVITY still works, and create or modify is refused with the full list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nd let them re-parse
Two defects behind one round trip.
The default engine read a wait for notification's boundary events as absent:
workflowActivityFromGen had no typed case for it, so it fell to the untyped
path, which reads only name and caption. Describe dropped every timer and its
handler flow while the legacy engine described them. Gen has had
WaitForNotificationActivity with BoundaryEventsItems() for some time; the
fallback's comment ("no genWf struct") had gone stale.
And describe output with two boundary events did not parse on either engine:
describe writes `boundary event …` before each event, as the syntax topic
documents, but the grammar took the keyword once and then all clauses
(`mismatched input 'boundary' expecting ';'`). The grammar now accepts the
keyword per clause and still accepts the shared form.
The second was found because the first round trip was vacuous: it diffed two
describes around an exec that had died on the parse error, and the second
describe read the unchanged document. The integration round trips have the
same blind spot — they never re-parse describe output — so a test that does
(TestWorkflowDescribe_TwoBoundaryEventsReparse) is added.
Verified: tests written first and failing; on an 11.13 app the fixture execs
and builds, both engines describe both boundary clauses, and the describe
output re-executes with 0 parse errors and describes identically afterwards.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ixes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(check): check a user task's page and targeting microflow signatures
…ivity-proposal # Conflicts: # CHANGELOG.md
feat(workflow): end a workflow from inside a branch with `end workflow`
…keeps-studio-pro-state # Conflicts: # .claude/skills/mendix/write-workflows/SKILL.md # CHANGELOG.md # mdl/executor/cmd_workflows_describe_test.go # mdl/executor/validate_workflow_refs.go # mdl/executor/validate_workflow_rewrite.go # mdl/executor/validate_workflow_rewrite_test.go # mdl/visitor/visitor_workflow_test.go
…o-state fix(workflow): refuse rewrites that reset Studio Pro state, and round-trip boundary events
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Nine commits from
ako/mxcli:main, all workflow, in three groups. Follows #1095 and the Mode 2 sync; upstream is at v0.22.0.The theme across all three: a team building workflows from MDL listed several reasons "a green CLI check is not evidence for workflows". Each group closes one of them, and every rule below is a row of an mxbuild measurement rather than a reading of a CE code.
checkwas resolving workflow references by name onlyfix(check)— a user task's page and targeting microflow signatures. A task page typed to the context entity, one with no parameters, or a targeting microflow missing theSystem.Workflowparameter passedcheck --references, was written by exec, and failed only in the native build. Every rule is one user task per shape on 11.13.0, verdict = the literalmx checkline:WorkflowUserTaskparameterWorkflowUserTask+ an extra parameter(System.Workflow, Ctx)/(Ctx, Workflow)(Workflow)/()/(Workflow, Ctx, String)(Workflow, generalization of Ctx)(Workflow, specialization of Ctx)fix(check)— stop telling a workflow statement to move before itself. Every error naming the workflow being created ended with "hint: M.W is defined later in this script — move its create statement before this one".annotateForwardRefdraws candidates fromscriptContext.allNames, which includes workflows, and excludes anything already created throughscriptContext.has, which answered for every kind a script can create except workflows. Two lists over the same kinds with nothing keeping them in step. It surfaced through the signature errors above, but any workflow reference error had it.test(workflow)— the round-trip fixtures had never been valid task pages or targeting microflows; they only compared describe output, so nothing had built them. The unit suite could not have caught it either: the rule is gated on the project version and the mock projects have none.docs(workflow)— the two signatures written down.end workflow— ending a workflow from inside a branchfeat(workflow)—end workflow [comment '<caption>'];is legal in any{ }block and ends the whole workflow: the counterpart of a microflow'sreturn, which in a workflow body is now refused with a pointer to it (MDL-WF11) and refused by exec rather than dropped, since a droppedreturnfalls through.The first workflow grammar dropped the statement for colliding with the closing
END WORKFLOW. That collision exists only at the top level — the one place Mendix refuses an explicit End — so the top-level body is its own rule and brace bodies are walked in child order.Placement rules, each an 11.13.0 measurement with the real syntax, all checked without a project:
docs(proposal)— the design behind it.Rewrites that silently reset what Studio Pro configured
fix(workflow)— refuse them. Measured on a Studio Pro reference workflow (11.14.0, 0 errors under mxbuild):ALTER WORKFLOW … REPLACE ACTIVITYwith an identical user task reset the task's on-created microflow (MicroflowBasedEvent→NoEvent) while exec reported "Altered workflow".SET ACTIVITY, which edits the stored document in place, kept it — the control that pins the loss on the rebuild. Every rebuild writes a default for what MDL has no spelling for (OnCreatedEvent,OnWorkflowEvent,CompletionCriteria, and an AI agent task describe only prints as a comment), none of it guarded, socreate or modifylost it the same way. Both now refuse, listing every reason at once.fix(workflow)— boundary events on a wait for notification. Two defects behind one round trip: the default engine had no typed case for it, so it fell to the untyped path (name and caption only) and describe dropped every timer and its handler flow, while the legacy engine described them — gen has had the typed struct for some time and the fallback's "no genWf struct" comment had gone stale. And describe output with two boundary events did not parse on either engine: describe writes the keyword before each event, as the syntax topic documents, while the grammar took it once. The grammar now accepts both spellings.docs(changelog)— the two above.