Skip to content

feat(mdl): USE BUILDING BLOCK v1 — instantiate an Atlas block onto a page - #19

Merged
ako merged 3 commits into
mainfrom
feature/use-building-block
Jul 24, 2026
Merged

feat(mdl): USE BUILDING BLOCK v1 — instantiate an Atlas block onto a page#19
ako merged 3 commits into
mainfrom
feature/use-building-block

Conversation

@ako

@ako ako commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Implements v1 of USE BUILDING BLOCK — the INSTANTIATE capability from the Building-Blocks proposal, and the biggest lever for the atlas-design skill. It turns the skill's "discover → inspect → mirror" workflow into "discover → use".

Also adds the USE BUILDING BLOCK follow-up slice to show-describe-building-blocks.md (and marks READ as shipped).

What it does

Deep-copies an out-of-the-box Atlas building block onto a page in one line, instead of hand-copying its DESCRIBE output:

use building block Atlas_Web_Content.Card
use building block Atlas_Web_Content.Card as cust_   -- prefix the copied widget names

It's a page-body element (valid anywhere use fragment is), sourced from a persisted Pages$BuildingBlock. v1 = deep-copy only (+ optional as <prefix>); you configure the copied widgets afterwards with the shipped alter page commands. (Inline configuration is the v1.1 slice in the proposal — never implicit slots.)

How (reuses the fragment machinery)

  • Grammar: a useBuildingBlockRef rule (USE BUILDING BLOCK qualifiedName (AS ident)?), wired everywhere useFragmentRef is a page-body alternative. (BUILDING/BLOCK tokens already existed from READ.)
  • Visitor: emits a USE_BUILDING_BLOCK sentinel WidgetV3, mirroring buildUseFragmentRef.
  • Executor: expanded in the same expandIfFragment hook. The one hard part — turning the block's persisted BSON widgets into []*ast.WidgetV3 — is solved by round-tripping through the DESCRIBE renderer (render → define fragment __tmp wrapper → visitor.Build.Widgets), so it reuses the entire existing widget parser and degrades to a clear parse error rather than a fragile hand-written converter. Prefix rename reuses prefixWidgetNames.

Verification

  • Real end-to-end against Atlas_Web_Content.Card on a live Mendix 11.12.1 project: use building block Atlas_Web_Content.Card as cust_ → the container + dynamictext deep-copy with the cust_ prefix, preserving the card-title class and the 'Card style' design property.
  • Unit tests (mock backend): prefix / no-prefix / not-found / parse.
  • make grammar + go build ./... + go vet + gofmt clean; mdl/executor + mdl/visitor suites green.

Notes / follow-ups

  • Engine: works on MXCLI_ENGINE=legacy today. modelsdk support arrives with the ListBuildingBlocks impl in fix(building-blocks): modelsdk ListBuildingBlocks + exec-safe doctype example #17 (same method both call) — no extra work here.
  • Compound design properties (e.g. Spacing: ['margin-bottom': 'L']) currently drop — verified to be a pre-existing create-page BSON-writer gap (they drop on direct authoring too, and DESCRIBE/expansion both handle them faithfully), so it's out of scope for this PR. Simple toggle properties (Card style: on) round-trip fine.
  • No doctype exec-example added: it would need a real Atlas project + legacy engine, so unit tests + the real-project run cover it (same reasoning as the READ example).

🤖 Generated with Claude Code

https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4


Generated by Claude Code

claude added 2 commits July 24, 2026 15:55
Mark the Building-Blocks proposal partial — READ (SHOW/DESCRIBE +
CATALOG.building_blocks) shipped in PR #16/#17 and is validated live against a
real Atlas app — and add the USE BUILDING BLOCK instantiate slice:

- Syntax mirroring `use fragment` (`use building block Mod.Name [as prefix_]`),
  a page-body element sourced from a persisted Pages$BuildingBlock.
- Deep-copy / no-live-link / prefix semantics.
- The configuration decision: afterwards via `alter page` (v1), an optional
  inline override block (v1.1), a possible `over database …` data-context
  shortcut — and explicitly NOT implicit slot inference.
- v1 implementation approach: reuse the fragment-expansion hook; convert the
  block's BSON widgets to []*ast.WidgetV3 by round-tripping through the DESCRIBE
  renderer + visitor.Build (verified re-parseable), not a hand-written converter.

Also corrects the doc's stale storage-type (Pages$BuildingBlock) and refreshes
the proposals README index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Instantiate an out-of-the-box Atlas building block onto a page in one line,
instead of hand-mirroring its DESCRIBE output. Mirrors `use fragment`:

    use building block Atlas_Web_Content.Card
    use building block Atlas_Web_Content.Card as cust_

It's a page-body element (valid anywhere `use fragment` is), sourced from a
persisted Pages$BuildingBlock rather than a script-scoped `define fragment`. v1
is deep-copy only (+ optional `as <prefix>` rename); configure the copied widgets
afterwards with `alter page` (inline config is the v1.1 slice in the proposal).

Wiring mirrors use fragment: a `useBuildingBlockRef` grammar rule → a
USE_BUILDING_BLOCK sentinel WidgetV3 in the visitor → expansion in the same
expandIfFragment hook. The block's BSON widgets are turned into []*ast.WidgetV3
by round-tripping through the DESCRIBE renderer (render → `define fragment`
wrapper → visitor.Build → .Widgets) rather than a hand-written BSON→AST
converter, so it reuses the full widget parser and degrades to a clear parse
error. The prefix rename reuses prefixWidgetNames.

Verified end-to-end against the real Atlas_Web_Content.Card on a live 11.12.1
project: the container + dynamictext deep-copy with the cust_ prefix, preserving
the card-title class and the 'Card style' design property. Unit tests
(mock-backend) cover prefix/no-prefix/not-found and the parse.

Notes: requires MXCLI_ENGINE=legacy until the modelsdk ListBuildingBlocks lands
(PR #17). Compound/nested design-property values (e.g. Spacing: [...]) are a
pre-existing create-page writer gap (they drop on direct authoring too), out of
scope here.

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 24, 2026
…rimary path

Now that USE BUILDING BLOCK v1 exists, the skill leads with the one-line
instantiation instead of the hand-mirror workaround:

- Workflow renamed discover → inspect → USE (was → mirror). Step 3 is now
  `use building block Mod.Name [as prefix_]` (deep-copy), step 4 configures the
  copied widgets with `alter page`. Mirroring is reframed as the explicit fallback
  (hand-tuning, or the modelsdk engine before its building-block support lands).
- Capability table: Instantiate → ✅ v1 (deep-copy; configure afterwards; legacy
  engine today). Capability note updated to "discovery AND instantiation both work".
- Validation checklist updated to prefer `use building block` + `alter page`.

All `use building block` / `alter page` examples are bare page-body lines, which
the skill-MDL checker auto-skips (first keyword not in its create/alter/drop
entity|page checkable set) — verified with a binary built WITHOUT the grammar, so
this PR's CI is green even before the USE BUILDING BLOCK feature (#19) merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit 81f301a 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
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