Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m

## Unreleased

- fix(dg2-patterns, detection-gaps, obligation-check): **a DataGrid 2 column re-attributed by `ALTER PAGE … REPLACE` is named as a whole-page re-emit, and the old-shots LOOK FAULT names its remedy** ([#356](https://github.com/MendixMau/mxcli-project-toolkit/issues/356), [#357](https://github.com/MendixMau/mxcli-project-toolkit/issues/357)). #356: the REPLACE form passed `check --references` and failed mxbuild (v0.26.0, Mendix 11.15.0); `learned-dg2-patterns.md` rule 1 now covers replace/re-attribute, plus a detection-gaps row, a preflight gotcha pointer and `BUG-DRAFT-dg2-replace-column-attribute-passes-check` (upstream candidate: `check` should reject it as it rejects the SET form). #357 change 1: after a partial re-take, the "screenshots older than their section's VALID AT" FAULT now says to keep the section and append an addendum with its own `VALID AT:` and PROOF-OF-LOOK lines; `close-the-loop.md` routes re-shot pages there; `test-obligation-valid-at.sh` T5 asserts the text. Per-page staleness (#357 change 2) is not implemented. — joshuamoesa (#356, #357)
- process(build): **Stage 5 drafts the ready set in parallel and applies serially.** The runbook dispatch row said "sequential within a module", which serialised drafting even for independent scripts; it now points to `iterative-build-loop.md` → Gate cost, the single statement of the rule. The unit is the build-plan row: every `BUILD` row whose "Depends on" rows are all applied, minus overlapping writers (two rows that replace, alter or drop+create the same document never share a ready set, and no row is drafted while it overlaps a queued unapplied one; `brd-to-build-plan.md` has Produces name every document a row writes), runs at once, its scripts in order, one `mdl-agent` per script. Applies stay one at a time through `bin/exec.sh`, and the ready set is recomputed after each. A queued draft made before a sibling apply landed is re-checked with `mxcli diff --format struct` (preflight STOP rule 23); any removal line (checked against `describe` per BUG-146) or STOP rule 21 target means re-draft, never patch, because a stale restatement of a shared document is exactly rule 21's silent seed-call loss and passes `check --references`. Report `ready set N, drafted N, applied k`; the `mdl-agent` template says a drafter never applies and never depends on an unapplied draft. Field evidence: none yet. — MendixMau
- fix(platform-link): **CLI commits now show their changed documents in Studio Pro History.** Studio Pro's History → Model changes pane reads a `ModelChanges` array from the commit's `refs/notes/mx_metadata` note. Trap 6c's hand-written template set it to `[]`, so every mxcli/agent commit showed "Model changes 0" (field project: 270 of 270 notes empty). New `project-bin/mx-metadata-note.py` (stdlib only) computes the array from the commit's changed `.mxunit` files, in Studio Pro's entry shape: Status, UnitID, UnitType, UnitName and Module (walked up the `.mpr` Unit tree). It has `changes` / `write` / `backfill` / `sync <remote>` subcommands. `install-project-hooks.sh` now also installs post-commit and post-rewrite hooks that write the note and never block, and `--check` covers them, so `sync-project.sh` rolls them out to wired projects. Notes are published with `mx-metadata-note.py sync <remote>` after the branch push. Push config is never touched. With mxcli v0.26+ in the project root the hooks call `mxcli git note --write` instead (mendixlabs/mxcli#1337); the Python script is the fallback for older pins, and on 160 field commits both wrote the same 3,594 entries. Trap 6c rewritten. Measured against Studio Pro's own notes, 51 commits in 5 apps: 3387/3389 entries exact on all five fields. Field run on a 357-commit Mendix 11.15.0 app: 158 notes backfilled and pushed, and Studio Pro History lists the documents per revision. Review fixes: an earlier revision added `HEAD` + the notes ref to `remote.<r>.push`. It was dropped because any push refspec replaces `push.default`. A branch tracking a differently named upstream got a new same-named branch on Team Server where `simple` refuses, a missing local notes ref failed the whole push, and nobody knows whether Studio Pro's own push reads that config. The installer now only fetches the remote's notes (non-forced). It removes that exact `HEAD` + notes pair if present, and leaves and reports any other entry. `sync` handles a remote with no notes ref (it used to exit 1) and retries a rejected push once after re-merging. A note that already lists changes (Studio Pro's) is never overwritten by `write`/`backfill` without `--force`, and `sync` keeps a colleague's non-empty note over ours. The mxcli probe greps `git note --help` for `--write`. Hooks and docs call the script through `resolve_py`. Fixture `tests/wave2/test-mx-metadata-note.sh` (40 cases, over real captured template units; single- and two-tree layouts, amend, backfill and `--force`, push exit codes identical to a no-hooks clone, leftover-refspec cleanup, sync to an empty remote and its retry, notes fetch at install, a diverged notes merge; green under `init.defaultBranch` master and main). — field project (Mendix 11.15 app), MendixMau
- learn(fixture-seeding): **explore the data structure first, choose rows by use case, scan actions for resets before any run.** Two seeding runs on public template apps: seeding raised journey passes 33 -> 58 and 20 -> 51 of the same sets (FAIL 93 -> 68, 163 -> 132; INVALID unchanged), and a "Reset all demodata" journey wiped accounts so 104 of 127 journeys reported `The username or password you entered is incorrect`, identical on empty and seeded databases. Skill gains three questions a seed must answer, a use-case checklist, a destructive-action scan, a generic traps table with symptoms, measured expectations and a denominator checklist; routing trigger now fires on seed/test/demo data and empty pages. Independent review fixes: object ids follow the Mendix bigint pattern with a business-key guard (no uuids), associations are seeded where the page's path stores them, four new traps (enum names, inheritance chain, calculated attributes, view/external entities), and the port-offset and container-label traps moved to `harness-architecture.md`. — MendixMau
Expand Down
4 changes: 3 additions & 1 deletion bin/lib/obligation-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,9 @@ _ob_look_proof_broken() {
local why=""
[ -n "$missing" ] && why="cited screenshots not on disk:$missing"
[ -n "$small" ] && why="${why:+$why; }cited screenshots under 10KB (blank/placeholder):$small"
[ -n "$oldshots" ] && why="${why:+$why; }screenshots older than their section's VALID AT (a previous review's shots):$oldshots"
# The remedy is named in the message (#357): after a partial re-take, agents read this FAULT as
# "re-shoot the whole module" or "accept the FAULT", because nothing pointed at the addendum form.
[ -n "$oldshots" ] && why="${why:+$why; }screenshots older than their section's VALID AT (a previous review's shots):$oldshots — partial re-take? keep the old section and its original stamp (restore it if bumped); append an addendum (VALID AT: <model commit the shots were taken on> + PROOF-OF-LOOK for the re-shot pages) — module-review.md stage 5"
if [ -n "$claimed" ] && [ "$n_cited" -lt "$claimed" ]; then
why="${why:+$why; }$n_cited citation(s) for $claimed reviewed page(s)"
fi
Expand Down
23 changes: 23 additions & 0 deletions bug-logs/mxcli-bugs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6950,3 +6950,26 @@ Two related findings from reading the v0.26.0 source (not field-run):
matching the project. Recipe and the rule-set comparison to run before any diff:
`skills/lint-that-actually-runs.md` → "Linting a copy of the model". `lint-gate.sh`'s collapse
guard catches this inside the gate. Nothing catches it when lint is run directly.

## BUG-DRAFT-dg2-replace-column-attribute-passes-check: `ALTER PAGE … REPLACE` of a DataGrid 2 column with a new `Attribute:` passes `check --references`, then fails mxbuild (2026-10-14)

**Severity:** Low. No damage: `exec.sh`'s mxbuild gate fails and auto-restores the snapshot. The cost is one wasted exec/mxbuild cycle, and the form is tempting because `check` approves it.
**mxcli version:** v0.26.0. **Mendix version:** 11.15.0, macOS.
**Discovered:** 2026-10-14, an Ollama connector app (existing-app mode): one build row swapped the `Attribute` of an existing DG2 column (raw bytes → a formatted String attribute on the same entity, same module). Reported by joshuamoesa (#356).
**Reproducible:** yes, by the reporter: against a snapshot copy, `mxcli check -p <copy>.mpr --references <script>` rejects the SET form and passes the REPLACE form. Not re-probed here.
**Upstream candidate (not filed):** `check --references` should reject a `REPLACE … column` whose `Attribute:` it cannot bind, the same way it already rejects the SET form.

Three forms, as tried (placeholders are the reporter's):

1. `ALTER PAGE … { SET (Attribute: <Attr>) ON <grid> column(<Col>); }` — `check --references` rejects it:
```
column property "Attribute" holds a value of kind Attribute, which ALTER cannot set from a plain value — rewrite the column with CREATE OR REPLACE PAGE instead
```
2. `ALTER PAGE … { REPLACE <grid> column(<Col>) WITH { column (Attribute: <Attr>, Caption: '…') }; }` — `check` prints `Check passed!`, `exec` applies it, and mxbuild fails with two errors on `Property 'Columns/3/Attribute'` of the grid:
```
An attribute is required when 'Show' is set to 'Attribute'. Select the 'Attribute' property for column (<Col>)
```
plus the sorting variant of the same error. `exec.sh` reported `Auto-restored from: .mpr-snapshots/<ts>/`.
3. **Workaround (worked in the field):** re-emit the whole page with `create or modify page`, the column carrying the new attribute.

**Rule:** any change to an existing DG2 column — insert, re-attribute, re-sort — is a whole-page re-emit, never `ALTER PAGE` (`skills/learned-dg2-patterns.md` rule 1; `skills/learned-detection-gaps.md` register). Related: BUG-96 (cross-module column insert via `ALTER PAGE`, which corrupts).
1 change: 1 addition & 0 deletions skills/close-the-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ topics at once.
| A lesson that transfers to another Mendix project | the shared toolkit's `skills/` | Still true with a different client, different module, different .mpr |
| A confirmed, reproducible tool defect | `bug-logs/mxcli-bugs.md` | You could hand the repro to the tool's author |
| A P1/P2 finding from `module-review.md`, `process-coherence-pass.md`, `monkey-test.md`, or `wiring-sweep.md` | `docs/improvement-register.md` (`improvement-register.md`) | You need to know later whether this defect class recurred — a single pass's own report cannot answer that |
| Re-shot screenshots for some pages of a module whose LOOK report already exists | that report, as an appended addendum: its own `VALID AT:` and `PROOF-OF-LOOK:` lines for the re-shot pages only (`module-review.md` stage 5) | Editing the original section's stamp makes its untouched pages' shots "older than VALID AT", and the obligation check FAULTs the whole report: restore the original stamp if you bumped it |
| How the user wants me to work; project context not derivable from code | memory dir + one line in `MEMORY.md` | It's about the collaboration, not the codebase |
| Where we are, what's next, what's unfiled | `docs/progress/checkpoints.md` | It expires. Nobody will want it in a month |

Expand Down
1 change: 1 addition & 0 deletions skills/learned-detection-gaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ verification rungs form a ladder, and a green result only certifies what that ru
| A **workflow branch point** in a `create workflow` script: a `DECISION`, or a call-microflow task with Boolean or enumeration outcomes (both go through the same outcome writer) | rungs 1–4, lint, **and rung 6 within one deploy**: every journey that starts and finishes an instance passes | rung 6 **across a restart or redeploy**. An instance left paused after the decision comes back `Incompatible` (*"A selected outcome has been replaced in the already executed path"*), even when the restart changes nothing in the model | mxcli writes condition outcomes with no `PersistentId`, so the runtime gives them a new identity on every load. There is no MDL fix. Once per workflow, probe it: start an instance, let it stop at the first task after a decision, restart the app, read the instance's state. Until upstream fixes it, the app's workflow admin page must list and handle `Incompatible` instances. Note what the ladder cannot express here: the defect is not in any one deploy, it is *between* two | BUG-141 |
| `create import mapping` array-to-child binding | rungs 1–4; `DESCRIBE IMPORT MAPPING` looks correct | live retrieve after a real `import from mapping`: child list empty | Unverified-until-proven-live; severity not yet classified — read BUG-99's hold before blaming mxcli | BUG-99 |
| Cross-module `ALTER PAGE ... INSERT` of a DG2 column | rungs 1–3 (`DESCRIBE` *omits* the malformed column) | rung 5: Studio Pro loader `InvalidCastException`; `mx check` also crashes | Forbidden construct; recovery is `create or replace page` | BUG-96 |
| `ALTER PAGE ... REPLACE` of an existing DG2 column with a new `Attribute:` (same module). The `SET (Attribute: …)` form is refused by `check` | rungs 1–2: `check --references` prints "Check passed!", exec applies it | rung 4: mxbuild, "An attribute is required when 'Show' is set to 'Attribute'" plus its sorting variant | Any change to a DG2 column is a whole-page `create or modify page` re-emit (`learned-dg2-patterns.md` rule 1) | BUG-DRAFT-dg2-replace-column-attribute-passes-check; an Ollama connector app, 2026-10-14 |
| An `if` with no `else` inside an `on error { }` handler body | rungs 1–3 (mxcli v0.24.0) | mxbuild: CE0079 / CE0773 — the `if`'s false flow is not configured | Keep handler bodies straight-line calls; move the branching into a sub-microflow the handler calls | card-disbursement requirements-driven build, 2026-09-26 |
| An entity, attribute or Java action parameter named with a Mendix-reserved word — `Case` failed as both an entity and a Java action parameter; `Context` as an attribute | rungs 1–3 (mxcli v0.24.0) | mxbuild: CE7247 | Qualify the name (`CustomerCase`, `ApprovalContext`). The reserved list is Mendix's, not MDL's, so quoting does not help — mxcli's bundled `check-syntax` skill lists what it knows (`syntax domain-model keywords` lists MDL's keywords, not these) | card-disbursement requirements-driven build, 2026-09-26; BUG-149 |
| A data widget on a page some role can open without read access to the widget's entity — even in a section that role never sees (BUG-94: no role-scoped visibility) | rungs 1–3, **and a bare `mx check` on a scratch copy** (cause of the difference not isolated) | exec.sh's mxbuild gate: CE2729 per widget attribute | Give that role read under an XPath no row satisfies (`[Assoc = empty and Assoc != empty]`), or move the widget to a page only the reading roles open. A clean scratch probe is not a pass for pages with security-scoped widgets — gate on the real exec | card-disbursement requirements-driven build, 2026-09-26 |
Expand Down
7 changes: 4 additions & 3 deletions skills/learned-dg2-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ inserts corrupt too — BUG-96). So:

**Decision rule**

1. **Never insert columns into any grid via `ALTER PAGE`.** New/changed columns go through
`CREATE OR REPLACE PAGE` with the full tree written out. This retires the reason for the
"never native DATAGRID" ban.
1. **Never insert, replace or re-attribute columns in any grid via `ALTER PAGE`.** New/changed
columns go through `CREATE OR REPLACE PAGE` with the full tree written out. This retires the
reason for the "never native DATAGRID" ban. A `REPLACE … column` with a new `Attribute:`
passes `check` and fails mxbuild (#356).
2. **Need per-column filters written in MDL → native `DATAGRID`.** `TEXTFILTER` inside a
column `{}` is auto-wired to the column's attribute; the pluggable form's textfilter
binding is not persisted correctly (CE1613).
Expand Down
1 change: 1 addition & 0 deletions skills/learned-mdl-preflight.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ None of these needs a STOP, but each passes `mxcli check` and fails later — at

- **Quote / unquote retry rule:** when any identifier fails at `mxcli check`, `mx check`, or runtime, try the other form before concluding anything. Applies to every identifier position. One form always works; the other may parse and still write wrong BSON.
- **Cross-module association paths need the module prefix on every step:** `$Item/ModA.Item_Other/ModB.OtherEntity/Attr`, not `$Item/Item_Other/Attr`. v0.24.0: `check` and `exec` pass the short form, `mx check` gives `[CE0117] Change object …`. The prefixed form builds at 0 errors. (Association-step rules: `learned-microflow-patterns.md`.)
- **Changing an existing DataGrid 2 column (insert or new `Attribute:`) is a whole-page re-emit, never `ALTER PAGE`.** v0.26.0: `REPLACE … column` passes `check`, fails mxbuild (`learned-dg2-patterns.md` rule 1).
- **Enumeration values named with reserved words** (`Receive`, `Move`, `Count`, `Open`, …) cannot be targeted by `alter enumeration … modify value` — v0.24.0 gives a parse error (`mismatched input 'Receive' expecting IDENTIFIER`), quoted or not, before any write. Use `create or modify enumeration` — which **replaces** the enum, so restate the full value set and the doc comment. Change captions only; renaming a value NAME silently breaks every reference and `mxcli check` will not say so.
- **Grants are co-located with the element, never deferred to one `security.mdl`:** `grant execute` ends the microflow script, `grant view` the page script, entity access the domain script — the entity and its access rule are one unit, same script, same commit. mxbuild raises no error for a missing grant; the only symptom is a blank screen or CE2729 once a non-admin role opens the page. A `roles.mdl` that only *creates* roles is fine. Pre-flight per entity: access rule in the same script? every module role that needs it granted? every module role composed into a user role? non-persistent DTOs — read/write only (CONV006)?
- **Demo users:** one throwaway policy-compliant password per project, recorded in the build plan (`brd-to-build-plan.md` Step 7). Never wipe, reset or re-create MxAdmin (ships with password `1`).
Expand Down
3 changes: 2 additions & 1 deletion skills/module-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ the render was broken) becomes mechanically impossible to repeat, not just prohi
A report may carry addenda: each re-look adds its own `VALID AT:` and its own `PROOF-OF-LOOK:`
lines below it. Each citation is checked against the nearest stamp above it, and the report is
stale only when the model has been built past its newest stamp (by commit time). Keep earlier
sections; do not delete their proof lines.
sections; do not delete their proof lines. Stamp an addendum with the model commit its shots were
taken on, never a later commit that only adds the screenshots or the report: that commit postdates the shots.
Reports dated before 2026-08-23 predate the rule and are left alone.

**A LOOK report expires when the module changes.** Any BUILD/GATE commit that touches the module
Expand Down
Loading
Loading