feat: real-time MPK widget derivation — CREATE PAGE works without widget init#542
feat: real-time MPK widget derivation — CREATE PAGE works without widget init#542engalar wants to merge 18 commits intomendixlabs:mainfrom
Conversation
Spec for runtime WidgetTemplate generation from .mpk files, enabling any pluggable widget in project/widgets/ to be used in MDL commands without pre-built embedded templates. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
4-task plan covering multi-widget MPK support, GenerateFromMPK, loader.go fallback wiring, and end-to-end verification. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Replace getWidgetIDFromMPK (reads only the first WidgetFiles entry) with getWidgetIDsFromMPK which iterates all entries, so MPKs that bundle multiple widgets (e.g. CrusherWidgets.mpk) are fully indexed. Add ParseMPKForWidget(mpkPath, widgetID) with a compound cache key so callers can request any individual widget from a multi-widget MPK without re-parsing the archive. Guard against oversized package.xml with the same maxTotalSize cap used in ParseMPK. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add GenerateFromMPK in sdk/widgets/generate.go: given a parsed WidgetDefinition from an MPK file, build a complete WidgetTemplate with the correct BSON structure (CustomWidgets$CustomWidgetType + WidgetObjectType + WidgetObject). TypePointer on the WidgetObject references the ObjectType ID, not the CustomWidgetType ID. All $IDs use placeholder prefix so collectIDs can remap them to real UUIDs at serialisation time. Marks generated templates with Generated:true so the augment pass is skipped (the MPK is already the source of truth). Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add getOrGenerateTemplate (3-tier lookup): embedded JSON → session cache → MPK derivation. GetTemplateBSON and GetTemplateFullBSON now use this instead of GetTemplate, so any pluggable widget whose .mpk lives in the project's widgets/ directory works without a pre-built embedded template. Guard augmentFromMPK with !tmpl.Generated to avoid double-augmenting templates that were already built from MPK data. Test fixture at sdk/widgets/testdata/crushertestproject/ carries CrusherWidgets.mpk so the integration test runs without an external project. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…ed list Add ListAllCustomWidgetTypes() to sdk/mpr/reader_widgets.go: scans every page and snippet, collects all unique CustomWidgets$CustomWidgetType widget IDs, and returns one RawCustomWidgetType per unique ID. Rewrite cmd/mxcli/cmd_extract_templates.go to call ListAllCustomWidgetTypes so it discovers and extracts every pluggable widget in the project without a predefined list. Project-specific widgets no longer require code changes to be extractable. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add ParseAll(mpkPath) to the mpk package: returns all WidgetDefinitions from an MPK (single-widget and multi-widget alike) by iterating every WidgetFiles entry and calling ParseMPKForWidget per ID. Update runWidgetInit and generateWidgetDocs in cmd_widget.go to call ParseAll instead of ParseMPK so that MPKs bundling multiple widgets (e.g. CrusherWidgets.mpk with 5 widgets) generate a .def.json and docs entry for each bundled widget, not just the first one. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…s unspecified BSON comparison between a Studio Pro page (AIPlayground) and an MDL-generated page (AIPlayground_MPKTest) showed that Weight=1 (one grid unit) was being written when no DesktopWidth was specified. The correct default is auto-fill (-1 in BSON), which the serializer already maps from Weight=0 via columnWeight(). Changing the initial Weight from 1 to 0 so that omitted DesktopWidth columns serialise as Weight=-1, matching Studio Pro's default new-column behaviour. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… AI hints
Pluggable widget properties with DataSourceProperty linkage (e.g.
gridCss/gridThrow/gridTph linked to gridData) now surface that linkage
through the full pipeline:
- sdk/widgets/loader.go: extract DataSourceProperty from def.json
ValueType objects via jsonValueToBSONWithNestedObjectType; stored in
PropertyTypeIDEntry.DataSourceProperty
- sdk/pages/pages_widgets_advanced.go: add DataSourceProperty field to
PropertyTypeIDEntry (parallel struct used by the backend layer)
- mdl/backend/mpr/widget_builder.go: copy DataSourceProperty in
convertPropertyTypeIDs so the executor sees it
- mdl/executor/widget_engine.go: two new AI-facing HINT log messages:
(1) when a DataSource-type property is not configured via DataSource:,
lists all linked attribute properties that need the datasource entity;
(2) when a linked attribute references the wrong entity instead of the
datasource entity.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… templates Two root causes for CE0463/CE0572 on MPK-derived pluggable widgets: 1. Missing CustomWidgetType metadata fields (CE0572 + CE0463) GenerateFromMPK emitted only $ID/$Type/WidgetId/ObjectType. Missing: WidgetPluginWidget (false→CE0572), WidgetName, OfflineCapable, SupportedPlatform, WidgetNeedsEntityContext, WidgetDescription, HelpUrl, StudioCategory, StudioProCategory. Fix: parse these attributes from the <widget> XML element and <description> child element; store on WidgetDefinition; emit in GenerateFromMPK's typeMap. 2. Empty AllowedTypes on attribute properties (CE0463) Studio Pro compares stored AllowedTypes against the installed widget's <attributeTypes> list. Our generated type had [1] (empty); the correct value is [1, "String"] / [1, "Decimal"] etc. Fix: parse <attributeTypes><attributeType name="..."/> in xmlProperty and collectNestedProperties; store as AllowedTypes []string on PropertyDef; emit in createDefaultValueType's AllowedTypes array. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Allows configuring widget action-type properties (e.g. onChange, onCellClick, onPredict) using nanoflow/microflow references directly in the pluggable widget property block: onChange: nanoflow Module.NF_OnChange onCellClick: nanoflow Module.NF_HeatmapClick Grammar: add two new widgetPropertyV3 alternatives — IDENTIFIER COLON actionExprV3 keyword COLON actionExprV3 so that named action properties parse using the full actionExprV3 rule (nanoflow, microflow, save, cancel, show page, etc.). Visitor: in parseWidgetPropertyV3, try ActionExprV3 first for IDENTIFIER and keyword alternatives, storing the result as *ast.ActionV3 under the property key. Widget engine: step 4.6 now handles *ast.ActionV3 propVal types — calls buildClientActionV3 and builder.SetAction(propName, act) for any property whose ValueType is "Action". Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
AI Code ReviewWhat Looks GoodThis PR successfully implements real-time MPK widget derivation so that Core Improvements
Code Quality
Test Coverage
RecommendationApprove - The PR is ready for merging. It addresses a significant workflow improvement (eliminating the mandatory
The implementation correctly prioritizes built-in → user override → MPK derivation, ensuring no accidental overrides of carefully crafted definitions. The demotion of Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Summary
WidgetRegistry:SetProjectDirpre-scanswidgets/*.mpkand maps derived MDL names → widget IDs.Get/GetByWidgetIDfall back to on-demand MPK parsing on cache miss, memoising results. Built-in and user-extracted definitions always take priority.pageBuilder:initPluggableEnginecallsregistry.SetProjectDir(filepath.Dir(backend.Path()))afterLoadUserDefinitions, so everyCREATE PAGEcommand automatically discovers project widgets — no manual extraction step needed.widget initdemoted to optional: adds--forceflag to overwrite existing.def.jsonfiles; updated help text clarifies the command is now an inspection/customization tool, not a prerequisite. Documents the built-in skip behaviour and the markdown docs side effect.WidgetTemplateis generated from MPK property definitions; unknown pluggable widgets fall back to MPK-derived templates; multi-widget MPKs handled inFindMPK/ParseMPKForWidget.custom-widgets.mddocuments the 4-tier registry, real-time discovery, and MDL name derivation rule so AI agents can reference project widgets without being told to runwidget initfirst.Test Plan
go test ./mdl/executor/ -run "TestRegistryMPK" -v— 4 new tests pass (Get, GetByWidgetID, cache identity, builtin precedence)go test ./mdl/executor/ -timeout 60s— full executor suite passes (pre-existing OData/Windows failures are unrelated)go build ./...— clean buildmxcli widget list -p app.mpr— built-in widgets unchanged (GALLERY, COMBOBOX, etc.)mxcli widget init --help— shows--forceflag and updated Long textCREATE PAGEagainst a project with a third-party widget inwidgets/— page created without priorwidget initCrusherCopilot.mpr) — page creation succeeds with MPK-derived widget🤖 Generated with Claude Code