From 249f7982957cca16e9e8581e0e186be598f9a2be Mon Sep 17 00:00:00 2001 From: MendixMau Date: Fri, 25 Sep 2026 15:18:25 +0200 Subject: [PATCH 1/2] =?UTF-8?q?learn(skills):=20learned-file-upload-widget?= =?UTF-8?q?=20=E2=80=94=20File=20Uploader=20MDL=20shape,=20traps,=20upload?= =?UTF-8?q?=20instrument=20(field-proven=20on=20mxcli=20v0.24.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 1 + README.md | 1 + ROUTING.md | 1 + agents/mdl-agent.md | 1 + agents/test-agent.md | 1 + bin/gate-check.sh | 2 +- bin/lib/skill-routing.tsv | 1 + skills/learned-file-upload-widget.md | 149 +++++++++++++++++++++++++++ 8 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 skills/learned-file-upload-widget.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 480cc18..320efb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ three commits past it), and a bug report can name a release instead of a sha nob Sections dated before 2026-09-19 predate the cycle and stay as they are. ## Unreleased +- learn(skills/learned-file-upload-widget.md): **a file upload mxcli can author, with proof that it uploads.** The Mendix File Uploader 2.5.0 bound to a `System.FileDocument` specialisation: the MDL shape (entities, grants, create/delete microflows, advanced formats), which upload widgets mxcli cannot author (classic FileManager, PDS uploader), the two traps with workarounds (a simple-mode `allowedfileformat` passes exec and fails `mx check` with CE0463; an uploader DESCRIBE will not re-exec, `exposes 2 datasources`), and the six-step upload instrument. Field run on stock v0.24.0: the section-4 MDL taken verbatim from the skill gave `mx check` 0 errors, 2/2 files stored, 2/2 downloads sha256-equal, `.csv` rejected with 0 rows; both traps reproduce unchanged on v0.24.0 — a Mendix app-rebuild project - learn(bug-logs): **`BUG-DRAFT-layout-merge-in-if-branch`** — the v0.24.0 layout engine (upstream #1154) puts the merge node on top of the activity that follows a loop inside an `if` branch, so `mxcli lint` reports MPR008 on a correct script that carries no `@position`; plain if/else and a branch holding only the loop lay out clean. Paste-ready draft in `bug-logs/pending-github-issues/`, NOT YET FILED. Matters to the lint ratchet (#134): an MPR008 whose two elements are a merge and the activity after a loop in an `if` branch is this bug, fixed by the workaround, never by `--update-baseline`. Measured on mxcli v0.24.0, Mendix 11.12.1, scratch copy of a PoC model — Maurits Visser - new(skills/microflow-preflight.md): **a microflow with a loop, a list built from a list, or more than 20 activities now gets a posted checklist before its first MDL line.** Lint runs only after exec. CONV009/QUAL003 count top-level activities only, so 20 activities inside one loop count as 3. Retrieve, REST and Java calls inside a loop have no lint rule at all. The skill maps each Mendix microflow best practice to the rule that catches it afterwards, or marks it "preflight is the only check". It sets the layout rule to "omit `@position`", which upstream now states too, and adds STOP row 25, a baseline routing row and an mdl-agent bullet. It corrects the stale `reset layout` advice (BUG-28), the commit-in-loop cookbook recipe and the 30–50-activity guideline. Field evidence: measured on mxcli v0.24.0 on a scratch copy of a real model — CONV011 on a commit in a loop, MPR008 on partial and 100 px hand placement, MPR011 on negative loop-body coordinates, and a v0.24.0 auto-layout defect where a loop followed by an activity in an `if` branch puts the merge on that activity (MPR008 on a correct script) — Maurits Visser - fix(project-bin/exec.sh): **the lint ratchet now runs after every clean mxbuild, and its verdict lands in the same BUILD-LOG row.** mxbuild proves the model compiles, not how it is built: on a field project a migration microflow shipped three commits inside loops (`mxcli lint` CONV011) under "✅ applied · mxbuild clean", because lint lived only in the gate-agent definition as an optional step and that agent had been spawned 0 times against 21 `exec.sh` runs. `exec.sh` now calls `bin/lint-gate.sh` after a clean build; a rise vs the committed baseline writes `⚠️ applied, LINT ROSE: ` and exits 1 while keeping the write (shape, not corruption), `SKIP_LINT=` is the only override and is recorded in the row, and a project without `lint-gate.sh` gets `lint not installed` rather than a blank cell. `agents/gate-agent.md` and `agents/agent-roles.md` now read the row instead of treating lint as optional; `learned-detection-gaps.md` gains the register row. Field run: a marketplace guest-group migration, 2026-09-24, ~13 s per run on a 10k-finding project — Maurits Visser diff --git a/README.md b/README.md index 3e3932b..341af55 100644 --- a/README.md +++ b/README.md @@ -623,6 +623,7 @@ Every mxcli project has a `.ai-context/skills/` directory (bundled by `mxcli ini | Generating a whole page tree in one script — the structure patterns that survive it | `skills/oneshot-page-structure-patterns.md` | | Building or auditing a collapsible sidebar nav — Atlas Core's collapsed state needs icons assigned per menu item or it silently clips label text | `skills/learned-sidebar-collapse-icons.md` | | Building or altering any data grid — native DATAGRID vs pluggable DG2 decision rule, the ALTER PAGE INSERT corruption, sort-by and filter-binding traps | `skills/learned-dg2-patterns.md` | +| Putting any file upload / attachment / document field on a page, an uploader page failing mx check with CE0463, or an uploader DESCRIBE that will not re-execute — the File Uploader MDL shape proven end to end on v0.23 and v0.24, the widgets mxcli cannot author, and the upload instrument | `skills/learned-file-upload-widget.md` | **Build · Agents — Mendix AI agents, tools, knowledge bases, chat UI** diff --git a/ROUTING.md b/ROUTING.md index 5ebad4d..ebdf291 100644 --- a/ROUTING.md +++ b/ROUTING.md @@ -168,6 +168,7 @@ picks the row up. That is the whole procedure — there is no second list to rem | Generating a whole page tree in one script — the structure patterns that survive it | `skills/oneshot-page-structure-patterns.md` | mdl | 5 | ondemand | | Building or auditing a collapsible sidebar nav — Atlas Core's collapsed state needs icons assigned per menu item or it silently clips label text | `skills/learned-sidebar-collapse-icons.md` | mdl | 5 | ondemand | | Building or altering any data grid — native DATAGRID vs pluggable DG2 decision rule, the ALTER PAGE INSERT corruption, sort-by and filter-binding traps | `skills/learned-dg2-patterns.md` | mdl | 5 | ondemand | +| Putting any file upload / attachment / document field on a page, an uploader page failing mx check with CE0463, or an uploader DESCRIBE that will not re-execute — the File Uploader MDL shape proven end to end on v0.23 and v0.24, the widgets mxcli cannot author, and the upload instrument | `skills/learned-file-upload-widget.md` | mdl,test | 5 | ondemand | #### Build · Agents — Mendix AI agents, tools, knowledge bases, chat UI diff --git a/agents/mdl-agent.md b/agents/mdl-agent.md index f27caf7..bded40d 100644 --- a/agents/mdl-agent.md +++ b/agents/mdl-agent.md @@ -102,6 +102,7 @@ a rule below names an asset (e.g. "the wireframe", "the brief"), it means the pa | `skills/field-run.md` | Driving the whole toolkit pipeline on a real source to find what the written skills don't say — the toolkit is the subject, not the app it builds | | `skills/learned-mdl-cannot-express.md` | Before a wireframe or a design commits to a WIDGET — and when a page script hits a parse error that looks like a syntax mistake: the short list of things MDL cannot write at all, and the four-minute probe that answers it at Stage 3 instead of at build time | | `skills/learned-dg2-patterns.md` | Building or altering any data grid — native DATAGRID vs pluggable DG2 decision rule, the ALTER PAGE INSERT corruption, sort-by and filter-binding traps | +| `skills/learned-file-upload-widget.md` | Putting any file upload / attachment / document field on a page, an uploader page failing mx check with CE0463, or an uploader DESCRIBE that will not re-execute — the File Uploader MDL shape proven end to end on v0.23 and v0.24, the widgets mxcli cannot author, and the upload instrument | | `skills/scriptable-sp-verification.md` | Needing Studio Pro load evidence without a human at the GUI — direct-binary launch and log capture; a capture technique, NOT a validated pass/fail oracle | | `skills/learned-local-db-confusion.md` | A runtime test reads/writes data that then is not there, or vice versa — three local Postgres instances can answer on this box; resolve the real port from the project's own compose file first | | `skills/walking-skeleton.md` | Stage 5 start, before the first module of any entry mode — one entity, flow, page, nav, demo user, journey and screenshot proven in the running app, so build/run/look/test are known to work before a module depends on them | diff --git a/agents/test-agent.md b/agents/test-agent.md index 4d83c47..66b9f99 100644 --- a/agents/test-agent.md +++ b/agents/test-agent.md @@ -64,6 +64,7 @@ and `"DESCRIBE ..."` reads are always fine, and are how you ground every name yo | `skills/empty-widget-triage.md` | A page/grid/combobox renders empty (blank cells, zero rows, zero options) during UI review or an e2e run — before assuming a single cause | | `skills/doctor-triage.md` | doctor.sh reports FAIL or WARN, or someone asks whether a red setup line blocks them — check what is actually on the machine before naming a fix; a wrong-arch binary, a missing one and a broken self-check all read the same | | `skills/field-run.md` | Driving the whole toolkit pipeline on a real source to find what the written skills don't say — the toolkit is the subject, not the app it builds | +| `skills/learned-file-upload-widget.md` | Putting any file upload / attachment / document field on a page, an uploader page failing mx check with CE0463, or an uploader DESCRIBE that will not re-execute — the File Uploader MDL shape proven end to end on v0.23 and v0.24, the widgets mxcli cannot author, and the upload instrument | | `skills/learned-local-db-confusion.md` | A runtime test reads/writes data that then is not there, or vice versa — three local Postgres instances can answer on this box; resolve the real port from the project's own compose file first | | `skills/full-harness-audit.md` | The user asks for a full end-to-end test, a click-through proof, or does-everything-actually-work — or you are unsure which harness skill applies; this one routes you | | `skills/test-result-audit.md` | End of any build+test cycle that wrote docs/report.json — did the testing itself hold up, not just get filed; one level up from finding-disposition | diff --git a/bin/gate-check.sh b/bin/gate-check.sh index dc7fe19..2e7cb7d 100755 --- a/bin/gate-check.sh +++ b/bin/gate-check.sh @@ -1609,7 +1609,7 @@ stage_protocol_paths() { 2) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-brd.md skills/checkpoints/checkpoint-architecture.md skills/image-transcription.md skills/small-project-tier.md skills/kb-generation.md skills/brd-generation.md skills/brd-validation.md" ;; 3) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-design.md skills/small-project-tier.md skills/layering-review.md skills/architecture-blueprint.md skills/modularize-domain.md skills/design-artifacts.md skills/brd-to-build-plan.md skills/workflow-structure-rules.md skills/learned-mdl-cannot-express.md" ;; 4) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-build.md skills/agent-roles.md skills/small-project-tier.md skills/module-brief.md skills/module-folder-convention.md skills/brd-to-build-plan.md skills/coverage-ledger.md skills/workflow-structure-rules.md skills/rest-integration-first-time-right.md skills/learned-constants-and-secrets.md" ;; - 5|build-ready) echo "skills/interview-protocol.md skills/grill-mode.md skills/agent-roles.md skills/module-brief.md skills/learned-mdl-preflight.md skills/module-folder-convention.md skills/learned-microflow-patterns.md skills/microflow-preflight.md skills/ui-preflight-pages.md skills/design-spacing.md skills/learned-stylegallery.md skills/ui-loop.md skills/learned-mcp-patterns.md skills/module-review.md skills/testing-shape.md skills/microflow-loop-antipatterns.md skills/iterative-build-loop.md skills/mdl-cookbook-microflows.md skills/build/mdl/oneshot-mdl-method.md skills/learned-page-patterns.md skills/oneshot-page-structure-patterns.md skills/mendix-agents.md skills/mendix-agent-ui.md skills/mendix-agent-setup.md skills/fixture-seeding.md skills/journey-proof.md skills/monkey-test.md skills/report-schema.md skills/harness-architecture.md skills/process-coherence-pass.md skills/lint-that-actually-runs.md skills/improvement-register.md skills/journey-examples.md skills/wiring-sweep.md skills/learned-workflow-patterns.md skills/workflow-structure-rules.md skills/rest-integration-first-time-right.md skills/bug-submission-checklist.md skills/empty-widget-triage.md skills/learned-sidebar-collapse-icons.md skills/learned-popup-navigation.md skills/learned-datagrid-customcontent-binding.md skills/learned-popup-feedback-pattern.md skills/learned-mdl-cannot-express.md skills/learned-css-that-never-applied.md skills/learned-detection-gaps.md skills/learned-dg2-patterns.md skills/security-is-not-a-later-script.md skills/learned-local-db-confusion.md skills/full-harness-audit.md skills/test-result-audit.md skills/finding-disposition.md skills/preview-over-hub-tunnel.md skills/walking-skeleton.md skills/platform-link.md skills/teamserver-alignment.md skills/learned-constants-and-secrets.md" ;; + 5|build-ready) echo "skills/interview-protocol.md skills/grill-mode.md skills/agent-roles.md skills/module-brief.md skills/learned-mdl-preflight.md skills/module-folder-convention.md skills/learned-microflow-patterns.md skills/microflow-preflight.md skills/ui-preflight-pages.md skills/design-spacing.md skills/learned-stylegallery.md skills/ui-loop.md skills/learned-mcp-patterns.md skills/module-review.md skills/testing-shape.md skills/microflow-loop-antipatterns.md skills/iterative-build-loop.md skills/mdl-cookbook-microflows.md skills/build/mdl/oneshot-mdl-method.md skills/learned-page-patterns.md skills/oneshot-page-structure-patterns.md skills/mendix-agents.md skills/mendix-agent-ui.md skills/mendix-agent-setup.md skills/fixture-seeding.md skills/journey-proof.md skills/monkey-test.md skills/report-schema.md skills/harness-architecture.md skills/process-coherence-pass.md skills/lint-that-actually-runs.md skills/improvement-register.md skills/journey-examples.md skills/wiring-sweep.md skills/learned-workflow-patterns.md skills/workflow-structure-rules.md skills/rest-integration-first-time-right.md skills/bug-submission-checklist.md skills/empty-widget-triage.md skills/learned-sidebar-collapse-icons.md skills/learned-popup-navigation.md skills/learned-datagrid-customcontent-binding.md skills/learned-popup-feedback-pattern.md skills/learned-mdl-cannot-express.md skills/learned-css-that-never-applied.md skills/learned-detection-gaps.md skills/learned-dg2-patterns.md skills/learned-file-upload-widget.md skills/security-is-not-a-later-script.md skills/learned-local-db-confusion.md skills/full-harness-audit.md skills/test-result-audit.md skills/finding-disposition.md skills/preview-over-hub-tunnel.md skills/walking-skeleton.md skills/platform-link.md skills/teamserver-alignment.md skills/learned-constants-and-secrets.md" ;; 6) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-cutover.md skills/module-review.md skills/testing-shape.md skills/existing-app-assurance.md skills/app-analysis.md skills/module-dependency-review.md skills/microflow-loop-antipatterns.md skills/qa-loop-goal-pattern.md skills/mendix-agent-setup.md skills/e2e-harness-base.md skills/learned-db-assertions.md skills/fixture-seeding.md skills/journey-proof.md skills/monkey-test.md skills/learned-skill-ux-audit.md skills/learned-skill-scope-delta.md skills/report-schema.md skills/harness-architecture.md skills/process-coherence-pass.md skills/e2e-evidence-report.md skills/record-demo-video.md skills/share-demo-package.md skills/lint-that-actually-runs.md skills/improvement-register.md skills/journey-examples.md skills/wiring-sweep.md skills/workflow-structure-rules.md skills/bug-submission-checklist.md skills/empty-widget-triage.md skills/anonymize-client-app-for-demo.md skills/learned-css-that-never-applied.md skills/learned-detection-gaps.md skills/learned-local-db-confusion.md skills/full-harness-audit.md skills/test-result-audit.md skills/finding-disposition.md skills/handoff-to-studio-pro.md skills/preview-over-hub-tunnel.md skills/platform-link.md skills/teamserver-alignment.md skills/learned-constants-and-secrets.md" ;; 7) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-cutover.md skills/close-the-loop.md skills/share-demo-package.md skills/handoff-to-studio-pro.md skills/platform-link.md skills/teamserver-alignment.md skills/deploy-to-sandbox.md" ;; *) echo "" ;; diff --git a/bin/lib/skill-routing.tsv b/bin/lib/skill-routing.tsv index ce38ff6..fbb771b 100644 --- a/bin/lib/skill-routing.tsv +++ b/bin/lib/skill-routing.tsv @@ -196,6 +196,7 @@ learned-mdl-cannot-express skills/learned-mdl-cannot-express.md Before a wirefra learned-css-that-never-applied skills/learned-css-that-never-applied.md A style change that appears to have done nothing, or an app still grey after a design port every instrument called green — the three ways a correct rule paints nothing (matches nothing / matches chrome / loses the cascade), the two reads that tell them apart, and the class that arrived in the stylesheet and is bound to no widget mdl,review,gate 5,6 baseline design learned-detection-gaps skills/learned-detection-gaps.md Before trusting a green check/exec/DESCRIBE result as proof, or when a runtime symptom appears over a fully green model — the register of constructs that pass early rungs and fail later ones mdl,gate,review 5,6 baseline diagnose learned-dg2-patterns skills/learned-dg2-patterns.md Building or altering any data grid — native DATAGRID vs pluggable DG2 decision rule, the ALTER PAGE INSERT corruption, sort-by and filter-binding traps mdl 5 ondemand build/pages +learned-file-upload-widget skills/learned-file-upload-widget.md Putting any file upload / attachment / document field on a page, an uploader page failing mx check with CE0463, or an uploader DESCRIBE that will not re-execute — the File Uploader MDL shape proven end to end on v0.23 and v0.24, the widgets mxcli cannot author, and the upload instrument mdl,test 5 ondemand build/pages security-is-not-a-later-script skills/security-is-not-a-later-script.md Creating any entity, or calling a module security-ready — entity and grants land in one script, and ready means SHOW SECURITY MATRIX proves it mdl,gate,review 5 baseline build/security scriptable-sp-verification skills/scriptable-sp-verification.md Needing Studio Pro load evidence without a human at the GUI — direct-binary launch and log capture; a capture technique, NOT a validated pass/fail oracle mdl,gate - ondemand diagnose learned-local-db-confusion skills/learned-local-db-confusion.md A runtime test reads/writes data that then is not there, or vice versa — three local Postgres instances can answer on this box; resolve the real port from the project's own compose file first mdl,test,gate 5,6 ondemand diagnose diff --git a/skills/learned-file-upload-widget.md b/skills/learned-file-upload-widget.md new file mode 100644 index 0000000..90ced1a --- /dev/null +++ b/skills/learned-file-upload-widget.md @@ -0,0 +1,149 @@ +# File upload widget — a file upload mxcli can author, and proof that it uploads + +**Applies to:** any mxcli project +**Purpose:** putting a working file upload on a page: the Mendix File Uploader 2.5.0 pluggable +widget bound to a `System.FileDocument` specialisation, with its create/delete microflows, grants +and allowed formats. Covers which upload widgets mxcli cannot author, the two CE0463 / round-trip +traps with their workarounds, and the instrument that proves an upload works end to end. Load it +before adding any upload/attachment/document field, when an uploader page fails `mx check` with +CE0463, or when a DESCRIBE of an uploader page will not re-execute. +**Source:** a Mendix app-rebuild project, 2026-09-25 (research, then a 6-iteration e2e loop, then a +v0.24.0 re-test). + +--- + +Field-proven 2026-09-25 on Mendix 11.12.2, in throwaway apps made with `mxcli new`, on stock +mxcli **v0.23.0** and again on stock **v0.24.0**. On v0.24.0 the MDL in section 4 was run exactly as +written, taken straight from this file, with only a wrapper around it (module, role, page, menu microflow, +demo user). Result: `mxcli check --references` passed, exec reported 0 errors, `mx check` said +`The app contains: 0 errors.`, and the app booted. A non-admin user uploaded 2/2 files (1024 B .txt, +244 B .zip), both rows had `HasContents true`, both downloads were sha256-equal to the source (2/2 +`MATCH`), and a `.csv` was rejected with 0 rows created. Both traps in section 5 reproduce unchanged on +v0.24.0. + +## 1. Pick the widget + +| Widget | mxcli can author it? | Use it? | +|---|---|---| +| **Mendix File Uploader 2.5.0** (`com.mendix.widget.web.fileuploader.FileUploader`, keyword `fileuploader`) | Yes, on stock v0.23 and v0.24, with the rules below | **Yes.** Proven end to end | +| Classic `Forms$FileManager` (Studio Pro "File manager") | No. No keyword. A `.def.json` gives a false-green `mxcli check`, then exec fails with `template not found: filemanager` | No, until mxcli gets a native `filemanager` (upstream #151) | +| `mendix.PDSFileUploader` | Writes, but stock mxcli gives CE0463 (action variables not written). It also posts to a REST endpoint, not to a FileDocument | Only with a REST backend and a patched mxcli | + +## 2. What the File Uploader needs (it is not a FileManager) + +The File Uploader does not fill the data view's own object. It works on a **context object** and +creates one FileDocument per dropped file: + +1. The widget calls `createFileAction` with the context object. That microflow creates the file + entity, sets the association to the context, and commits it. +2. The widget watches `associatedFiles` (an association path from the context) for the new + object, then POSTs the bytes to `/file?guid=`. +3. On failure it calls `onUploadFailureFile` with the file object, so that microflow can delete it. + +So the domain model is: a context entity, a `System.FileDocument` specialisation, and a reference +from the file to the context. + +## 3. Install + +1. Get the widget `.mpk`. The Marketplace module package (`FileUploader.mpk`, 2.5.0, sha256 + `9e592629…`) contains `widgets/com.mendix.widget.web.FileUploader.mpk`. Copy only that inner file + into the project's `widgets/`. The module's own entities and nanoflows are not needed. +2. Run `mxcli widget init -p App.mpr`. Without it you get `MDL-WIDGET25 fileuploader is not a widget in this project`. + +## 4. The working MDL shape + +```sql +create persistent entity "Uploads"."UploadRequest" ("Title": String(200)); +create persistent entity "Uploads"."Attachment" extends System.FileDocument ("Note": String(200)); +create association "Uploads"."Attachment_UploadRequest" + from "Uploads"."Attachment" to "Uploads"."UploadRequest" type reference; + +-- tested with write * (includes Name and Contents). The module's own reference grant writes Name, Contents and the association; a narrower grant was not tested +grant "Uploads"."User" on "Uploads"."UploadRequest" (create, delete, read *, write *); +grant "Uploads"."User" on "Uploads"."Attachment" (create, delete, read *, write *); + +create microflow "Uploads"."ACT_CreateAttachment" ($UploadRequest: "Uploads"."UploadRequest") +returns "Uploads"."Attachment" as $Attachment +begin + $Attachment = create "Uploads"."Attachment" ("Attachment_UploadRequest" = $UploadRequest); + commit $Attachment with events refresh; + return $Attachment; +end; +/ +create microflow "Uploads"."ACT_DeleteAttachment" ($Attachment: "Uploads"."Attachment") +begin + delete $Attachment; +end; +/ + +-- inside a dataview on the context object: +fileuploader "upFiles" ( + uploadMode: 'files', + associatedFiles: association $currentObject/Uploads.Attachment_UploadRequest, + readOnlyMode: false, + createFileAction: microflow Uploads.ACT_CreateAttachment, + onUploadFailureFile: microflow Uploads.ACT_DeleteAttachment, + maxFileSize: 5, + objectCreationTimeout: 10, + enableCustomButtons: false +) { + allowedfileformat "fmtTxt" (configMode: 'advanced', mimeType: 'text/plain', extensions: '.txt', typeFormatDescription: 'Text file') + allowedfileformat "fmtZip" (configMode: 'advanced', mimeType: 'application/zip', extensions: '.zip', typeFormatDescription: 'ZIP archive') +} +``` + +Also grant `execute` on both microflows to the user's module role. Missing grants give CE0106 at `mx check`. + +**Commit the context object before you open the page.** The menu microflow creates the +`UploadRequest`, commits it, then shows the page. The Save button then only has to commit it again +(or commit and show a result page). An uncommitted context was not tested. + +## 5. The traps, and what to do on stock v0.23 / v0.24 + +Both of the first two traps reproduce unchanged on v0.24.0, which was re-tested 2026-09-25. v0.24's CE0463 fix (#1161) is for +Barcode Scanner's seeded object lists, a different cause. Neither trap is filed upstream yet (checked: closed +#999 and #574 are related but not the same). When a newer mxcli ships, re-probe both on a copy before trusting this table: +(1) change one format to `configMode: 'simple', predefinedType: 'plainTextFile'`, exec, run `mx check`; +(2) `describe page` the uploader page and exec the output unchanged on a copy that differs from it. + +| Trap | Symptom | Stock v0.23 / v0.24 workaround | Proposed fix (proven on a local mxcli build, not upstream yet) | +|---|---|---|---| +| `allowedfileformat` in the default `configMode: 'simple'` (`predefinedType: 'plainTextFile'`) | `mx check`: `[CE0463] "The definition of this widget has changed. ..." at File uploader 'upFiles'`, `The app contains: 1 errors.` | Write every format as `configMode: 'advanced'` with `mimeType`, `extensions` and `typeFormatDescription` | Nested visibility rules applied to object-list items: the hidden required `typeFormatDescription` is written as null (Studio Pro's shape), not as a filled template | +| DESCRIBE of an uploader page, then re-exec | exec: `` widget `upFiles` (fileuploader) exposes 2 datasources, so a generic `datasource:` clause is ambiguous — name the one you mean: associatedFiles, associatedImages `` | Hand-edit the DESCRIBE output: change `DataSource:` to `associatedFiles:` | DESCRIBE names the key when the widget's schema declares more than one datasource | +| Object-list item names | DESCRIBE prints `allowedfileformat1`, not the name you wrote, so `drop widget "fmtTxt"` has nothing to hit later | Replace the whole page (`create or replace page`) to change formats | Not fixed | +| DESCRIBE prints `predefinedType: 'pdfFile'` on advanced items | Re-exec warns `MDL-WIDGET10 ... predefinedType is hidden when its own configMode is not "simple" — the value will be ignored` (a warning; mx check stays 0 errors) | Delete that line from advanced items | Not fixed | +| `mxcli check` says "Check passed!" | Proves nothing for widgets: CE0463 only shows at `mx check` | Always run `mx check` after exec | n/a | +| Re-exec of an identical page | `Unchanged page ...`: nothing was written, so `0 errors` proves nothing about the round trip | Run a round-trip test against a copy where the page differs | n/a | + +With the locally patched build, a simple-mode `.txt` format is shown at runtime as "Plain Text (.txt)" in +the rejection message, so the predefined type does reach the widget. + +## 6. The instrument: what counts as "the upload works" + +`mx check` 0 errors only proves the model is valid. The upload counts as working only when all of these hold: + +1. `mx check`: `The app contains: 0 errors.` +2. The app boots: `mxcli run --local ... --db-type hsqldb --app-port --admin-port --serve-port `. + The default serve port 6543 collides with Studio Pro's own `mxbuild --serve`, so pick a free one. +3. Playwright, logged in as a **non-admin** demo user: open the page, then + `page.locator('.mx-name- input[type=file]').setInputFiles([...])`. Wait until the widget text + shows `Uploaded successfully.` once per file, then click Save. +4. OQL on your own admin port: `mxcli oql -p App.mpr --direct --port "select a.Name, a.Size, a.HasContents from Mod.Attachment a"`. + The default `--port` is 8090. Never let it default when another app is running. +5. Download the file back as the same user (`GET /file?guid=&target=internal` in the logged-in + browser context) and compare its sha256 with the source file. This catches a HasContents=true row that holds the wrong bytes. +6. Negative check: drop a format you did not allow. The widget must show `File format is not supported, ...`, and no row may be created. + +Measured result, stock v0.23 (advanced formats): 1/1 file stored, 1024 bytes, `HasContents true`, sha256 match. +Stock v0.24.0 (advanced .txt and .zip, fresh app): 2/2 files stored (1024 B and 244 B), 2/2 sha256 match, `.csv` +rejected with 0 rows created. +Patched build (simple .txt plus advanced .zip, written from a DESCRIBE round trip): 2/2 files stored +(1024 B and 244 B), both sha256 match, `.csv` rejected with 0 rows created. + +## 7. Not covered + +- Image mode (`uploadMode: 'images'`, `associatedImages`, `System.Image`) was not tested. +- Custom buttons, `maxFilesPerUpload`, and the delete-from-widget path were not tested. +- Only the `mxcli run --local` runtime with HSQLDB was used. Not tested on PostgreSQL, in Docker, or opened in Studio Pro. +- The proposed fixes in section 5 exist only as a local mxcli patch with unit tests. When they land upstream, + re-run the simple-mode format and the DESCRIBE round trip before dropping the workarounds. From 312f2a8c19d65af34b161475ac3fa6e948a249a2 Mon Sep 17 00:00:00 2001 From: MendixMau Date: Fri, 25 Sep 2026 15:43:20 +0200 Subject: [PATCH 2/2] =?UTF-8?q?learn(skills):=20learned-file-upload-widget?= =?UTF-8?q?=20=E2=80=94=20both=20traps=20now=20filed=20upstream=20(mendixl?= =?UTF-8?q?abs/mxcli#1198,=20#1199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- skills/learned-file-upload-widget.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skills/learned-file-upload-widget.md b/skills/learned-file-upload-widget.md index 90ced1a..8ae288d 100644 --- a/skills/learned-file-upload-widget.md +++ b/skills/learned-file-upload-widget.md @@ -101,8 +101,8 @@ Also grant `execute` on both microflows to the user's module role. Missing grant ## 5. The traps, and what to do on stock v0.23 / v0.24 Both of the first two traps reproduce unchanged on v0.24.0, which was re-tested 2026-09-25. v0.24's CE0463 fix (#1161) is for -Barcode Scanner's seeded object lists, a different cause. Neither trap is filed upstream yet (checked: closed -#999 and #574 are related but not the same). When a newer mxcli ships, re-probe both on a copy before trusting this table: +Barcode Scanner's seeded object lists, a different cause. Both are filed upstream: the simple-mode CE0463 as mendixlabs/mxcli#1198, the DESCRIBE round trip as #1199 +(closed #999 and #574 are related but not the same). When a newer mxcli ships, re-probe both on a copy before trusting this table: (1) change one format to `configMode: 'simple', predefinedType: 'plainTextFile'`, exec, run `mx check`; (2) `describe page` the uploader page and exec the output unchanged on a copy that differs from it. @@ -145,5 +145,5 @@ Patched build (simple .txt plus advanced .zip, written from a DESCRIBE round tri - Image mode (`uploadMode: 'images'`, `associatedImages`, `System.Image`) was not tested. - Custom buttons, `maxFilesPerUpload`, and the delete-from-widget path were not tested. - Only the `mxcli run --local` runtime with HSQLDB was used. Not tested on PostgreSQL, in Docker, or opened in Studio Pro. -- The proposed fixes in section 5 exist only as a local mxcli patch with unit tests. When they land upstream, +- The proposed fixes in section 5 exist only as a local mxcli patch with unit tests (#1198, #1199 describe them). When those issues close, re-run the simple-mode format and the DESCRIBE round trip before dropping the workarounds.