Skip to content

Sync mendixlabs/mxcli main into ako/mxcli main - #689

Merged
ako merged 15 commits into
mainfrom
claude/laughing-brahmagupta-e3niwq
Sep 25, 2026
Merged

ako merged 15 commits into
mainfrom
claude/laughing-brahmagupta-e3niwq

Conversation

@ako

@ako ako commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Summary

Brings the 14 upstream commits from mendixlabs/mxcli main (up to 86d395ee, mendixlabs#1179) into this fork. The sync was blocked by a single conflict.

Upstream changes included:

Conflict resolution

The only conflict was in CHANGELOG.md → ## [Unreleased]. Both sides added entries there:

I kept all of the fork's entries and added the upstream bullet at the end of the fork's ### Fixed list. Every other shared file merged without a conflict, including mdl/executor/validate_microflow.go, mdl/linter/context.go, sdk/microflows/*, the quick-reference docs and the findings .jsonl (merge=union).

Validation

  • make build: passes
  • make test: passes, no failures
  • make check-findings: passes (1233 records)
  • go vet ./mdl/... ./sdk/...: only warns about unreachable code in the generated mdl/grammar/parser/mdl_parser.go

Merge with a merge commit, not squash, so that main records the upstream commits as ancestors and the next sync starts from this point.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QDNtypktPBXPyyTChWGKrb


Generated by Claude Code

claude and others added 15 commits September 24, 2026 07:40
An entity access rule carries a 'default rights for new members' setting
(None / ReadOnly / ReadWrite) that decides what access a newly added
attribute or association inherits. A rule enforcing a safe default - so
that adding an attribute can never silently grant write access - cannot be
written today, because the value is neither stored in the catalog nor
exposed to Starlark.

The builder already reads it: entityAccessFromMemberRights derives
read/write from rule.DefaultMemberAccessRights and then discards the
value. This stores it instead.

Modelled on XPathConstraint, which is likewise a property of the rule
rather than of the access type and is already repeated across the rows one
rule produces. Non-entity permissions (microflow, page, OData) write NULL.
A 'Call REST service' activity has a 'Use a timeout' toggle and a timeout
in seconds. Neither reaches a custom lint rule, so the rule 'every outbound
REST call must have a timeout' cannot be written: a rule can identify a
RestCallAction but learns nothing about how it is configured.

Storage names verified against a real MPR with 'mxcli bson dump':

  "TimeOutExpression": "300"
  "UseRequestTimeOut": true

Note the seconds are stored as an expression string, not an integer, and
that UseRequestTimeOut was not read at all - microflowActionFromGen built a
RestCallAction without it, so the value was lost on read.

Changes: read UseRequestTimeOut in microflow_read_actions.go, carry it on
sdk/microflows.RestCallAction, store both it and the existing
TimeoutExpression on the activities table, and expose them as
use_request_timeout / timeout_expression. Follows the ServiceRef/ActionRef
precedent.

Read side only. microflow_write.go currently writes UseRequestTimeOut as a
hardcoded true, which will silently flip a false back to true on rewrite -
a separate bug, left alone here to keep this change scoped to linting.
A while loop builds the same Microflows$LoopedActivity as a for-each loop,
which has no Caption property, so @caption on it was dropped by exec. MDL042
was raised only in the LoopStmt case; it now lives in checkCaptionOnLoop and
is called for WhileStmt too, pointing to @annotation instead.
…adicts

Review follow-up on mendixlabs#1188. MDL042 now tells the author a loop's @caption is
dropped because the activity has no Caption property -- while two files away
the builder still ran

    case *microflows.LoopedActivity:
        // LOOP / WHILE activities can carry a caption just like splits
        activity.Caption = ann.Caption

and the describer still emitted @caption for one. Nothing read either back:
generated/metamodel -- the arbiter -- declares no Caption on
Microflows$LoopedActivity, microflow_write.go sets none on the gen object, and
the reader therefore cannot populate one. The next contributor reads the
builder, concludes the new warning is wrong and deletes MDL042, reopening
mendixlabs#1187 from the other side.

Measured before deleting anything, with the UNMODIFIED branch build against a
Mendix 11.6.6 project: `@caption` on a loop and on a while are both absent from
`describe microflow` after `exec`, while `@annotation` on either round-trips.
So the value died at the gen boundary and the assignment only ever populated an
in-memory field.

That is also why the code survived: three tests asserted it. They tested the
semantic object, never storage, so they passed throughout and would have failed
on the correct fix. Inverted rather than deleted, each naming the measurement:

  TestLoopCaptionPreserved       -> TestLoopCaptionNotStorable
  TestWhileLoopCaptionPreserved  -> TestWhileLoopCaptionNotStorable
  TestEmitObjectAnnotations_LoopCaption -> ..._LoopCaptionNotEmitted

The describe test's real value was mdlQuote escaping, which
TestMdlQuote_* already covers directly, so retargeting loses nothing.
Reinstating the assignment fails the two builder tests -- checked.

Also adds the bug-test script the checklist asks for, covering both captions
(MDL042 x2) and both @annotation forms (silent), and keeps sdk/microflows
LoopedActivity.Caption with a comment saying it cannot be stored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From the review of mendixlabs#1188. The fix added MDL042 for a @caption
on a while loop -- telling the author the loop activity has no Caption property
-- while the builder two files away still assigned one, under a comment saying
loops can carry captions, and the describer still emitted it. Nothing read
either back.

The part worth recording is why such code survives: it had three tests. All
asserted the caption was carried, all against the semantic object and none
against storage, so they passed throughout and failed only when the dead code
was removed. A reviewer who stops at "the tests pass" concludes the capability
is real.

The canonical fix therefore carries the measurement step -- exec then describe
on a real project with the UNMODIFIED build, so the deletion rests on the
stored document rather than on reading the codec -- and the instruction to
invert those tests rather than delete them, keeping any half still true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…caption-mdl042

fix(check): report MDL042 for @caption on a while loop (mendixlabs#1187)
…gs-row-32

docs(review): record the dead-code-beside-a-new-diagnostic pattern
…timeout

feat(lint): expose the Call REST service activity's timeout
The matrix claimed two shipped document types were unavailable, which is the
failure its own "Keeping this honest" note warns about -- a row claiming a gap
that has since been filled sends people to Studio Pro for work mxcli can do.

  - Microflow rules sat under "Not Yet Implemented" ("no MDL surface at all")
    while CREATE [OR MODIFY] RULE ships with a skill, rules.mdl, a
    `microflow.rule` syntax topic, a CATALOG.RULES view and rule REFS edges.
  - Message definitions likewise: SHOW / DESCRIBE / CREATE OR MODIFY / DROP /
    ALTER all ship, with tests and examples.

Both now have rows in Core Document Types instead.

Layouts were recorded as read-only in four places -- the row's CREATE, OR
MODIFY, DROP, ALTER, Examples, Tests, Skills and Syntax cells were all N, and
three gap lists repeated "Read-only". All eight are Y. OR MODIFY was verified
at exec rather than parse: `create or replace` reports Created then Unchanged,
and `create or modify` over the stored layout reports Unchanged.

Sixteen Examples cells corrected against the doctype-tests listing, including
two that cited the wrong file -- Task Queues and Scheduled Events both pointed
at 21 (import/export mappings) and the mapping rows pointed at 06 (the REST
client).

Rule counts are measured, not remembered: 19 built-in (`lint --list-rules`
with no project rules dir) and 31 Starlark (`.claude/lint-rules/*.star`). Four
different wrong pairs were in circulation across five live docs, one claiming
41 built-in rules. CHANGELOG and the dated eval proposal are left alone: they
are records of a past state, not claims about the present.

The guard could not have caught any of it. TestCapabilityDocsDoNotClaim...
reads only the "Not Yet Implemented" section and checks a hand-maintained list
of seven capabilities naming neither rules, message definitions nor layouts.
So the list is hoisted to `shippedCapabilities`, extended with all three, and a
second test asserts that "Missing Syntax Topics" may not list a capability
whose `mxcli syntax` topic resolves -- a flat contradiction, which is what
makes it mechanically assertable. It is deliberately not extended to the
Skills and Examples gap lists, where an entry can be true at the same time as
a syntax topic exists (Regular Expressions has a topic and no skill).

Checked by reinstating all three false claims: two tests fail, naming each.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From the review of mendixlabs#1181. A column added to createTables
without bumping CatalogSchemaVersion: the version guard only drops tables when
the version CHANGES, and CREATE TABLE IF NOT EXISTS never adds a column, so
every user with a cached catalog keeps a table the new SELECT cannot read.

Measured: activities_for yielded 302 activities on main and 0 on the branch
against the same cache file, with `no such column: UseRequestTimeout` and
`mxcli lint` exiting 1. `mxcli report` runs the same rules through the same
LintContext and never calls QueryErrors(), so there the same stale cache
scores the project silently.

The canonical fix carries the reproduction, because the obvious attempt does
not reproduce it: the cache has to actually be REUSED. Building it with one
`-p` spelling and running the new binary with another invalidates on "MPR path
changed" and rebuilds, and a default lint run builds a fast catalog with zero
activities — either way the run is green and the bug invisible. Confirm the
output says "Loading cached catalog … (from cache)" first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…trix-audit

docs(matrix): audit the feature matrix against what actually ships
…gs-row-33

docs(review): record the unbumped catalog-schema-version pattern
…mber-rights

feat(lint): expose the access rule's default member access rights
Resolve the only conflict, CHANGELOG.md [Unreleased]: keep this fork's
mendixlabs#750 Changed/Fixed entries and add upstream's mendixlabs#1187 while-loop MDL042
entry to the Fixed list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QDNtypktPBXPyyTChWGKrb
@ako
ako merged commit 8325731 into main Sep 25, 2026
17 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.

3 participants