Skip to content

create or modify carries workflow, OData client and external entity content; describe switches the verb (#743, #764) - #782

Merged
ako merged 41 commits into
mainfrom
feature/743-workflow-odata-client-carry
Sep 28, 2026
Merged

ako merged 41 commits into
mainfrom
feature/743-workflow-odata-client-carry

Conversation

@ako

@ako ako commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Closes #743 (the remaining part; TestApp in the harness and the external entity / OData service carries landed in #759). Closes #764.

What changes

Running a Studio Pro document's own describe output now leaves ako/TestApp unchanged for the three kinds that still printed a plain create. describe now emits create or modify for all three.

Workflow (workflow.Workflow1, Workflow1_2)

  • Every outcome now stores a Flow. It is empty when the outcome leads nowhere; Workflows$Outcome.flow is Required. Condition outcomes now store a PersistentId, as every other outcome does.
  • Activities store RelativeMiddlePoint/Size as "0;0", which is what Studio Pro stores (14 of 14 in TestApp). The writer used to store "".
  • An empty EventSubProcesses [2] is written on 11.8+ only (encodeWorkflow). For an older project, Encoder.OmitKeys suppresses only the empty default. Declared sub-processes are never suppressed. MandatoryListMarkers now honours OmitKeys, as MandatoryLists already did.
  • The executor carries the stored names of the activities describe cannot name: start1, end1, the ends of branches and sub-processes, jumps and end-of-path markers (workflow_carry_names.go). The pairing is structural. It runs before the CE0495 deduplication, so the carried names take part in it.

Consumed OData service (Clients.OrderODataClient, Odata.Bug1073)

  • UpdateConsumedODataService lays the encoded document over the stored one (carryStoredConsumedODataService). This keeps UseQuerySegment, Icon, ExportLevel, CustomLocationTemplate and the list markers. The catalog, proxy and microflow keys that Studio Pro stores as "" stay "".

External entity (Clients.Orders, Odata.Devices)

Refusal messages. A plain create of an existing workflow or OData client recommends create or modify again, and still names alter for a one-part change.

Design choices the ADRs did not settle

Test plan

  • Failing tests written first:
    • TestWorkflowToGen_EveryOutcomeStoresAFlow, ..._ActivityLayoutIsTheStudioProDefault, TestWorkflowEncoder_EmptyEventSubProcessesFromMendix118
    • TestCreateOrModifyWorkflow_CarriesNamesDescribeCannotPrint, with the control ..._ImplicitNamesWithoutStoredNames
    • TestConsumedODataServiceRewrite_CarriesWhatTheModelDoesNotHold / _ModelValuesWin
    • TestEntityToGen_WritesEveryGeneralizationFlag
    • TestCreateOrModifyExternalEntity_RemoteTypeMismatch{RefusedUnderMdl1,WarnsUnderMdl0}, with the control _MatchingTypeCarriesRemoteType
  • Revert checks. Reverting each of these made its test fail:
    • the outcome Flow, layout and ESP changes;
    • the name-carry call;
    • the overlay (replaced with the fresh document);
    • the checkRemoteTypes call;
    • the flags change (the test failed before implementation).
  • Before switching the verbs, TestTestAppCreateOrModifyProbe was run with its allowlist emptied. Workflows and clients passed after their fixes, and external entities passed after the flags fix.
  • go test ./mdl/backend/modelsdk/ ./mdl/executor/ ./modelsdk/codec/ ./mdl/roundtrip/ passes.
  • go test -tags integration ./mdl/roundtrip/ passes, run suite by suite with -p 1 because the shared machine OOM-killed the full package: TestApp round trip, PedApp round trip, probe/describe/terminator/flow-modify/misc, and TestUpgrade. 6 #743 entries and 27 #721 entries were struck from the TestApp allowlist. No entries were added.
  • mx check (11.14.0) on a TestApp copy passed with 0 errors:
    • after running all six documents' describe output as create or modify (4 were reported unchanged);
    • after creating new workflows with a decision, a parallel split and empty outcomes;
    • the CE6616 measurement above.
  • make build, make lint, make check-findings all pass.
  • Skills updated: write-workflows, odata-data-sharing. Findings appended.

🤖 Generated with Claude Code

ako and others added 30 commits September 28, 2026 05:08
…en in { })

REST client operations and agent attachments take their properties in ( );
image collections and message definition collections put their children in
{ }. The old spellings keep parsing as registered deprecated aliases
MDL-DEPR070..073, recorded once per statement, with fmt --upgrade rewrites.
describe emits the canonical forms.

Part of #754.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fragment is imperative flow, written as the body of create microflow is,
so insert after/before and replace … with take begin … end. The brace
fragment keeps parsing as MDL-DEPR074 with an fmt --upgrade rewrite. BEGIN
now ends an alter target, as { does; describe's handles never contain it.

Part of #754.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Converted with the visitor's own MDL-DEPR070..074 rewrites (no other
spelling touched), plus the syntax-pattern blocks by hand. Records the
alter-fragment decision in the beta syntax proposal.

Part of #754.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ere R3 puts them (#751)

R3 of the beta syntax freeze: `:` sets a model property, so every `alter`
takes the same `( Key: value, … )` list its `create` does, and a colon is
written in a property list or an attribute definition and nowhere else.

Canonical forms, parsed under mdl 0 and mdl 1; the old spellings keep
parsing as registered aliases that warn and that `fmt --upgrade` rewrites:

- MDL-DEPR060 alter settings <section> / configuration 'X' and
  create configuration: `Key = v, …` -> `( Key: v, … )`
- MDL-DEPR061 alter consumed|published odata service X set Key = v
  -> set ( Key: v, … ), exactly create's property list
- MDL-DEPR062 alter styling … set Class = 'x', 'P' = on -> set ( … )
- MDL-DEPR063 alter entity … set allow_create_change_locally = v
  -> set ( AllowCreateChangeLocally: v )
- MDL-DEPR064 association `type: Reference` / owner: / storage: -> no colon
- MDL-DEPR065 modify attribute A T -> modify attribute A: T
- MDL-DEPR101..103, the generic alter page/snippet/layout aliases, move
  from the executor's provisional table into the registry, with rewrites.

describe settings now writes the canonical lists, and the MDL mxcli
generates (testrunner, new-project hints) uses them. Docs, skills,
`mxcli syntax` and the quick reference follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oke mirrors grant (#753)

The rest of R5 after #775, each old spelling a registered deprecated alias
with an fmt --upgrade rewrite (codes 080-085):

- workflow decision / wait for timer / timer boundary event / timer event
  sub-process / due date take a bare expression; the string form keeps its
  meaning (MDL-DEPR080). describe writes the bare form, forcing the name of a
  decision or wait for timer when the expression would read as the name.
- page Visible: / Editable: take a bare client expression, stored as written,
  in create page and alter page set; [expr] is MDL-DEPR081 and its rewrite is
  the expression it stored. A constant condition ([true]/[false]) has no bare
  spelling and is not reported.
- revoke rights|all on entity M.E from M.Role; the reversed form is
  MDL-DEPR082.
- @Module.Const is the one constant reference: REST $Const (083), agent
  Key: M.C (084), alter settings constant 'M.C' (085).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…753)

Skills, docs-site, mxcli syntax, MDL_QUICK_REFERENCE, the spec docs, the
CHANGELOG and the mdl-examples doctype tests in the canonical forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fmt --upgrade rewrote `insert after $X{ … }drop …` to `$Xbegin … enddrop`,
which lexes as a variable and an identifier; the upgrade's re-parse refused it.
The fragment's words now get a space where the source had none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the"

The warning prints "Rewrite the <Structural>"; MDL-DEPR031 and 070-074 began
with "the" (or "each"), so it read "Rewrite the the fragment's …". A
registry test now refuses a leading "the".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…boundary event (#753)

With the delay optional and the next boundary event allowed without a
repeated 'boundary event', 'interrupting timer non interrupting timer x'
read 'non' as the first timer's bare delay and made the second event
interrupting. A predicate keeps a bare delay from starting with NON, so
the script builds what it built when only a string could be the delay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion (#753)

describe writes a decision's condition bare since R5; the integration
test still asserted the string form and failed in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#751)

fmt --upgrade promises to keep comments, but the MDL-DEPR103, -064, -060..063
rewrites replaced whole gaps: 'drop widget -- c\n a' became 'drop a',
'type: /* c */ Reference' became 'type Reference'. A gap that holds more than
whitespace now keeps its text; only the old token goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…list (#751)

The MDL-WIDGET renamed-property suggestion told users to write
`set 'Key' = 'Value'`, the spelling this PR deprecates as MDL-DEPR062.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… part 2)

The remaining single-thing `show` forms get their `describe` spelling and
the words that broke a pattern take the pattern's word. Every old spelling
keeps parsing with its meaning and warns with a registered code (090-096):

- MDL-DEPR090: `show|list page X`, `project security`, `security matrix`,
  `structure`, `context of` -> `describe …` (new describe alternatives that
  build the same statement). `show entity|association|navigation|settings`
  print a summary where describe prints MDL, so they are reported without a
  rewrite.
- MDL-DEPR091: `alter user role … remove module roles` -> `drop`.
- MDL-DEPR092: `alter settings language|workflows remove …` -> `drop`.
- MDL-DEPR093: `column` for `attribute` in alter entity.
- MDL-DEPR094: `rest call` -> `call rest service`; describe emits it.
- MDL-DEPR095: `describe widget X` -> `describe widget type X`.
- MDL-DEPR096: `define fragment` -> `create fragment`; describe emits it.

Syntax entries, skills, docs-site and the quick reference use the canonical
forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
helpStatement was IDENTIFIER helpTopicWord*, the grammar's catch-all: any
statement starting with an unknown word parsed as a help topic and the visitor
dropped it, so 'craete module Foo;' was a silent no-op and 'craete entity
M.E (...)' reported its error at the '('. A predicate limits the rule to
help/exit/quit, and a statement that starts with an unknown word reports
"unknown statement 'craete' — did you mean 'create'?" at the word.

Part of #755.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
connect, disconnect, use, set <key> = ..., status, check, build, lint,
debug, execute script, execute runtime, help and introspect api need a
session or an environment. The grammar keeps parsing them, because the REPL
reads its input with the same parser; a script that holds one is refused
under 'mdl 1;' and warns MDL-V1-SESSION without the header (ADR-0011). The
REPL never validates a program, so it keeps accepting them.

fmt --upgrade --header has no rewrite for it (moving the command out of the
script is a decision about how the script is run), so it is listed as
unrewritable, and the three examples that exercise help and lint keep mdl 0.

Part of #755.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tput

The execute-both property test required an upgraded script to record no
deprecation at all. MDL-DEPR090's summary forms (show entity, …) are
reported without a rewrite by design, so allow exactly the uses the
upgrade listed in Result.Unrewritten, as mdl/upgrade's examples test does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI subcommands structure, context and describe projectsecurity, the TUI
overview preview, the generated CLAUDE.md / tool templates, and three error
hints (slot outside a fragment, an already-enabled language, a binary REST
body) still wrote show structure / show context of / show project security /
define fragment / alter settings language remove / rest call. They now write
describe structure, describe context of, describe app security, create
fragment, drop and call rest service. Also the blog drafts and the
implement-mdl-feature contributor skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Documentation is a /** */ doc comment and the folder a 'folder' clause after
the name, on every document; a workflow activity's caption is 'caption'.
The older places keep parsing as registered aliases that build the same
statement, and fmt --upgrade moves the text:

- MDL-DEPR100: the 'comment' clause on a constant, association, JSON
  structure or image collection. A statement with both spellings keeps the
  one it stored: where the clause won, the doc comment is demoted to a plain
  /* */ comment; where the doc comment won, the clause is deleted.
- MDL-DEPR104: a workflow activity's 'comment', which sets the caption.
- MDL-DEPR105: 'Folder:' in a page, snippet, consumed REST service,
  consumed/published OData service or published REST service header. The
  grammar gains the folder clause after the name on each of them.
- MDL-DEPR106: 'Documentation:' in a regular expression, task queue or
  scheduled event property list.

describe emits the canonical forms. The JSON structure, association, regular
expression, task queue and scheduled event visitors never set
DocumentationSet, so create or modify never updated their documentation
(mendixlabs#1018's rule); they do now, which is also what makes the alias and the doc
comment build the same statement.

Part of #755.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Documentation as a /** */ doc comment, the folder as a clause after the name,
and 'caption' for a workflow activity's caption, in the skills, 'mxcli syntax'
entries, docs-site and the quick reference. The deprecated spellings are
named once, with their codes.

Part of #755.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Outcomes always store a Flow and a PersistentId, activities store the
Studio Pro layout default 0;0, an empty EventSubProcesses list is written
on 11.8+, and the stored names of the implicit activities (start1, end1,
...) are carried onto the rebuild.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UpdateConsumedODataService lays the encoded service over the stored one,
keeping UseQuerySegment, the icon, CustomLocationTemplate, list markers and
the keys Studio Pro stores empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#721 B, #743)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lish under mdl 1 (#764)

Under mdl 0 it still writes and warns MDL-V1-REMOTETYPE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te or modify (#743)

Their rewrites now keep TestApp's documents unchanged, so describe emits
create or modify and a plain create of an existing workflow or client
recommends it again. Strikes the #743 entries and the 27 entries the
generalization flags fixed from the TestApp allowlist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TestPedApp/TestAppDescribeUsesCanonicalSpellings now also fail on
MDL-DEPR100/104/105/106. Control: with the old page describe (Folder: in
the header) the PedApp run reports 38 uses of MDL-DEPR105.

Part of #755.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…755)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ps the comment clause (#755)

R9 moved the documentation of a constant into a doc comment. A text with
`*/` in it ends the doc comment early, so describe emitted a statement that
no longer parses, where the comment clause before held any text. Describe
now keeps the deprecated clause for exactly that text, for constants and JSON
structures. Blank lines and edge whitespace keep the doc comment's
normalisation, as for every other document: falling back for them would put
the deprecated spelling into describe of Studio Pro content (TestApp's
WorkflowCommons constants) and fail the canonical-spelling gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(R7, ADR-0011)

The helpStatement predicate made a statement that starts with an unknown
word an error under every language version. Under mdl 0 such a statement
parsed as a help topic and did nothing, so a script that ran before stopped
running: CI's skill check failed on three `create constant … default ''
PRIVATE;` in the database-connections skill, whose `PRIVATE;` was one.
ADR-0011 applies a new rejection only under the header.

The predicate now also admits any word when the script is not mdl 1
(IsMdl1Parse: the header is scanned before lexing, and an mdl 1 script is
lexed from a StrictEscapeStream). The visitor keeps building nothing for it
and warns MDL-V1-UNKNOWN, which fmt --upgrade --header lists as unrewritable:
which keyword was meant cannot be guessed. Under mdl 1 the error is at the
word, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ako and others added 11 commits September 28, 2026 09:07
…ught (#755)

`create constant … default '' PRIVATE;` never set anything: `PRIVATE;`
parsed as a separate help statement and did nothing, which R7 now reports.
MDL has no private flag on a constant; the advice is to leave the default
empty and set the value per environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ataService (#743)

The overlay's tests call carryStoredConsumedODataService directly, so
dropping its call from the write path left the unit suite green. This test
goes through UpdateConsumedODataService on a fixture and fails with the
call removed (UseQuerySegment reset, Icon dropped).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	mdl/deprecation/deprecation.go
# Conflicts:
#	CHANGELOG.md
#	cmd/mxcli/syntax/features_misc.go
#	docs-site/src/appendixes/quick-reference.md
#	docs/01-project/MDL_QUICK_REFERENCE.md
#	docs/05-mdl-specification/01-language-reference.md
#	mdl/deprecation/deprecation.go
#	mdl/grammar/domains/MDLSettings.g4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	cmd/mxcli/syntax/features_integration.go
#	cmd/mxcli/syntax/features_misc.go
#	docs/01-project/MDL_QUICK_REFERENCE.md
#	mdl/deprecation/deprecation.go
#	mdl/grammar/domains/MDLDomainModel.g4
#	mdl/grammar/domains/MDLSettings.g4
#	mdl/visitor/visitor_document_names_test.go
#	mdl/visitor/visitor_settings.go
# Conflicts:
#	CHANGELOG.md
#	cmd/mxcli/syntax/features_domain_model.go
#	cmd/mxcli/syntax/features_integration.go
#	cmd/mxcli/syntax/features_workflow.go
#	docs-site/src/language/image-collections.md
#	docs-site/src/language/workflow-activities.md
#	docs-site/src/reference/image-collection/create-image-collection.md
#	docs/01-project/MDL_QUICK_REFERENCE.md
#	mdl/deprecation/deprecation.go
#	mdl/executor/cmd_workflows.go
#	mdl/executor/workflow_esp_test.go
#	mdl/grammar/domains/MDLWorkflow.g4
#	mdl/visitor/visitor_workflow.go
…r mdl 1"

A misspelt statement keyword (`craete entity …`) used to parse as a help
statement and build nothing. Rejecting it changes no script's meaning: it
is the same class as the dead grammar #706 removed ungated (proposal §5
items 7-8, forms that never worked). Gating it behind mdl 1 also left REPL
typos silent, because a REPL line never carries a header. So it is an
error at the word under both versions again, as bf0d1d3 had it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	mdl/executor/cmd_odata.go
…n the bare-expression tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ako
ako merged commit 98b8584 into main Sep 28, 2026
31 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

1 participant