Skip to content

feat: real-time MPK widget derivation — CREATE PAGE works without widget init#542

Open
engalar wants to merge 18 commits intomendixlabs:mainfrom
engalar:feature/mpk-template-derivation
Open

feat: real-time MPK widget derivation — CREATE PAGE works without widget init#542
engalar wants to merge 18 commits intomendixlabs:mainfrom
engalar:feature/mpk-template-derivation

Conversation

@engalar
Copy link
Copy Markdown
Contributor

@engalar engalar commented May 9, 2026

Summary

  • Real-time MPK fallback in WidgetRegistry: SetProjectDir pre-scans widgets/*.mpk and maps derived MDL names → widget IDs. Get/GetByWidgetID fall back to on-demand MPK parsing on cache miss, memoising results. Built-in and user-extracted definitions always take priority.
  • Wired into pageBuilder: initPluggableEngine calls registry.SetProjectDir(filepath.Dir(backend.Path())) after LoadUserDefinitions, so every CREATE PAGE command automatically discovers project widgets — no manual extraction step needed.
  • widget init demoted to optional: adds --force flag to overwrite existing .def.json files; 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.
  • MPK template augmentation (earlier commits): WidgetTemplate is generated from MPK property definitions; unknown pluggable widgets fall back to MPK-derived templates; multi-widget MPKs handled in FindMPK/ParseMPKForWidget.
  • AI skill updated: custom-widgets.md documents the 4-tier registry, real-time discovery, and MDL name derivation rule so AI agents can reference project widgets without being told to run widget init first.

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 build
  • mxcli widget list -p app.mpr — built-in widgets unchanged (GALLERY, COMBOBOX, etc.)
  • mxcli widget init --help — shows --force flag and updated Long text
  • CREATE PAGE against a project with a third-party widget in widgets/ — page created without prior widget init
  • Validated against real project (CrusherCopilot.mpr) — page creation succeeds with MPK-derived widget

🤖 Generated with Claude Code

engalar and others added 18 commits May 8, 2026 18:16
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]>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

AI Code Review

What Looks Good

This PR successfully implements real-time MPK widget derivation so that CREATE PAGE works without requiring widget init. The implementation is thorough and well-structured:

Core Improvements

  • Real-time MPK fallback in WidgetRegistry: Pre-scans widgets/*.mpk and maps derived MDL names → widget IDs, with on-demand parsing on cache miss
  • Priority ordering: Built-in and user-extracted definitions always take priority over MPK-derived ones (correctly implemented in preScanWidgets)
  • Seamless integration: Wired into pageBuilder so every CREATE PAGE command automatically discovers project widgets
  • Improved workflow: widget init demoted to optional inspection tool with --force flag for overwriting definitions
  • Multi-widget MPK support: Fixed FindMPK and ParseMPKForWidget to handle MPKs containing multiple widgets
  • Template generation: Added GenerateFromMPK to build WidgetTemplate from MPK property definitions
  • Documentation: Updated AI skill (custom-widgets.md) to reflect the new 4-tier registry and real-time discovery

Code Quality

  • Changes are consistently applied across all relevant files
  • No duplication of existing functionality
  • Error handling is appropriate (checking return values, logging warnings)
  • Method receivers appear correct
  • No silent side effects on typos apparent

Test Coverage

  • Includes 4 new tests for WidgetRegistry MPK fallback (widget_registry_mpk_test.go)
  • Tests for multi-widget MPK support in sdk/widgets/mpk/mpk_test.go
  • Unit tests for template generation in sdk/widgets/generate_test.go
  • Integration tests verifying the fallback mechanism
  • Test plan covers build, CLI verification, and real project validation

Recommendation

Approve - The PR is ready for merging. It addresses a significant workflow improvement (eliminating the mandatory widget init step) while maintaining backward compatibility and following the project's architectural patterns. All checklist items have been satisfactorily addressed:

  • No overlap/duplication with existing features
  • No MDL syntax changes (so syntax design checklist doesn't apply)
  • Full-stack consistency maintained for existing MDL features
  • Adequate test coverage with no problematic synchronization patterns
  • Properly scoped to a single concern (real-time MPK widget derivation)
  • High code quality with consistent application of patterns

The implementation correctly prioritizes built-in → user override → MPK derivation, ensuring no accidental overrides of carefully crafted definitions. The demotion of widget init to an optional tool is well-communicated through updated help text and documentation.


Automated review via OpenRouter (Nemotron Super 120B) — workflow source

@github-actions github-actions Bot mentioned this pull request May 9, 2026
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.

1 participant