fix(pages): read a pluggable widget's flow datasource, and stop dropping children silently - #59
Merged
Merged
Conversation
…flowSettings (mendixlabs#795) DESCRIBE PAGE emitted `datagrid g1 {` with no DataSource for a DataGrid2 bound to a microflow, so re-applying the describe output recreated the grid with no data source at all. A `database from` source described correctly, which made the loss look selective. A Forms$MicroflowSource stores the microflow name in the nested Forms$MicroflowSettings ("MicroflowSettings" -> "Microflow") — what the write path emits and what Studio Pro stores. extractDataGrid2DataSource looked up a top-level "Microflow" key, got "", and returned no datasource. The describe formatter's `case "microflow"` branch was correct all along, so nothing was ever wrong with the stored model or the write path: purely a read bug. Four readers had their own copy of this lookup and disagreed — two read the nested settings, one read the top-level key, one did both. They now share microflowSourceRef / nanoflowSourceRef, which read the nested settings and fall back to the legacy top-level key so older files still round-trip. Two gaps the regression tests found while covering this: - a Gallery whose source used the legacy flat shape was unreadable too: the correct reader it delegates to handles only the nested form, and the flat fallback in its own switch is not reached for a CustomWidget gallery - Forms$NanoflowSource was missing entirely from the DataGrid2 and Gallery switches, so a Studio-Pro-authored nanoflow datasource described as nothing Left alone deliberately: CustomWidgets$CustomWidgetNanoflowSource, a different metamodel type whose Nanoflow key really is top-level, and the Forms$MicroflowAction reads, which are actions rather than datasources. Also corrects the datasource table in the create-page skill: it documented `datasource: microflow Module.GetData()`, and the trailing `()` does not parse. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
A construct the codec engine cannot represent — a nanoflow datasource on a DataGrid2 is the reachable case — was logged and discarded while `mxcli exec` still printed `Created page`. The grid landed with no data source at all. The converter does return an error; it just had nowhere to go. Child content is serialized through widgetobj.ChildSerializer, whose methods return BSON with no error channel — the interface change flagged by the TODO in mdl/backend/widgetobj/builder.go. Until that lands, the failure is recorded in the modelsdk backend and drained by the page/snippet write entry points, so the statement fails with the actionable message instead of the write succeeding with data missing. ADR-0004 already required this: where the codec path cannot reproduce a construct the backend refuses the op rather than dropping it. Authoring a pluggable nanoflow datasource still needs MXCLI_ENGINE=legacy, which writes it correctly — verified, since the error message says so. Also adds the missing `nanoflow` case to the DataGrid2 and Gallery describe formatters. Reading such a source works after mendixlabs#795, but the formatter had only database/microflow/parameter branches, so a legacy-written or Studio-Pro-authored nanoflow grid still described with no DataSource. Any new write entry point that builds pluggable widgets must drain too; the symptom table records that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
…urns
A data container bound to a microflow or nanoflow reported the *flow* as its
context entity:
datagrid g1 (DataSource: microflow Module.GetOrders) {
-- Context: $currentObject (Module.GetOrders) <- the flow, not the entity
The datasource reference was used verbatim. That is correct for a database
source, where the reference is the entity, and wrong for a flow source, where
it is the flow's own qualified name. Besides the misleading comment, the
context is passed down to the column and child-widget readers, which resolve
attributes against it.
The flow's return type is resolved instead, taking the entity from an Object
or List return. When the flow cannot be resolved, or returns a scalar, the
reference is kept — the previous behaviour, so nothing gets worse.
Resolution goes through ListMicroflows/ListNanoflows plus the container
hierarchy rather than GetRawUnitByName, which the flow builder's fast path
uses: that method is unimplemented on the modelsdk engine, which is the
default, so the list path is the one that actually resolves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
ako
pushed a commit
that referenced
this pull request
Aug 1, 2026
Every bug fix touches .claude/skills/fix-issue.md, and new rows went in directly under the table header. Two branches fixing unrelated bugs therefore inserted at the same line, which git cannot merge — a conflict by construction rather than by bad luck. It cost five separate resolution rounds in one week (#59-#62 three times, then #68 and #69), and each round risks silently dropping a row: one earlier round was "resolved" by deleting a PR's rows entirely and restoring them in a follow-up. Appending puts each branch's insert at a different offset, which git merges without help. The table is unordered — it is looked up by matching a symptom, not read top to bottom — so position carries no meaning and appending costs nothing. Recorded in both places a contributor might look: the skill's How to Use, with the reasoning so it is not "fixed" back, and the PR checklist item in CLAUDE.md. Existing rows are deliberately left where they are. Reordering them would conflict with every open branch at once, which is the problem this change exists to avoid. Folded into this branch rather than opened separately: it edits the same How to Use block, so a sixth branch would have manufactured exactly the conflict it is meant to prevent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes
mendixlabs/mxcli#795. Split out of #57.(Issue numbers refer to
mendixlabs/mxcli, not this fork.)585f5f0— read the microflow datasource from MicroflowSettings (mendixlabs#795)A DataGrid2 bound to a microflow described as
datagrid g1 {with noDataSource, so re-applying the describe output recreated the grid unbound. Adatabase fromgrid described correctly, which made the loss look selective.The name lives in the nested
Forms$MicroflowSettings;extractDataGrid2DataSourceread a top-levelMicroflowkey, got"", and returned no datasource. The describe formatter was correct all along — purely a read bug.Four readers each had their own copy of this lookup and disagreed. They now share
microflowSourceRef/nanoflowSourceRef, which read the nested settings and fall back to the legacy top-level key.Two further gaps the regression tests turned up:
Forms$NanoflowSourcewas missing entirely from the DataGrid2 and Gallery switchesAlso corrects the create-page skill, which documented
datasource: microflow Module.GetData()— the trailing()does not parse.78699b8— fail the write when a child is droppedA construct the codec engine cannot represent (a nanoflow datasource on a DataGrid2 is the reachable case) was logged and discarded while
mxcli execstill printedCreated page.The converter does return an error; it had nowhere to go.
widgetobj.ChildSerializerreturns BSON with no error channel — the interface change flagged by the existingTODO(shared-types)inbuilder.go. Until that lands, the failure is recorded and drained by the page/snippet write entry points, so the statement fails instead of the write succeeding with data missing. ADR-0004 already required this.Verified the error's own advice is true:
MXCLI_ENGINE=legacydoes write a nanoflow datasource correctly. That surfaced one more gap — the describe formatter had onlydatabase/microflow/parametercases, so a legacy-written nanoflow grid still described with no DataSource. Added thenanoflowcase.Trade-off: the accumulator is package-level, so any new write entry point building pluggable widgets must drain too. Recorded in the symptom table.
6e59e0e— describe a flow datasource's context as the entity it returnswidget.EntityContext = widget.DataSource.Referenceis right for a database source and wrong for a flow source:More than cosmetic:
EntityContextis passed to the column and child-widget readers, which resolve attributes against it. Unresolvable flows and scalar returns keep the reference, so nothing regresses.Resolution goes through
ListMicroflows/ListNanoflows+ the container hierarchy rather thanGetRawUnitByName, which is unimplemented on the modelsdk engine — the default. The obvious-looking approach would have silently done nothing.Testing
make build,make test,make lint,make check-mdlpass on this branch alone.The
describe→exec→describeround-trip the issue is about now produces byte-identical output. New tests incmd_pages_describe_flowsource_test.go,cmd_pages_describe_flowcontext_test.goandwidget_child_error_test.go(all verified failing pre-fix — the flowsource table also caught the two gaps I had not predicted), plus fixtures795-datagrid-microflow-datasource-describe.mdland795b-flow-datasource-context-entity.mdl.Out of scope
A nanoflow datasource on a pluggable widget still cannot be authored by the modelsdk engine.
modelsdk/gen/pageshas noNanoflowSettingstype, the reflection data is not in the tree so it cannot be regenerated, and there is no Studio Pro sample to verify the field set against — hand-writing a guessed shape is how CE0463-class bugs are made. Authoring needs--engine legacy; it now fails loudly rather than quietly.🤖 Generated with Claude Code
https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
Generated by Claude Code