Repository navigation
Sync ako/mxcli: ALTER PAGE pluggable fixes, describe round trips, integration import fixes, lint catalog - #1576
Merged
Merged
Conversation
…ixlabs#1499) `revoke create on entity M.E from M.RoleA, M.RoleB, M.RoleC` printed "Unchanged entity access: … — nothing to revoke", exit 0, and every rule kept create. Same cause as mendixlabs#1501: the partial-revoke matcher only touched a rule whose role set equalled the statement's, and create/delete go through that matcher too. fb0cec9 already fixed it on main; its tests pinned only the member-write right. Adds a backend test in the reported shape (one rule per role, two with XPath, one without), the bug-test script, and the finding. Control: restoring the sameStringSet guard fails the test with 0 rules modified and create kept on all three; the pre-fix binary prints the reported notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PYLYzy5hqkdw3nKZm6uuS
…"/"no" `alter page … set (showFooter: true, clearable: false) on cbColor` stored PrimitiveValue "yes" / "no" (mendixlabs#1454). Studio Pro and the widget template store a Boolean as "true" / "false", and the client reads anything but "true" as false, so a bare `true` passed mx check and turned the property off. setPluggableWidgetPropertyMut now spells a bool from the property's declared kind: Boolean -> "true"/"false"; an Enumeration that declares yes/no keys keeps taking a boolean as those keys; any other enumeration refuses a boolean and names its keys. Control: with the fix stashed, TestSetPluggableBooleanWritesTrueFalse_1454 fails with `PrimitiveValue = "yes", want "true"`. On a blank 11.13.0 app the unfixed and fixed builds differ in exactly the two reported values, and both report 0 errors from mx check, which is why only the stored bytes show the defect. Refs #1327 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019httGxDh8UXt1Zoyh3Gh52
… single user` Every single- and multi-user task was written with AutoAssignSingleTargetUser = false (a constant in userTaskToGen and the MCP constructor), the reader never read it, describe never printed it, and MDL had no spelling, so `create or modify workflow` turned off a box ticked in Studio Pro. The runtime then did not assign a task whose targeting yielded one user, and completing it from a microflow failed with "it is not assigned to you". check and mx check were silent. - `auto assign single user` on user task and multi user task, at most once - written from the model, read back on all three task types, described when set - create or modify / replace activity that does not restate a stored `true` is refused, like `await all users` (omission must be able to mean off, so a carry would make it impossible to turn off) - default stays off: Model SDK default false, and Studio Pro 11.14.0 (ako/TestApp) stores false on every new user task Fixes #1445 (mendixlabs#1547) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019MqgMzUGeMdUt9ZBdNkoUr
widgetSlotKeywordOverrides / mdlContainerForWidgetSlot decide that a Data grid 2's filtersPlaceholder is spelled `controlbar` and a Gallery's `filter`. The page mutator is about to resolve the same keyword for `<widget>.<keyword>` targets, so the table moves to mdl/types beside ItemPropertyAliases, for the same reason: two paths reading one table cannot drift. No behaviour change; the executor names are kept as thin aliases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019httGxDh8UXt1Zoyh3Gh52
`alter page … set (DataSource: microflow Shop.DS_Order_Open) on galOrders` passed check, printed "Altered page" and left the page unit byte-identical (mendixlabs#1548); the same statement on a list view worked. A CustomWidget keeps its source in the datasource property's Value, not in a top-level DataSource key, and SetWidgetDataSource's DSet onto that missing key failed with its result ignored. For a pluggable widget the source is now written into that property, via the engine's pluggable serializer (the one CREATE and INSERT use). Microflow and nanoflow sources are written; other kinds are refused with a pointer to REPLACE, and check reports the refusal because its dry run runs the setter. The plain-widget path now checks DSet's result, so no widget can report success without a slot to write into. Control: with the fix stashed, the four new tests fail (source unchanged, no error). On a blank 11.13.0 app the unfixed build leaves both widgets on `database from V.Item`; the fixed build shows `microflow V.DS_Items` for both, and mx check reports 0 errors either way. Refs #1446 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019httGxDh8UXt1Zoyh3Gh52
….controlbar Once a Data grid 2's last control-bar button was dropped, describe printed no `controlbar` and every route back was refused (mendixlabs#957): insert into controlBar1 { … } -> widget "controlBar1" not found … insert into dataGrid21 { … } -> cannot INSERT INTO "dataGrid21" (CustomWidgets$CustomWidget): it is not a simple container The bar is the grid's filtersPlaceholder widgets-property: a slot with no Name. The drop never deleted it (Widgets [2] stays in the BSON); describe just omits an empty slot. A pluggable widget's own widgets-typed properties are now addressed like a data view's footer: `<widget>.<keyword>`, using the keyword describe prints (types.WidgetSlotKeyword) or the property key. INSERT INTO appends, REPLACE swaps the content, DROP empties it, BEFORE/AFTER are refused, and a member that also names a column is refused as ambiguous. ResolveAlterTarget accepts the address too. `insert into <grid>` on the grid itself now lists the slot addresses. Control: with the fix reverted the new tests fail with the reported refusals; the unfixed binary answers `insert into dataGrid21.controlbar` with "column \"controlbar\" … not found". Fixed, on a blank 11.13.0 app (stock Administration.Account_Overview, both buttons dropped) the button comes back inside `controlbar { … }` and mx check reports 0 errors. Refs #1278 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019httGxDh8UXt1Zoyh3Gh52
set / insert before|after / replace / drop on a widget inside a legacy Forms$Table cell reported `widget "X" not found` (mendixlabs#1453): none of the three child walkers (the finder, the data-source walk, the widget-scope collector) descended into Cells[].Widgets[]. All three now do. The descent is keyed on the Cells list rather than a $Type, because a Mendix 11 cell is stored as Forms$DbTableCell, which gen does not model; Forms$Table is the only page type with a Cells list. Control: with the walk reverted, the new tests fail with `widget "btnInCell" not found` and, for the scope collector alone, "btnInCell missing from the widget scope". Not verified against a Studio Pro-authored table: none exists in testdata, MDL cannot create one, and mx convert refuses Forms$TableCell (widget_list_markers.go). For the same reason there is no bug-test script. A wrong guess at the key fails safe as the same not-found. `insert into` an empty cell still has no address; a cell stores no name. Refs #1326 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019httGxDh8UXt1Zoyh3Gh52
fix: ALTER SET writes a pluggable Boolean as "true"/"false", not "yes"/"no"
fix: ALTER PAGE SET DataSource writes a Gallery's / Data grid 2's source
fix: ALTER PAGE can address a pluggable widget's slot, e.g. dataGrid1.controlbar
fix: ALTER PAGE reaches widgets inside legacy table cells
…n error handler (#1465) The error-handler walk stopped at the first merge with more than one way in, which is the merge closing an `if` in the handler body, so everything after `end if;` was dropped and drop + create rerouted the handler into the main flow. Stop instead at the first merge with a way in from outside the handler. Loops in a handler printed only their header; emit the body. Fixes #1456 (mendixlabs#1559). Claude-Session: https://claude.ai/code/session_01JgXjVr9Tw5KRof2jj4JyAv Co-authored-by: Claude <noreply@anthropic.com>
test: pin revoke create from several single-role rules (mendixlabs#1499)
fix: user tasks always written with auto-assign off; add `auto assign single user`
… events in folders
An event's container is a folder whenever it is not at the module root,
which is how Marketplace modules ship theirs. The iterator mapped the
direct container to a module, so module_name was "", qualified_name became
".Name", and IsExcluded("") let excluded modules through. Walk the folder
chain to the module, as the catalog's hierarchy does.
The test helper closed the catalog before returning it, so the module
lookup failed silently in every existing test; it now closes on cleanup.
Fixes #1452 (mendixlabs#1555)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FiK7bYZEqF75x9DeX6Eyrm
buildPermissions read entities, microflows, pages and OData services but never nanoflows, so a stored GRANT EXECUTE ON NANOFLOW was invisible to every lint rule. Emit element_type NANOFLOW / access_type EXECUTE rows from the nanoflow's AllowedModuleRoles, and document the value in the write-lint-rules skill. Fixes #1434 (mendixlabs#1536) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiK7bYZEqF75x9DeX6Eyrm
A statement after `return` in the same block, or after a `while true` that has no `break`, passed check, exec wrote it, and mx check refused the flow. New rule MDL-FLOW05 (error) refuses the first dead statement of each block after return, raise error, break, continue, join, an if/else that ends on both sides, or a `while true` with no break of its own. A `merge` makes the path live again. Runs for microflows and nanoflows; stands down for an @excluded document like the other #893 gap rules. Measured on mxbuild 11.13.0, one microflow per shape (mx check -j for the per-document attribution): log after return in an if CE0709 log after break / continue CE0709 log after raise error / return in a handler CE0105 + CE0709 log after join CE0105 + CE0709 log after if/else returning both CE0104 + CE0105 log after while true (no break) CE0104 + CE0105 return after that loop CE0104 return after loop whose only break is in a nested loop CE0104 nanoflow, log after return in an if CE0709 Controls, 0 errors: nothing after the loop, break then return, an if without else that returns, merge after a return reached by a join. Splits are not treated as ending the path (an all-returning enum split is CE0104 but with CE0079, whose fix is the empty outcome). Control: with the rule unwired, the test reports 0 findings on all nine dead shapes. The cheatsheet listed CE0104 for code after a return in an if; that shape is CE0709. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJcUHPvAr7SDBcAoGHYcPQ
The catalog had no table for MessageDefinitions$MessageDefinitionCollection or its Mendix 11.15 replacement MessageDefinition2, so the objects view behind documents() never held one and a naming rule on them could not fire. Index both (message_definition_collections, message_definitions), union them into objects as MESSAGE_DEFINITION_COLLECTION and MESSAGE_DEFINITION, map them for bare DESCRIBE, and bump the catalog schema to 26 so cached catalogs rebuild. Fixes #1433 (mendixlabs#1535) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiK7bYZEqF75x9DeX6Eyrm
…abs#1498) `create nanoflow … export level api` parsed, passed check and exec reported "Created nanoflow", but the document was stored with ExportLevel "Hidden" and describe printed no clause; the same clause on a microflow was stored as API. Nanoflows share the microflowOptions rule, so the clause parsed, but ExitCreateNanoflowStatement read only FOLDER and EXPOSED AS. CreateNanoflowStmt and sdk Nanoflow now carry ExportLevel (absent = carry the stored level); nanoflowToGen writes an authored one and carryStoredNanoflowHeader stands down for it; the reader fills it; both describe renderers print `export level api`; the create-or-modify header diff treats an absent clause as the stored level. Measured on 11.13.0: create -> API; create or modify without the clause keeps API; with `hidden` -> Hidden; describe -> exec into a fresh project -> API; mx check 0 errors on both. genMf.VersionInfos records no introduction for MicroflowBase.exportLevel, so it is declared at every supported version. Control: with the non-test changes reverted the backend test stores "Hidden" (the reported symptom), and the exec and describe tests fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJcUHPvAr7SDBcAoGHYcPQ
…tion The backend already read UserRole.Description, but the lint struct dropped it, so a rule "every user role is documented" could not be written; the catalog had only role_mappings, with no row per user role. Add `description` to user_roles() and a CATALOG.USER_ROLES table (Name, Description, IsAnonymous, ModuleRoleCount), schema 27. DESCRIBE/CREATE USER ROLE already carry Description on main (#707). Fixes #1442 (mendixlabs#1544) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiK7bYZEqF75x9DeX6Eyrm
parse_xpath fed the constraint to the microflow-expression parser, which has no entity-rooted path: `Mod.A/Mod.E/Name = 'x'` came back as just qname Mod.A. exprcheck gains an XPath mode, used only by parse_xpath, in which such a path is an attr_path with an empty variable and a per-segment `predicates` list; expression mode still reports it (E014). Rule files ran under Starlark's legacy options, which forbid recursion, so any tree walker -- including the write-lint-rules skill's count_not example -- failed with "function called recursively". Recursion is on (the interpreter still stops at 100,000 frames). The example itself also matched `not` as a call instead of a unary NOT and used `sum`, which Starlark lacks; it is rewritten and a test now runs it from the skill. matches() has no captures, so find_all(s, pattern) returns them, shaped like Python's re.findall. Fixes #1439 (mendixlabs#1541) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiK7bYZEqF75x9DeX6Eyrm
…dule fix: scheduled_events() empty module_name and excludeModules leak for events in folders
fix: permissions() has no rows for nanoflow EXECUTE grants
fix: user_roles() and the catalog do not expose a user role's description
Resolve the catalog schema conflict with #1442's user_roles (27): this change becomes 28, since a cache built at 27 has no message-definition tables and would never rebuild for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiK7bYZEqF75x9DeX6Eyrm
Resolve the write-lint-rules skill's generated line index (#1442 added a user_role row); regenerated with `make skill-index`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiK7bYZEqF75x9DeX6Eyrm
fix: check refuses a statement that can never run (mendixlabs#1491)
fix: a nanoflow's `export level api` is written, not dropped (mendixlabs#1498)
`describe module … with all` printed each entity's access rules with the entity, ahead of the module's associations. A grant naming an association the entity owns was then refused on replay — "entity ModA.EntA has no member(s) EntA_EntB; grant only names members of the entity or of an entity it inherits from" — after `check` had passed the same script (mendixlabs#1409, #1315). describe module now prints entities, then associations, then every grant. check's grant pass resolved members against the whole script and skipped script-declared entities, so it could not see the ordering exec enforces; it now reports a grant naming an association created further down, with the reorder hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wx8dgxQm9qE8ZsMxwRweXj
… mapping ImportMappingCall.ContentType was always written as "Json", so re-executing a microflow whose `import from mapping` uses a mapping built on a web service operation failed mx check with "The selected import mapping cannot handle content type JSON." until the activity was opened in Studio Pro (mendixlabs#1232, #1290). The builder derives the content type from the mapping's source (XML schema or web service -> Xml, otherwise Json), the reader carries a stored one so a rewrite keeps it, and REST call mapping result handling uses the same field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wx8dgxQm9qE8ZsMxwRweXj
Copying Atlas_Core.Atlas_Default with `describe layout` -> `create layout` dropped its Forms$SidebarToggleButton, which describe printed as a NOT re-executable comment; Atlas's slide-in navigation on phones and tablets depends on it (mendixlabs#1220, #1285). `sidebartoggle name (Caption, Tooltip, ButtonStyle, Icon, RenderMode)` shares an action button's face and has no action. The written widget has the key set of Atlas's own; modelsdk/gen bound its caption under the SDK name `Caption`, so the storage key is overridden to `CaptionTemplate` and struck off the key-audit ledger. The toggle only works on a region that can be toggled: a real mx check of the copy reported CE0628 because a scroll container region's ToggleMode was never described or written. Regions now round-trip `ToggleMode`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wx8dgxQm9qE8ZsMxwRweXj
fix: documents() never returns message definitions
fix: parse_xpath drops association paths; lint rules cannot recurse
…r fails CE6524 The reverse traversal of a Reference was always stored as a list (MaxOccurs -1). A Reference owned by both ends is one-to-one, so from the TO side it is a single object too; storing -1 made mx check report CE6524 "The occurrence of '...' has changed" on CREATE, CREATE OR MODIFY and ALTER ... ADD MEMBER. Measured on 11.12.1: the repro gives 2x CE6524 before, 0 errors after; the forward definition in the same script is the control (clean on both). Fixes #1311 (mendixlabs#1386). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PnpNHdpFqu1sPL7yNAQgTh
…s Studio Pro does `create external entities from M.Client into M` named a top-level entity after its OData entity set (`ModA.Parts`); Studio Pro names it after the entity type (`ModA.Part`), so scripts written against that name failed with "entity does not exist". Re-import resolves an existing entity by service + entity set before name, so a module imported by an older mxcli is updated in place and keeps its entity-set name instead of growing a type-named twin mapping the same set. The navigation-association and primitive-collection passes use the same resolver. Measured on 11.12.1: base reproduces "external entity not found: Issue1432.Product"; fixed creates Product/Category, mx check 0 errors; a base-imported project re-imported by the fix reports 0 created, 2 updated. Fixes #1319 (mendixlabs#1432). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PnpNHdpFqu1sPL7yNAQgTh
…CE6618 createNavigationAssociations made one association per navigation property, so both ends of a bidirectional (Partner) relationship became separate associations and mxbuild rejected each with CE6618 "Navigation '...' is not available in the OData service". Measured on 11.12.1, Partner is the line, not multiplicity: unpartnered collections build clean. A partnered one-to-many pair is now ONE Reference from the single-valued end naming both navigation properties (BothDirections) - 0 errors. Partnered many-to-many and one-to-one pairs build in no form (one association, two, or Owner Both), so they are skipped and reported. A re-import names associations an earlier import made the old way as runnable `drop association` lines; drop + re-import builds clean. Repro: base 6x CE6618, fixed 0 errors. Fixes #1289 (mendixlabs#1231) asks 2 and the root cause; ask 1 (DROP ASSOCIATION) already existed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PnpNHdpFqu1sPL7yNAQgTh
describe layout -> create layout dropped two things from Atlas layouts:
- Phone_/Tablet_ layouts: their Forms$Header (printed NOT re-executable),
with the HeaderLeft / HeaderRight placeholders pages bind to (CE1613) and,
on Phone_Sidebar / Tablet_Sidebar, the sidebar toggle. With region
ToggleMode now copied, those two failed mx check with CE0611 "A sidebar
toggle button is required if a region can toggle".
- Native layouts: the header placeholder, ShowBottomBar and the sidebar,
dropped with no note; the content was written with 4 of the 8 keys
Studio Pro stores.
`layoutheader name { region left { … } region right { … } }` writes the
header. A native layout takes `showbottombar: true`, `region header
{ placeholder X }` and `region sidebar { … }`.
Measured on a new 11.13.0 app: all 22 Atlas_Core layouts copied, with
pages bound to the copied header and sidebar placeholders, build with 0
errors; main's copy of NativePhone_SideMenu fails CE1613 twice. The CE0535
earlier blamed on the copy is the PedApp fixture's own, not a copy defect.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wx8dgxQm9qE8ZsMxwRweXj
…-trip-gaps-fix-thcbcr
…p6ini-1311 fix: a one-to-one association member in a message definition no longer fails CE6524
…p6ini fix: create external entities names entities after the entity type, as Studio Pro does
…p6ini-1289 fix: importing partnered OData navigation properties no longer fails CE6618
…-thcbcr fix: describe round-trip gaps — grant order, import content type, Atlas layout copies
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.
Syncs
ako/mxcli:maininto upstream: 22 commits from fork PRs ako#1460–ako#1466, ako#1469–ako#1475 and ako#1478–ako#1481.ALTER PAGE
"true"/"false", not"yes"/"no"(alter ... set on a pluggable Boolean property writes "yes"/"no" instead of "true"/"false" #1454). The client reads anything other than"true"as false, so a cleanset (showFooter: true)used to turn the option off.alter page … set (DataSource: microflow …) on <gallery | datagrid>reports "Altered page" and writes nothing #1548). It used to print "Altered page" and leave the unit byte-identical.dataGrid1.controlbar(ALTER PAGE cannot create or populate an empty classic-grid control bar; DROP of the last button deletes the bar itself #957). Once the last control-bar button was dropped, there used to be no way back.mdl/types. This is a refactor and is its own commit.describe → exec round trips
if, and loop bodies, inside an error handler are kept (describe microflowdrops what follows anif … end ifinside anon errorhandler, and prints a loop there without its body; rebuilding from the output silently reroutes the handler into the main flow #1559). Before, everything afterend if;was dropped, and drop + create rerouted the handler into the main flow.describe module … with allno longer refuses them.import from mappingimports XML for an XML schema or web service mapping (regenerating import XML provides model error #1232). The content type used to be written as JSON every time.sidebartogglewidget for Atlas's responsive sidebar toggle.create or modify workflowresets it #1547). It was always written off, and the newauto assign single userclause lets a script say so.export level apiis written, not dropped (Protected (add-on) modules: export levels are not shown by DESCRIBE and not checked, socheckpasses builds that fail with CE1406 and CE5400 #1498).Integrations
create external entitiesnames entities after the entity type, as Studio Pro does (create external entities from M.Client into Mnames entities after the entity set, not the entity type #1432).mx checkfails with CE6524 (CREATE and ALTER … ADD MEMBER) #1386).check
checkpasses a statement that can never run (afterreturn, or after awhile truewith nobreak), andmx checkthen fails with CE0104 / CE0105 / CE0709 #1491). For example, a statement afterreturn, or after awhile truewith nobreak.Lint and catalog functions
parse_xpathkeeps association paths, and lint rules can recurse (lint: parse_xpath stops at association paths; Starlark has no recursion #1541).user_roles()and the catalog expose a user role's description (User role documentation is not exposed (catalog, DESCRIBE, lint) #1544).documents()returns message definitions, including 11.15'sMessageDefinition2(lint: documents() never returns message definitions #1535).permissions()has rows for nanoflow EXECUTE grants (lint: permissions() has no rows for nanoflows #1536).scheduled_events()fillsmodule_nameand honoursexcludeModulesfor events in folders (lint: scheduled_events() ignores excludeModules and has an empty module_name #1555).Tests
revoke createfrom several single-role rules (revoke create on entitysays "nothing to revoke" and exits 0 while the rule still grants create #1499), which has the same cause asrevoke write (<attribute>)says "nothing to revoke" on rules whose default member access is ReadWrite; the attribute stays writable #1501