R2 part 3: menus, property maps and database connections take ( ) properties and { } children (#754) - #783
Merged
Conversation
`if not exists` was accepted on create entity and create association only (and parsed-then-ignored on view entities). ADR-0010 R1 makes it the separate "leave it alone" operation beside create or modify, on every type. - grammar: `ifNotExists?` after the kind's keywords, before the name, in every create rule that names one element. Not on annotation, index, validation rule, navigation, translations, external entities. - ast: CreateGuard embedded in every named create statement. - visitor: the guard is applied once, in ExitCreateStatement, to whichever statement the create rule built; a type that cannot carry it is an error. - executor: Registry.Dispatch probes existence and skips before the handler (entity/association keep their in-handler check); stmtCreateInfo counts the guard as idempotent for every kind; MDL067 (or modify + if not exists) covers every kind. - docs: mxcli syntax create-if-not-exists, basics.md, quick reference, check-syntax skill; finding recorded. describe never emits the guard, so the round trip is unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ets (#754) Menu items are { } children with no ';' separators, and an item's action and icon are its properties: menu item 'Home' ( OnClick: show page M.P, Icon: I ). A navigation profile's items are its own { } children. The property maps are in ( ): a page or snippet header's Params/Variables, ContentParams/CaptionParams/<Name>Params ({1} = e), DesignProperties, a snippet call's arguments (P = $v, R4) and a REST operation's Headers ('Name': value). A database connection is ( Type: ..., ConnectionString: @M.C, ... ) { query Q ( Sql: ..., Parameters: ( ... ), Returns: M.E, Map: ( Attr = column ) ) }. Every old spelling keeps parsing under both language versions, builds the same statement, warns (MDL-DEPR120..127) and carries a structural fmt --upgrade rewrite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… brackets (#754) describe navigation/menu, describe page/snippet (Params, Variables, ContentParams, CaptionParams, <Name>Params, DesignProperties), describe consumed rest service (Headers) and describe database connection emit the canonical forms, so their output re-parses without recording any deprecated spelling. Hints in validator messages use the same forms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#754) Skills (synced), docs-site, the quick reference, the language reference, mxcli syntax entries, mdl-examples and the layout scaffold use the canonical spellings. The .mdl files and parseable markdown blocks were converted by applying only the visitor's own MDL-DEPR120..127 rewrites; syntax patterns and prose were edited by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#731) exec skips `create … if not exists` when the element is already there and leaves it untouched, but diff compared the script's definition against the stored one and reported the element as modified — a pre-apply view that disagreed with what exec does. The stored definition is now what the guarded statement leaves behind. Tests: existing -> unchanged; controls for the unguarded form (modified) and an absent element (new). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h no Sql (#754) Review of #783. The new menu-item, database-connection and query property lists were described as strict but took a duplicated key silently: a menu item with two OnClick actions was written with whichever the builder checked first. A query's Sql became optional by accident (the clause form required it), and a sub-menu accepted an OnClick that the old spelling could not write and describe never prints. All three are now errors in the new syntax only; the old spellings are unchanged. Also: a half-converted sub-menu, `menu 'X' icon I { … }`, parses as the MDL-DEPR122 alias and upgrades; and MDL058's suggestion writes the canonical `Username: @M.C` rather than the deprecated clause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rop } (#712) alter workflow M.W { set (Key: value) [on <activity>]; insert before|after <activity> { ... } insert into <activity> { outcomes | path | boundary event } replace <activity> with { ... } drop <activity> [member]; }. The old per-action forms build the same operations and are registered aliases MDL-DEPR140-149 with fmt --upgrade rewrites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…712) WorkflowMutator embeds AlterTargetResolver (shared rule in backend.ResolveWorkflowActivityTarget); the executor resolves every target before applying anything, and checks an inserted path's number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#765) `refresh catalog full source` describes every document from a goroutine pool that shares one ExecContext.Cache. describeEntity -> findModule filled executorCache.modules lazily with no synchronisation, and preWarmCache did not warm it, so workers each listed the modules and published the slice while others read the field: two DATA RACE reports per refresh under -race. The module list is now filled and invalidated under executorCache.modulesMu (cachedModules); the page builder's getModules goes through the same path. Tests: TestModuleCache_ConcurrentFillListsOnce (detector-free: 8 concurrent lookups on a slow mock list once; 8 without the fix) and TestParallelEntityDescribes_NoDataRace (-race over PedApp's entity describes on the catalog's parallel path). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The start activity is an addressable member of the flow (`start1`, caption 'Start'), and the new `insert before` put activities ahead of it: exec said "Altered workflow" and mx check (11.14.0) answered CE9526 "Main process in workflow should start with a start event". The shared activity-kind guard now refuses it for both passes and both backends, pointing at `insert after <start>`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…otchas (#712) Two gotchas still named the old `set activity` / `replace activity` forms, and the start-activity refusal for `insert before` is now documented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The full describe (layout flows, ELK, diff, with handles, create or modify's diff) misrouted the merge-to-split flow in CxLayout.CX_VAL_Factory and CX_VAL_EmailTemplate: when a decision's merge wraps onto a new row the flow leaves the merge's bottom, but a merge has no statement, so describe wrote only the next split's `to: top` and the rebuild drew the flow out of the merge's right side. `from:` on an if's @anchor is now that exit side, the same slot `from:` has on any statement (the flow leaving it). Describe emits it for the merge the @merge line already names; the builder hands it to the merge-out flow at the top level (it already honoured it inside branch bodies), and case / type-split branches clear a nested if's leftover exit anchor so it cannot land on the flow out of the enclosing merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…header templates and images (#707) The part of #707 that #728 left open. Each item was a property describe printed as a `--` comment or a side file because the grammar had no slot for it, so a replay lost it: - User role properties are a ( Key: value ) list: ModuleRoles, Description, ManageAllRoles, ManageableRoles, ManageUsersWithoutRoles, CheckSecurity. The list is optional, so a role with no module roles describes as `create or modify user role X;` and parses. The positional form is the deprecated alias MDL-DEPR710 with an fmt --upgrade rewrite. New backend method SetUserRoleProperties; `create or modify` sets only the stated properties. - Workflow notes: `@annotation '...'` before an activity or event sub-process, and an `annotation '...'` header clause for the workflow. - A consumed REST header value is a template (`'Bearer {Token}'`). `'Bearer ' + $Token` stored only "Bearer "; it now builds the template and is the deprecated alias MDL-DEPR711 with an fmt --upgrade rewrite. - Image collection describe writes `Data: '<base64>'` (Format only when the bytes do not show it) instead of /tmp/mxcli-preview paths; the TUI preview decodes it; File: paths resolve script-relative. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… image Data (#707) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # mdl/grammar/domains/MDLSecurity.g4
# Conflicts: # mdl/ast/ast_page_v3.go # mdl/grammar/MDLParser.g4 # mdl/grammar/domains/MDLService.g4
…abase connections and REST headers (#754) describe prints a database connection as ( Type: …, ConnectionString: … ) with a query's Map: ( Attr = column ), and a REST header as 'Name': 'value'; the integration round trips still looked for the keyword forms. The header inputs use ':' too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ako
added a commit
that referenced
this pull request
Sep 28, 2026
The examples #790 migrated conflicted with #783's edits; each takes the chain's side and is re-upgraded with the chain's fmt --upgrade, which also knows wave 4's new aliases (MDL-DEPR130..149, 710) — so the examples that merged cleanly but used those spellings are upgraded too. The deprecated-aliases corpus is left verbatim. The conformance allowlist follows #787's docs renames (show-* -> list-*) and shrinks by what wave 4 fixed; two new fragments are made conformant (the #767 anchor example as a whole microflow, #783's header template in a text fence with ':'). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 tasks done
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.
Closes #754 (phase 3.4, R2). This is the third and last PR for the issue, after #771 (flow blocks) and #776 (integration documents). It covers what #776 listed as the remainder:
What changes
Headers: ('Accept': 'application/json')('Accept' = 'application/json')navigation P home page M.H { menu item … menu 'X' { … } },create menu M.M { … }menu ( item; … ),create menu M.M ( … ),menu 'X' ( … );, and a;after an itemmenu item 'Home' ( OnClick: show page M.P, Icon: I ),( OnClick: call microflow M.F ),( OnClick: sign out ),menu 'X' ( Icon: I ) { … }menu item 'Home' page M.P icon I,microflow M.F,sign out,menu 'X' icon I (Params: ( $Order: M.Order ),Variables: ( $show: Boolean = 'true' ){ … }ContentParams: ({1} = Name), alsoCaptionParamsand<Name>Params[{1} = Name]DesignProperties: ('Spacing': ('margin-top': 'Large'), 'Full width': on)['Spacing': ['margin-top': 'Large'], …]Params: (Asset = $Asset){$Asset: $Asset}/{Asset: $Asset}create database connection M.Db ( Type: 'PostgreSQL', ConnectionString: @M.Url, Username: @M.U, Password: @M.P ) { query Q ( Sql: $$…$$, Parameters: ( p: Integer default '0', q: String null ), Returns: M.E, Map: ( Attr = column ) ) }type '…' connection string @… username … password … begin query Q sql … parameter … returns … map (column as Attr); endThe two spellings build the same AST. This is checked in three ways:
reflect.DeepEqualtest;No BSON writer changed.
Snippet call parameter name. The one visible AST normalisation is that a snippet call's parameter name is now stored without its
$for both spellings.$AssetandAssetused to be stored as written, and the page builder stripped the$anyway, so written BSON is unchanged.Warnings and rewrites. Each old form is a registered alias marked
/* @alias MDL-DEPR12x */in the grammar. It warns under both mdl 0 and mdl 1.fmt --upgraderewrites it structurally from the parse tree, replacing clause words and inserting punctuation, so every value, string and SQL body stays where it is. That means the gated string-escape rewrite can never overlap it.sign_outinside a menu item keeps MDL-DEPR020's token rewrite. The DEPR122 rewrite inserts around that word instead of replacing it.describe prints the canonical forms. That covers navigation, menus, page and snippet headers, widget template parameters and design properties, REST headers and database connections. describe navigation now puts the
{ … }menu block afteron sync errorandsync ( … ). The clauses are order-free, so the old output and the new one build the same statement.New syntax is strict. The database connection's property list and the query properties refuse an unknown key or a value of the wrong kind. A menu item's
OnClick:takes only the three actions an item can carry.Docs are moved to the canonical forms:
make sync-skills;MDL_QUICK_REFERENCE.md, the language reference andmxcli syntaxentries;mxcli helptext, the layout scaffold, validator hints,mdl-examples, CHANGELOG, and a "Decided in R2: three brackets, three meanings — integration documents, navigation/menus, on error, while (3.4) #754" note in the proposal.The
.mdlfiles and the parseable markdown blocks were converted by applying only the visitor's own DEPR120–127 rewrites. Syntax patterns and prose were edited by hand.Design choices the ADRs did not settle
{ }children, as in the proposal's after-example, notmenu { … }.menu (becomes{, and the block is not moved. describe writes it last.home page … [for Role],login page,not found page,on sync errorandsync ( sync M.E all; … ). The proposal's( HomePage: … )header needs a design for role-based home pages. The sync rules would need a child shape,sync M.E ( Mode: … ). Both are left out (see follow-ups).( key: value )" reads against R3 for two of the maps::.=, aswith ({1} = …)does (ADR-0010 R4). SoContentParams: ({1} = e)is a bracket swap only.(P = $v), without the$(R4).Parameters: ( name: Type [default '…' | null] )keeps the old test-value tail as written. There is no$, because the SQL references{name}.Map: ( Attr = column ). It binds the attribute the way a REST mapping side does (Attr = jsonField), which puts the two in the opposite order to the oldcolumn as Attr.Type:stays a string ('PostgreSQL'), since the stored value is written verbatim and'BYOD'is also valid. The proposal's example wrote a barepostgresql.on/off. The proposal's R3 example writes'Full width': true. That is a value change, not a bracket change, so it is not part of this PR.;after an item is still read inside either block, with a MDL-DEPR121 warning, so a half-converted menu parses.Test plan (what I ran)
make buildgo test ./mdl/... ./api/... ./sdk/... ./cmd/mxcli/...: all ok.mdl/visitor/r2_rest_test.go: 10 cases, one old spelling each: REST headers, navigation menu block, menu document, menu item clauses, page header maps, snippet header maps, template parameters, design properties (nested and empty), snippet call arguments, and database connection (two queries,defaultandnullparameters, map). Each checks:Also covered: exact rewrite text for mixed menu, navigation, snippet call and database-connection forms; unknown and mistyped database-connection keys; and a menu item refusing
OnClick: save changes.mdl/upgrade/r2_rest_test.go:TestUpgrade_R2NavigationMapsAndDatabaseConnection. It runs one script with every old spelling mixed in single statements, in both letter cases, with a comment inside the menu. It checks the exact output, the rewrite counts, and that a second upgrade is a no-op.mdl/executor/r2_rest_describe_test.go: describe output for a navigation profile, a menu, a database connection, REST headers, widget maps and a page header (mock backend). Each output re-parses with zero deprecations, and the navigation and database-connection statements round-trip field by field. Controls: a profile with no menu gains no block, and a connection with no queries ends at its property list.Registry examples for DEPR120–127 are checked by
TestRegistryExamplesRecordTheirCodeandTestUpgrade_EveryRegistryExample.make test-integration-roundtrip(PedApp + TestApp): ok. No allowlist entry changed; the menu entries are Round-trip harness: untracked describe → exec losses on the Studio Pro fixture (microflows, entities, pages, snippets, menus, roles, JS actions) #721's nested-items class and still fail.make test-integration-upgrade(execute-both property test over the converted mdl-examples): ok.make check-skill-mdl: all checkable blocks pass.make lint: passed.Revert checks
r2RestUse(n, use)commented out). These fail:TestR2Rest_OldFormIsADeprecatedAliasTestR2Rest_RewriteTextTestRegistryExamplesRecordTheirCodeTestUpgrade_R2NavigationMapsAndDatabaseConnectionTestUpgrade_EveryRegistryExampleorigin/main: all seven new describe tests fail, each reporting the matching code (DEPR121/122 for navigation and menu, 127 for both connection tests, 120 for headers, 124/125 for widget maps, 123 for the page header).Not verified in Studio Pro: no BSON writer changed, and both spellings build the same statement. The equality tests and the execute-both property test show that.
Follow-ups (not needed to close #754's bracket rule)
( HomePage: …, LoginPage: …, NotFoundPage: … )), with a design for role-based home pages.sync ( sync M.E all; … )rules as{ }children (sync M.E ( Mode: … )).true/falseinstead ofon/off, as the proposal's R3 example writes them.export level/commentclauses as( … )properties, left over from R2 part 2: integration documents take ( ) properties and { } children; alter microflow fragments are begin … end (#754) #776.🤖 Generated with Claude Code