Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/skills/fix-issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,4 @@ extracting `OffsetExpression`/`LimitExpression`.
| Someone translates the whole app into a language with `create translations`, every run reports success, `mx check` is clean — and the app shows not one translated string | The project had not ENABLED that language. Enabled languages (`Settings$LanguageSettings.Languages`) decide what the build emits; a translation for any other language is stored in the model, survives every check, and is discarded at build time. Measured on mxbuild 11.13.0: a caption translated into de_DE on a blank app (which enables en_US only) builds at 0 errors and the German string appears NOWHERE under `deployment/` — no `translations_de_DE.properties`, and "de_DE" does not occur in the built model at all; the English control appears in three files. A stock app invites the mistake: it enables ONE language while its marketplace modules ship translations in NINE, so "other languages already have translations here" is true and misleading | `mdl/executor/language_enablement.go` (`languageIsEnabled`, `unenabledLanguageWarning`), wired into `mdl/executor/cmd_translations.go` and surfaced by `mdl/executor/cmd_settings.go` (both `show` and `describe`) | Warn, do not refuse: Mendix tolerates the state at **both** layers — measured live over PED, Studio Pro loads the unenabled language's translation and keeps it beside the enabled one (`{"languageCode":"sv_SE","text":"Etikett"}` next to the en_US entry on a caption in an en_US-only project), reporting no error; only the BUILD drops it — and refusing would break the legitimate order of "translate now, enable when the review lands". **Unknown must not read as disabled** — with no settings, or a settings part carrying no `Languages`, a warning would be a guess, and a false warning about a language that IS enabled trains people to ignore the true one. DESCRIBE lists the enabled languages as a **comment**, because MDL cannot author the list (`alter settings LANGUAGE` carries `DefaultLanguageCode` only) and a statement that does not re-execute is worse than a comment that does not. The measurement that settles any doubt is `mxbuild --target=deploy` plus a same-page control string in the default language — `mx check` says nothing either way. Repro `mdl-examples/bug-tests/translations-unenabled-language.mdl` |
| **CE0463 on every page carrying a File Uploader**, whatever is on it — an action-free one with nothing but its `datasource:` fails identically. The widget the #956 action-slot work was *about*, and the one it never exercised | **Two independent causes, and each alone still fails.** (A) The widget XML's `defaultType` — the KIND of a default, `defaultType="CallNanoflow"` on the create* actions, `defaultType="Association"` on the datasources — was never parsed, so all six landed as `DefaultType: "None"`. It is part of the widget DEFINITION, exactly like the stale `onChange` of #716. (B) `uploadMode`'s declared default never reached the visibility evaluator, because a primitive mapping carries the XML default in `PropertyMapping.Value` while `widgetValueMap` read only `.Default` (populated for selections alone). With the condition unknown, `hiddenUnnamedProperties` correctly refuses to guess — so `associatedImages` was not pruned, the one `datasource:` clause fanned out into BOTH datasource properties, and the pruned one carried a value. File Uploader is the only widget in a stock 11.13.0 app that declares any `defaultType` (6 properties; every other bundled widget declares 0), which is why one widget was unwritable while the rest were fine | (A) `modelsdk/widgets/mpk/mpk.go` + `sdk/widgets/mpk/mpk.go` (`PropertyDef.DefaultType`, `xml:"defaultType,attr"`), `modelsdk/widgets/augment.go` + `sdk/widgets/augment.go` (`defaultTypeOf`); (B) `mdl/executor/validate_widgets.go` (`widgetValueMap`) | **`mx update-widgets` on a COPY is the oracle when there is no Studio Pro** — and when one IS reachable, `ped_check_errors` over the written documents is the real verdict, with the same page written by the pre-fix binary as the control (it reports the CE0463 text and a `/layoutCall/arguments/0/widgets/0/widgets/0` location; the fixed 17 documents report "No errors found."). `pg_read_page` then shows the editor's own view — on the pre-fix page it lists BOTH `associatedFiles` and `associatedImages` under `uploadMode: "files"`, which is the fanout, and on the fixed one only the active half. It is Studio Pro's own normalizer: run it, confirm it clears the error, then diff its output against yours — a 2,650-path document diff whose *only* differences are the cause. Diff by property NAME, never by index: the two dumps order top-level keys differently and a positional flatten reports ~60 spurious differences that hide the 6 real ones. Widget properties are keyed by `TypePointer` into `Type.ObjectType.PropertyTypes`, so resolve the pointer to its `PropertyKey` first or the diff is unreadable. Two more findings from the same measurement, both still open: mxcli writes widget-action `ParameterMappings` with list marker **3** where Studio Pro writes **2** (and omits the sibling `Variable`/`OutputMappings` keys) — accepted by both mxbuild 11.13 **and Studio Pro**, which resolves every mapping to the right parameter (checked live over PED), so it is a diff-churn item rather than a correctness one; and the visibility extractor misses a `hidePropertiesIn` call inside a **ternary branch** (`cond ? hide([…]) : hide([…])`), as opposed to the ternary *array element* #233 fixed — so `createImageAction` carries no uploadMode rule, MDL-WIDGET10 cannot warn, and wiring it in files mode is a CE0463 the script author gets no help with. Repro `mdl-examples/bug-tests/956-fileuploader-six-action-slots.mdl`. Issue #956 |
| Nothing visibly wrong: `create translations` writes a translation that builds at 0 errors and describes back correctly, but the `Texts$Translation` elements it minted carry `$ID`s of a shape Studio Pro never produces | `mdl/translations` minted its own id — 16 raw bytes from `crypto/rand` — instead of going through `types.GenerateID`/`types.UUIDToBlob` like every other element mxcli writes. Mendix stores an element id as a **.NET Guid**: mixed-endian, so the RFC-4122 version nibble lands at byte **7** and the variant bits at byte **8**. Raw bytes satisfy that 1 time in 64. Measured on a stock 11.13.0 app: **44,002 of 44,002** element ids are well-formed on that reading (version 4 or 5, variant `10b`), including all **1,650** translation ids the marketplace modules ship; after `mxcli exec translations.mdl`, **1 of 27** written ids was. `mx check` is silent — a Guid parses any 16 bytes — and so is DESCRIBE, because the id is never shown | `mdl/translations/text.go` (`newElementID`), against `mdl/types/id.go` (`GenerateIDErr`, `UUIDToBlob` — which is where the byte swap lives) | Mint through `types.GenerateIDErr` + `types.UUIDToBlob`. Assert the **form**, not the presence: draw ~200 ids and require version nibble 4 at byte 7 and variant `10b` at byte 8, so a raw-random implementation cannot pass by luck (`TestNewElementID_IsAUUIDInMendixByteOrder`). Note the two measurement traps: decoding with `bson.M` (or any Go map) **loses key order**, so "is `$ID` first" cannot be asked that way — use `bson.D`/`bson.Raw`, or Python, whose dicts preserve insertion order; and read the version nibble at byte **7**, not 6 — at byte 6 Mendix's own ids look like uniform noise, which reads as "Mendix does not care" and inverts the conclusion. The paired control is the whole project: 100% conforming before the run and after the fix, 3.7% for the new ids in between. Repro `mdl-examples/bug-tests/translation-id-form.mdl` |
| `mxcli run --ensure-db` fails to start PostgreSQL when no service manager becomes ready (e.g. Arch): `exec: "pg_ctlcluster": executable file not found in $PATH`, though `initdb`/`pg_ctl`/`psql` are present | `startLocalPostgres` only knew the `service`/`pg_ctlcluster` helpers (the latter a placeholder that could never run), and role/database provisioning assumed `sudo -u postgres`, which a user-owned cluster does not need | `cmd/mxcli/docker/ensuredb.go` (`startLocalPostgres`, `startUserCluster`, `resolveSuperuser`) | Fall back to a user-owned `initdb`/`pg_ctl` cluster under `~/.mxcli/postgres`, but never initialize a competitor when the requested TCP port is already owned by a service-started server that is still recovering. Enforce the passwordless-superuser boundary instead of assuming it: explicitly tighten an existing socket directory to `0700`, set the socket itself to `0700`, and persist `listen_addresses`/`port`/`unix_socket_directories` in `postgresql.conf` so a plain later `pg_ctl start` stays safe. Refuse, with a cleanup path, a cluster made by the earlier development revision if its host authentication is still `trust`. Reuse a running cluster only when both the port and socket directory in `postmaster.pid` match. Keep `sudo -u postgres` for system clusters, but make both sudo and psql non-interactive, retain peer authentication over the system Unix socket at the exact requested port, and force SCRAM password storage for pre-14 defaults. Normalize the endpoint once and return it to the runtime; parse the bracketed form before any last-colon IPv6 compatibility fallback. Tests stub every external tool and exercise slow service startup, legacy-cluster refusal, permissions, persisted settings, endpoint propagation, sudo arguments, and repeated/running cases. Issue #823 |
12 changes: 9 additions & 3 deletions .claude/skills/mendix/run-local/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,14 @@ association catalog only at startup; behavioural changes are hot-reloaded.
- A **PostgreSQL** database (defaults: `127.0.0.1:5432`, user `mendix`, db derived
from the project name; override with `--db-host/--db-name/--db-user/--db-password`).
- **`--ensure-db`** provisions it for a fresh session: starts local Postgres if the
port is down and creates the role + database if missing (local superuser via
`sudo -u postgres`). Remote hosts are only checked, not provisioned.
port is down and creates the role + database if missing. It uses a service
manager, or a user-owned `initdb`/`pg_ctl` cluster under `~/.mxcli/postgres`
when no service becomes ready (e.g. Arch) — needing no `postgres` OS account or `sudo`.
Remote hosts are only checked, not provisioned.
The user-owned cluster persists across sessions; its server log is
`~/.mxcli/postgres/server.log`. Stop it with
`pg_ctl -D "$HOME/.mxcli/postgres/data" stop`. To remove it, stop it first and
then delete `~/.mxcli/postgres` (this permanently deletes its databases).
- Without `--ensure-db`, create it once and the command errors if it's unreachable:

```bash
Expand Down Expand Up @@ -273,7 +279,7 @@ export OTEL_TRACES_EXPORTER=otlp OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:43
mxcli run --local -p app.mpr --trace
```
| `--app-port` / `--admin-port` / `--serve-port` | 8080 / 8090 / 6543 | Ports |
| `--db-host` / `--db-name` / `--db-user` / `--db-password` | 127.0.0.1:5432 / derived / mendix / mendix | Database |
| `--db-host` / `--db-name` / `--db-user` / `--db-password` | 127.0.0.1:5432 / derived / mendix / mendix | Database; bracket IPv6 endpoints (`[::1]:5432`) |

## Pages render in the browser

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
The cause was the fix for the opposite report. #884 was a describe→exec round-trip *moving* a hand-placed start (`145;200` came back as `100;200`), fixed by carrying the stored position over on every rewrite — which then pinned the start of every rewritten flow. Both reports are real, and neither is answerable without asking where the stored value came from. A start sitting at the derived spot is mxcli's own arithmetic handed back, carries no intent, and is now re-derived so it follows the activities; a start anywhere else was placed by a person and still survives. `@start(x, y)` states the position outright and beats both, which is the other half of what #951 reported — before it there was no way to move a start once one had been preserved.

Neither `mx check` nor a successful build detects this, in either direction: the Mendix model carries no geometry rules, so a stranded start is a valid document that builds and runs and is merely drawn wrong — 0 errors on mxbuild 11.13.0 before and after.
- **`mxcli run --ensure-db` can start PostgreSQL without a working service manager (#823)** — on hosts that ship neither `service` nor Debian's `pg_ctlcluster` (e.g. Arch Linux), `--ensure-db` failed with `exec: "pg_ctlcluster": executable file not found in $PATH` even though the portable `initdb`/`pg_ctl`/`psql` tools were present. `startLocalPostgres` now falls back to a user-owned cluster under `~/.mxcli/postgres` when no service becomes ready, but never starts a competitor while another process owns the requested port. The cluster is idempotent and needs neither a `postgres` OS account nor passwordless `sudo`: its listen address, port, private socket directory, and `0700` socket permissions persist in PostgreSQL's own configuration; loopback TCP uses SCRAM while role/database provisioning uses local trust only through that private socket. Anyone who ran an earlier development revision of this fix must stop PostgreSQL, remove `~/.mxcli/postgres`, and rerun `--ensure-db`; reuse detects and refuses its insecure host-trust records. The retained system-cluster path is non-interactive, keeps `sudo -u postgres` on the peer-authenticated system Unix socket at the requested port, and creates a SCRAM password even on PostgreSQL versions whose default is MD5. Password-bearing SQL is sent over standard input instead of exposed in process arguments. The canonical endpoint—including bracketed IPv6—is also the one handed to the Mendix runtime.

- **A pluggable widget's click/change action survives a describe→exec round-trip** (#956) — the action landed in the `.mxunit` and built at 0 errors, then `describe page` omitted it, so re-executing mxcli's own output deleted the wiring. A widget's storage key is not MDL's name for the slot: Mendix's own widgets suffix theirs (BadgeButton `onClickEvent`, HeatMap `onClickAction`, Combobox `onChangeEvent`) and the writer has stripped that suffix since ledger #14, while the reader looked up the literal string `"onClick"`. It now resolves the stored key through the writer's own `actionSourceForKey`, so the two cannot drift apart again, and reads the change slot as well as the click slot — the generic pluggable path had no `OnChange` read at all, which silently dropped a Slider/RangeSlider/StarRating's only action.

Expand Down
2 changes: 1 addition & 1 deletion cmd/mxcli/cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func init() {
runCmd.Flags().Int("app-port", 0, "HTTP port for the app (default 8080)")
runCmd.Flags().Int("admin-port", 0, "M2EE admin API port (default 8090)")
runCmd.Flags().Int("serve-port", 0, "mxbuild --serve port (default 6543)")
runCmd.Flags().String("db-host", "", "Database host:port (default 127.0.0.1:5432)")
runCmd.Flags().String("db-host", "", "Database host:port (IPv6: [::1]:5432; default 127.0.0.1:5432)")
runCmd.Flags().String("db-name", "", "Database name (default derived from the project name)")
runCmd.Flags().String("db-user", "", "Database user (default mendix)")
runCmd.Flags().String("db-password", "", "Database password (default mendix)")
Expand Down
Loading
Loading