Skip to content

fix(building-blocks): modelsdk ListBuildingBlocks + exec-safe doctype example - #17

Merged
ako merged 1 commit into
mainfrom
fix/building-blocks-modelsdk-and-example
Jul 24, 2026
Merged

fix(building-blocks): modelsdk ListBuildingBlocks + exec-safe doctype example#17
ako merged 1 commit into
mainfrom
fix/building-blocks-modelsdk-and-example

Conversation

@ako

@ako ako commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Fixes the CI failure after PR #16 merged: TestMxCheck_DoctypeScripts/36-building-block-examples.mdl failed on both engines. mx check passed (0 errors) — it was the exec step in the doctype integration harness.

Two root causes, both fixed

1. modelsdk: ListBuildingBlocks not implemented yet
The modelsdk backend's ListBuildingBlocks was an unimplemented stub, so show building blocks errored on the codec engine. Implemented it on *Backendmprread.ListUnitsWithContainer[*genPg.BuildingBlock] + scalar metadata (Name, Documentation, DisplayName, Platform, TemplateCategory) — mirroring ListSnippets. The widget tree still comes from the shared GetRawUnit, so read-only DESCRIBE needs nothing more. Both engines now list building blocks.

2. legacy (and modelsdk): building block not found: MyModule.LoginForm
The example described a specific block that doesn't exist in a freshly-created test project — and DESCRIBE of a missing block correctly errors. Made the example exec-safe: it keeps the show building blocks statements (empty result, no error) and shows DESCRIBE BUILDING BLOCK … + the select … from CATALOG.building_blocks query illustratively in comments (they need a project that actually contains building blocks).

Verification

  • go build ./..., gofmt, and go vet clean.
  • mdl/backend/modelsdk + mdl/executor suites green; example passes mxcli check.
  • The two exec paths the harness runs (show building blocks on legacy and modelsdk) are now both error-free; the failing describe-of-a-missing-block is gone.

Follow-up to #16 (the merged building-blocks READ feature).

🤖 Generated with Claude Code

https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4


Generated by Claude Code

The doctype integration harness (TestMxCheck_DoctypeScripts) execs each example
on both engines, and 36-building-block-examples.mdl failed two ways:

- modelsdk: `show building blocks` errored because the modelsdk backend's
  ListBuildingBlocks was an unimplemented stub. Implement it on *Backend
  (mprread over genPg.BuildingBlock + scalar metadata), mirroring ListSnippets,
  so SHOW/DESCRIBE BUILDING BLOCK work on the codec engine too. The widget tree
  still comes from the shared GetRawUnit, so read-only describe needs no more.
- legacy (and modelsdk): the example `describe building block MyModule.LoginForm`
  errored "building block not found" — a fresh test project ships no building
  blocks, and DESCRIBE of a missing block correctly errors. Make the example
  exec-safe: keep the `show building blocks` statements (empty, no error) and
  show DESCRIBE + the CATALOG.building_blocks query illustratively in comments.

Follow-up to the merged building-blocks feature (PR #16).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit 7dbb171 into main Jul 24, 2026
3 checks passed
ako pushed a commit that referenced this pull request Jul 28, 2026
Ledger findings #17–19: a `set $x = <expr>` value re-serialized from the
AST silently corrupted the stored Mendix expression — `mxcli check`
passed, `mx check` gave only a generic CE0117:

- #17 a division's right operand lost its `$` (`$a / $b` → `$a/b`)
- #18 a decimal literal lost its fraction (`2.0` → `2`), breaking Decimal
- #19 a small decimal became scientific notation (`0.000001` → `1e-06`)

shouldPreserveExpressionSource now keeps the raw source whenever the
expression contains a `/` or a decimal literal (a `.` adjacent to a
digit), mirroring the existing XPath-where handling — so the exact text
survives instead of a lossy AST round-trip. (MDL division is `div`; `/`
is the member-access separator, which is what caused the `$`-loss
mis-parse — preserving source is the robust fix either way.)

Test: TestShouldPreserveExpressionSource_DivisionAndDecimals; repro
mdl-examples/bug-tests/ledger-17-19-expression-serialization.mdl.
Symptom table updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 28, 2026
…ecimals

Corrects the earlier ledger #17-19 fix, which over-broadly preserved raw
expression source whenever it saw a '/'. In MDL '/' is the member-access
separator (`$obj/Attr`), not division (that is `div`), so preserving on '/'
source-froze every association-navigation path and regressed
TestAssociationNavParsing.

- shouldPreserveExpressionSource: drop the '/' trigger; keep the decimal-literal
  trigger (findings #18/#19 — `2.0`->`2`, `0.000001`->`1e-06` are genuine
  AST-serializer losses that raw-source preservation fixes).
- New MDL045: walk the microflow expression tree for a BinaryExpr with operator
  '/' (`$Dec / 2`, `(...) / $x`) and reject with an actionable "use div" message,
  instead of silently writing an invalid expression that fails the build (CE0117).
  The bare `$a / $b` form degrades to a member path and is caught by
  `check --references`.

Tests: TestShouldPreserveExpressionSource_Decimals (renamed),
TestValidateMicroflow_SlashDivision. Repros: ledger-17-19 (passes) +
ledger-17-slash-division.fail.mdl (MDL045). Skill + symptom-table updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 28, 2026
Re-test of PR #52 by the ledger project surfaced three gaps:

- #17 (MDL045): the variable/variable division form `$Dec / $Dec2` was not
  caught — it parses as a member-access path, so the BinaryExpr walk missed it.
  The visitor now narrowly preserves source when a `/` is immediately followed by
  `$` (a real association path never has `$` after `/`, so legit navigation is
  untouched), and MDL045 flags a source-preserved AttributePathExpr whose `/ $`
  source matches (`exprIsSlashDollarDivision`). The `$` guard on the RHS is the
  reliable division-vs-navigation discriminator the tester suggested.

- #25 (MDL047): the check only saw microflow `retrieve` constraints, so an
  association `= empty` in a page/widget datasource where-clause slipped through.
  Now `validateDatasourceXPathAssociationEmpty` inspects `DataSourceV3.Where` on
  every widget, sharing the detection via `xpathAssociationEmptyMatches`.

- #27 (MDL-WIDGET15): the advisory false-positived on heading+subtitle pairs.
  Only `Text`/unset RenderMode is inline; H1–H6/Paragraph are block-level and do
  not concatenate. `inlineDynamicText` now excludes block modes, so a heading
  breaks the run.

Tests updated (variable/variable division, page-datasource MDL047, heading
exclusion). Repros: ledger-17-slash-division.fail.mdl (+ var/var form),
new ledger-25-page-datasource-assoc-empty.fail.mdl. Symptom table refreshed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 29, 2026
MDL045 flagged `$a / $b` division only when it was the entire set-value
expression. When the division was embedded in a larger expression —
`$a / $b + 1`, `round($a / $b)`, `$a / $b * 100` — the `$a / $b` part
degrades to a member-path AttributePathExpr nested under a BinaryExpr or
FunctionCallExpr, and the check silently missed it (mxcli check passed but
mx check fails with CE0117). Only the bare form and a chained
`$a / $b / $c` were caught.

Root cause: exprIsSlashDollarDivision matched only when the source-preserved
SourceExpr *directly* wrapped an AttributePathExpr, so a division nested one
level down slipped through.

Fix: replace it with exprHasSlashDollarDivision, which walks the whole
expression tree for any SourceExpr and scans its raw source for a `/ $`
(slash before a variable) using a string-literal-aware scanner. A real
member/association path never writes `/$`, and the scanner skips `/$`
inside quoted literals (e.g. 'path/$x') to avoid false positives. The
division is now caught wherever it appears — in additions, multiplications,
function arguments, return values, and if-conditions.

Tests: TestValidateMicroflow_SlashDivision extended with embedded cases and
the string-literal guard; ledger-17-slash-division.fail.mdl exercises the
embedded forms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 29, 2026
…ute values (FINDINGS #17)

An aggregate (sum/count/average/…) or unknown function used inside a `create` or
`change` attribute value fails the build with CE0117, but `mxcli check` only
inspected return/if/declare/set expressions — the same MDL044 check never reached
attribute-assignment values, so `RowTotal = formatDecimal(sum($cells), '0.00')`
passed check and failed MxBuild with a message that didn't even mention `sum`.

Wired checkExprFunctions (MDL044) into the CreateObjectStmt and ChangeObjectStmt
cases of the microflow body walk. Verified: `sum()` in a create attribute now
reports MDL044 with the "assign the aggregate to a variable first" hint; legit
functions (formatDecimal/trim) pass; full executor suite green. Test cases added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 30, 2026
…ze (TraceOps #9/#10/#17)

Three page-builder defects found by the TraceOps project, each verified against
mxbuild 11.12.1 (`mx check` → 0 errors after the fix):

#9 — `dynamictext x (Content: '')` persisted as `Content: '{1}'` with no
parameters (an orphaned placeholder) → CE0720 "Place holder index 1 is greater
than 0". buildDynamicTextV3 unconditionally defaulted empty content to `{1}`.
Now empty content with no params is a literal empty template; `{1}` is only
synthesized when a parameter exists.

#10 — `dynamictext s (Content: '$318')` was treated as a variable reference and
became an unbound `{1}` param → CE0402/CE1613 ("attribute '$318' no longer
exists"). A `$` starts a variable ONLY when followed by a letter/underscore
(new isDynamicTextVariableRef / dynamicTextVariableRe); `$318` stays literal.

#17 — `listview (… PageSize: 200)` always paged at 20: the property parsed into
the AST but was ignored on write (buildListViewV3 hardcoded 20) and never read
or emitted by describe. Now the write honors GetIntProp("PageSize"), the
describe parse reads it, and the listview formatter emits a non-default value so
it round-trips.

Tests: TestBuildDynamicTextV3_{EmptyContent,DollarDigitLiteral,VariableStillBinds},
TestIsDynamicTextVariableRef, TestBuildListViewV3_PageSize. Repro
mdl-examples/bug-tests/traceops-9-10-17-dynamictext-listview.mdl. Symptom rows
added to fix-issue.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako added a commit that referenced this pull request Jul 30, 2026
fix: TraceOps findings — DYNAMICTEXT content, ListView PageSize (#9/#10/#17) + doc-comment & combobox-association follow-ups
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.

2 participants