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
3 changes: 2 additions & 1 deletion .claude/skills/fix-issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ to the symptom table below, so the next similar issue costs fewer reads.
| A microflow expression calls a function that doesn't exist (e.g. `randomInt(9)` — in some docs but not a Mendix built-in): parses, passes `mxcli check`, then fails the build with CE0117 "Error(s) in expression". Also: a Decimal-returning function (`random()`, `secondsBetween`, the duration `*Between` family) assigned to an Integer/Long variable fails CE0117, but `mxcli check` only caught bare `div` | (1) The func checker only validated arity for *known* functions; an unknown call name was ignored. (2) `checkNumericAssignment` used `SourceIsArithmeticDecimal`, which fires only on arithmetic roots, so a Decimal *function* result slipped through. Also the `*Between` duration funcs were mistyped Integer in `funcTable` | `mdl/exprcheck/unknown_funcs.go` (`UnknownFunctionCalls`, `SourceRejectedForIntegerTarget`, `nearestFunc`) + `mdl/exprcheck/func_checker.go` (`funcTable` between-date return kinds) + `mdl/executor/validate_microflow.go` (`checkExprFunctions` → MDL044; `checkNumericAssignment` now uses `SourceRejectedForIntegerTarget`) | Walk each expression for `CallExpr` names not in `funcTable` (which lists *every* built-in — a bare `name(...)` is always a built-in call) → **MDL044** with a Levenshtein/prefix "did you mean" hint. Extend the Decimal-into-Integer check to Decimal-returning non-rounding functions → **MDL041**. Correct `secondsBetween`/`minutesBetween`/`hoursBetween`/`daysBetween`/`weeksBetween` to Decimal (calendar variants stay Integer, millisecondsBetween stays Long). When adding a new Mendix built-in, add it to `funcTable` or MDL044 will false-positive. Bug-tests `f1-unknown-expression-function.fail.mdl`, `f2-decimal-func-into-integer.fail.mdl`. Findings #1, #2 |
| `index name on (cols)` inside `create entity` (or `alter entity add index name on (cols)`) fails with `extraneous input 'on' expecting '('` — the SQL-like form docs/users expect. The bare `index name (cols)` worked | `indexDefinition` had no `ON` token: `IDENTIFIER? LPAREN indexAttributeList RPAREN` | `mdl/grammar/domains/MDLDomainModel.g4` (`indexDefinition`) | Make `ON` optional: `IDENTIFIER? ON? LPAREN indexAttributeList RPAREN`, regen grammar. `buildIndex` reads columns from `IndexAttributeList` only, so ON can't be mistaken for a column and no visitor change is needed. Covers both CREATE (entityOption) and ALTER ADD INDEX (shared rule). Bug-test `mdl-examples/bug-tests/f4-entity-index-on.mdl`. Findings #4 |
| `retrieve … where [Seq = $Game/MoveSeq + 1]` fails with a bare `mismatched input '+' expecting ']'` — no hint that Mendix XPath can't compute values (this is a Mendix limitation, not an mxcli bug) | Mendix XPath constraints take a literal/token/variable/path on the value side, never an arithmetic expression; the parse error named the token but not the cause | `mdl/visitor/visitor.go` (`enhanceErrorMessage`, `looksLikeXPathArithmetic`/`xpathArithmeticRe`) | Do NOT add grammar support (mxbuild would still reject the XPath). Add an error hint keyed on `mismatched input '<+|*|div|mod>' expecting ']'` (`expecting ']'` only occurs inside a `[…]` constraint) explaining the limitation and the workaround: compute into a variable first, then compare. Also documented in `xpath-constraints.md`. Bug-test `mdl-examples/bug-tests/f8-xpath-arithmetic.fail.mdl`. Findings #8 |
| Design properties are written free-form: a `ColorPicker`/`ToggleButtonGroup` value serializes as a plain option (wrong `$Type` for Studio Pro's Appearance tab), and a typo'd key/value (they're case-sensitive) passes `mxcli check`. Also `show design properties <widget>` reports "No design properties found for widget type container" for a valid widget | Root bug: `resolveDesignPropsKey` upper-cased the MDL keyword but the lookup map is **lowercase-keyed**, so `container`→`DivContainer` never resolved — leaving `resolveDesignPropertyValueType` dead code and the theme registry unused on the write/validate paths | `mdl/executor/theme_reader.go` (`resolveDesignPropsKey` case fix) + `mdl/executor/cmd_pages_builder_v3.go` (`astDesignPropToValue` takes theme props) + `mdl/executor/validate_design_properties.go` (new, MDL-WIDGET11/12) wired from `cmd/mxcli/cmd_check.go` + `cmd/mxcli/lsp_diagnostics.go` (cached `themeRegistry`) | Fix `resolveDesignPropsKey` to lower-case the lookup. On write, resolve each flat value's type from the registry (`ColorPicker`/`ToggleButtonGroup`→custom). On check (`-p` only, when themesource defines properties), walk page/snippet/alter-page widget trees and warn: **MDL-WIDGET11** unknown key (case-sensitivity hint / valid-key list), **MDL-WIDGET12** invalid value (lists allowed values). Warnings, not errors — a newer theme may add keys/values (forward-compat, per `page-styling-support.md:402`). Skip compound (registry doesn't model sub-props) and widgets with no type-specific metadata (pluggable). Bug-test `mdl-examples/bug-tests/typed-design-properties.mdl` |
| Design properties are written free-form: a `ColorPicker`/`ToggleButtonGroup` value serializes as a plain option (wrong `$Type` for Studio Pro's Appearance tab), and a typo'd key/value (they're case-sensitive) passes `mxcli check`. Also `show design properties <widget>` reports "No design properties found for widget type container" for a valid widget | Root bug: `resolveDesignPropsKey` upper-cased the MDL keyword but the lookup map is **lowercase-keyed**, so `container`→`DivContainer` never resolved — leaving `resolveDesignPropertyValueType` dead code and the theme registry unused on the write/validate paths | `mdl/executor/theme_reader.go` (`resolveDesignPropsKey` case fix) + `mdl/executor/cmd_pages_builder_v3.go` (`astDesignPropToValue` takes theme props) + `mdl/executor/validate_design_properties.go` (new, MDL-WIDGET11/12) wired from `cmd/mxcli/cmd_check.go` + `cmd/mxcli/lsp_diagnostics.go` (cached `themeRegistry`) | Fix `resolveDesignPropsKey` to lower-case the lookup. On write, resolve each flat value's type from the registry **by matching the value against the property's declared options** (see the CE6084 correction below — the control type alone does NOT decide it). On check (`-p` only, when themesource defines properties), walk page/snippet/alter-page widget trees and warn: **MDL-WIDGET11** unknown key (case-sensitivity hint / valid-key list), **MDL-WIDGET12** invalid value (lists allowed values). Warnings, not errors — a newer theme may add keys/values (forward-compat, per `page-styling-support.md:402`). Skip compound (registry doesn't model sub-props) and widgets with no type-specific metadata (pluggable). Bug-test `mdl-examples/bug-tests/typed-design-properties.mdl` |
| Follow-up regression from the row above: after typed design properties merged, `mx check` fails **CE6084** "Expected design property _Flex container_ / _Column gap_ / _Align items Y_ … to be of type **Toggle button group**, but found **Custom**" on any page using a flat `ToggleButtonGroup` value (Atlas flex/spacing/typography, e.g. `'Column gap': 'Medium'`). Broke `TestMxCheck_DoctypeScripts` on `12-styling`, `15c-fragment-bindings`, `31-pluggable-datagrid-gallery-v010` (both engines) — green on unit tests, red only in `make test-integration` | `resolveDesignPropertyValueType` mapped `ToggleButtonGroup`→`custom` by control type. But a ToggleButtonGroup selection picks one of a **fixed option set**, so Studio Pro stores it as an **Option** — a `Custom` value type mismatches the declaration. Only a ColorPicker's **off-list** value (a free-form hex) is genuinely Custom. The value type is decided by the **value**, not the control | `mdl/executor/cmd_pages_builder_v3.go` (`resolveDesignPropertyValueType`, now takes the value and reuses `themeOptionAllowed`) | Make it value-aware: value ∈ declared options → `option` (Dropdown, ToggleButtonGroup, predefined ColorPicker swatch alike); off-list **and** `ColorPicker` → `custom`; else `option`; no metadata → `option`. Verified: the three doctype examples pass `mx check` = 0 errors on both engines. Test `TestAstDesignPropToValue_Typed` extended with the `Column gap: Medium` + ColorPicker swatch/hex cases. **Diagnosis pattern**: a value-type/BSON-`$Type` mapping keyed on a *declared control type* is a trap — verify it against `mx check`, never assert it from the type name alone (this is exactly how the original bug slipped in). **Process lesson**: this shipped red because `make test-integration` (mx-check doctype roundtrips) was not run before merge — run it, not just unit tests, for any page/widget-serialization change |
| `mxcli run --local`: when a page action throws, the browser shows the generic Mendix error dialog and there is nothing to correlate it against — the runtime's own stdout/stderr (server stack trace, microflow `LOG` output) is swallowed, so a server-side bug can't be told apart from a client one | The runtime JVM was spawned with `cmd.Stdout=log; cmd.Stderr=log` where `log` is an in-memory `syncBuffer` surfaced only on a *startup* failure; during normal operation it goes nowhere on disk | `cmd/mxcli/docker/localboot.go` (`spawnAndConfigure`, `openRuntimeLog`, `LocalRuntime.logFile`, `LocalRuntimeOptions.RuntimeLogPath`) + `cmd/mxcli/docker/runlocal.go` (default `<projectDir>/.mxcli/runtime.log`) + `cmd/mxcli/cmd_run.go` (`--runtime-log`) | Tee the JVM's stdout+stderr to `<projectDir>/.mxcli/runtime.log` via `io.MultiWriter(log, file)` (the in-memory buffer still backs startup-error reporting). Append across restarts with a `=== runtime start … ===` marker; close the handle on Stop/reopen. Default on; `--runtime-log <path>` relocates, `-` disables. Print the path at boot. Test `TestOpenRuntimeLog`. Findings #25 |
| Follow-up to the above (#25 re-test): `run --local` writes `runtime.log` but it stays **nearly empty** — the JVM tee captures startup/JVM output only; **application** logs (microflow `LOG`, server-side exception stack traces) never reach stdout, so a page-action error still can't be diagnosed | A standalone runtime (launched via `runtimelauncher.jar`) attaches **no log subscriber** by default — unlike a Studio Pro / m2ee run, which calls `create_log_subscriber` **after** start. Mendix application logs flow to log *subscribers*, not stdout, so with none attached they go nowhere | `cmd/mxcli/docker/runtime_controller.go` (`RuntimeController.LogSubscriberFile`/`Stdout`, `attachFileLogSubscriber`, called at the end of `Start`) + `cmd/mxcli/docker/localboot.go` (`StartLocalRuntime` sets `ctrl.LogSubscriberFile` to the abs runtime-log path) | After a successful `start` (and on every restart's `Start`, since each fresh JVM has no subscriber), call the `create_log_subscriber` admin action with `{type:"file", name:"mxcli-run-local", autosubscribe:"INFO", filename:<abs runtime.log>, max_size:1GiB, max_rotate:0}`. **`max_rotate:0` is load-bearing**: the JVM stdout tee holds an fd on the same file, and a rotate-rename would detach it. Best-effort (a logging failure must not fail an up runtime — warn to Stdout instead). Pass an **absolute** path (the runtime's cwd is `<install>/runtime`, not mxcli's). Tests `TestStart_AttachesLogSubscriber`, `TestStart_NoLogSubscriberWhenUnset`, `TestStart_LogSubscriberFailureNonFatal`. Findings #25 (round 2) |

Expand Down
39 changes: 26 additions & 13 deletions mdl/executor/cmd_pages_builder_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,27 +600,40 @@ func astDesignPropToValue(p ast.DesignPropertyEntryV3, themeProps []ThemePropert
default:
return pages.DesignPropertyValue{
Key: p.Key,
ValueType: resolveDesignPropertyValueType(p.Key, themeProps),
ValueType: resolveDesignPropertyValueType(p.Key, p.Value, themeProps),
Option: p.Value,
}, true
}
}

// resolveDesignPropertyValueType determines the correct ValueType for a design property
// based on the theme definition. ToggleButtonGroup and ColorPicker use "custom" type;
// Dropdown uses "option" type. Falls back to "option" if theme info is unavailable.
func resolveDesignPropertyValueType(key string, themeProps []ThemeProperty) string {
// resolveDesignPropertyValueType determines the BSON ValueType for a flat design
// property value from the theme definition.
//
// The control type alone does NOT decide the value type: a value that is one of
// the property's declared options is always stored as an "option", whether the
// control is a Dropdown, a ToggleButtonGroup, or a ColorPicker's predefined
// swatches. Studio Pro rejects a mismatch (CE6084 "Expected design property … to
// be of type Toggle button group, but found Custom"), so a ToggleButtonGroup
// selection like 'Column gap': 'Medium' must serialize as an option, not custom.
//
// Only a value that is NOT a declared option, on a ColorPicker, is a free-form
// value (a custom hex/color) stored as "custom". Any other off-list value stays
// "option" (an invalid value is reported separately by MDL-WIDGET12). Falls back
// to "option" when no theme metadata is available (backward compatible).
func resolveDesignPropertyValueType(key, value string, themeProps []ThemeProperty) string {
for _, tp := range themeProps {
if tp.Name == key {
switch tp.Type {
case "ToggleButtonGroup", "ColorPicker":
return "custom"
default:
return "option"
}
if tp.Name != key {
continue
}
if themeOptionAllowed(tp.Options, value) {
return "option"
}
if tp.Type == "ColorPicker" {
return "custom"
}
return "option"
}
// No theme info available — default to "option" (Dropdown)
// No theme info available — default to "option".
return "option"
}

Expand Down
23 changes: 15 additions & 8 deletions mdl/executor/validate_design_properties_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,28 @@ func testThemeRegistry() *ThemeRegistry {
}

// TestAstDesignPropToValue_Typed verifies the write path picks the BSON value
// type from the theme registry (ColorPicker/ToggleButtonGroup → custom) rather
// than always defaulting flat values to option. Typed design properties.
// type from the theme registry: a value that is one of the property's declared
// options is stored as "option" for every control type (Dropdown,
// ToggleButtonGroup, ColorPicker swatch); only a ColorPicker's off-list value is
// "custom". A ToggleButtonGroup selection must NOT become custom or Studio Pro
// fails CE6084 ("Expected … Toggle button group, but found Custom"). Typed design
// properties.
func TestAstDesignPropToValue_Typed(t *testing.T) {
props := []ThemeProperty{
{Name: "Background color", Type: "Dropdown"},
{Name: "Text alignment", Type: "ToggleButtonGroup"},
{Name: "Accent", Type: "ColorPicker"},
{Name: "Background color", Type: "Dropdown", Options: []ThemeOption{{Name: "Brand Primary"}, {Name: "Default"}}},
{Name: "Text alignment", Type: "ToggleButtonGroup", Options: []ThemeOption{{Name: "Left"}, {Name: "Center"}, {Name: "Right"}}},
{Name: "Column gap", Type: "ToggleButtonGroup", Options: []ThemeOption{{Name: "Small"}, {Name: "Medium"}, {Name: "Large"}}},
{Name: "Accent", Type: "ColorPicker", Options: []ThemeOption{{Name: "Brand Primary"}}},
}
cases := []struct {
key, val, wantType string
}{
{"Background color", "Brand Primary", "option"},
{"Text alignment", "Center", "custom"},
{"Accent", "#ff0000", "custom"},
{"Unknown Key", "x", "option"}, // not in registry → default option
{"Text alignment", "Center", "option"}, // ToggleButtonGroup option → option (not custom!)
{"Column gap", "Medium", "option"}, // the CE6084 regression case
{"Accent", "Brand Primary", "option"}, // ColorPicker predefined swatch → option
{"Accent", "#ff0000", "custom"}, // ColorPicker free-form color → custom
{"Unknown Key", "x", "option"}, // not in registry → default option
}
for _, c := range cases {
dp, ok := astDesignPropToValue(ast.DesignPropertyEntryV3{Key: c.key, Value: c.val}, props)
Expand Down
Loading