| name | mx-page-clone-from-template |
| description | GATED WRITE. Clone an existing well-built Mendix page (ideally a gold page) to a new name with optional entity rebinding, via the mxcli MDL round-trip. Routes through the shadow-write engine (7 safety pillars; live .mpr untouched until atomic swap). Pre-classifies the source page as SIMPLE / COMPLEX / UNCLONEABLE based on parser-incompatible patterns observed 2026-04-27. Refuses UNCLONEABLE; requires --allow-lossy on COMPLEX. Auto-emits a /mx-handoff sheet for any widgets stripped during the clone. Invocable as /mx-page-clone-from-template <Source.Page> <Target.Page>. |
mx-page-clone-from-template
Lowest-risk page-creation skill. Cloning preserves OS2 classes, layout, design-property bindings, and accessibility patterns automatically — all the things a hand-rolled scaffold would get wrong.
North star
Reuse beats reinvent. Clone a proven page; don't generate one.
Per MISSION.md: "Reuse over reinvent. Existing well-built page > clone with rebind > generate from spec > author from scratch." This skill is rung 2 of that ladder.
Hard rules
- NO git on the Mendix project. Recovery is via shadow-write + Pillar 2 byte-level snapshot. See
~/.claude/rules/mendix-safety.md.
- Refuse on non-allowlisted projects. Per
~/.claude/rules/mendix-testbed.md.
- Refuse on UNCLONEABLE source pages. See "Source classification" below.
- Refuse on COMPLEX source pages without
--allow-lossy. When passed, the skill MUST auto-emit a /mx-handoff sheet listing every stripped widget so the human can re-graft them in Studio Pro.
- Hard prerequisite: a fresh
/mx-discover inventory (≤24h old) for the target module so we can detect collisions on the new page name.
- Gold-page preference: if the source isn't on
mendix-context.gold_pages, warn but allow. Reusing a gold page is strictly preferred.
- No bulk operations. One clone per invocation. No "clone these five pages."
When to invoke
Auto-invoke when:
- A user describes a new page that is "like the existing X but for entity Y" — that's literally the clone-with-rebind use case.
- Inside a multi-step workflow (e.g., a page-tier flow from
WORKFLOWS.md).
Do not invoke when:
- The source page would need substantial structural change. Cloning is for rebind, not refactor.
- The user wants a page from scratch from a feature spec — use
/mx-page-scaffold (when built).
What it does — the full pipeline
Phase 0 — Preflight (read-only)
- Resolve project root. Walk up from cwd until
*.mpr is found.
- Allowlist check. Per
mendix-testbed.md. Refuse with the standard testbed-refusal block on miss.
- Studio Pro lock check.
Get-Process studiopro — if title matches the project, refuse and suggest /close-studio-pro <project>.
- Resolve
mx.exe. Per the updated /mx-version skill — probe C:\Program Files\Mendix\<v>\modeler\, C:\DevTools\Mendix\<v>\modeler\, and the running studiopro path.
/mx-discover freshness check. Confirm there's a fresh inventory for the target module (so we can verify the new page name doesn't collide with an existing page).
- Gold-page check. Call
mendix-context.gold_pages(project); flag if source page is NOT registered.
Phase 1 — Source extraction (read-only)
mxcli describe page <Source.Page> with stderr separated:
mxcli -p $proj describe page Module.Source 1>$scratch/source-described.mdl 2>$scratch/describe.stderr
The "vibe-coded PoC" warning lands in stderr; if it ends up on stdout (line 1 of the file), the parser breaks.
Phase 2 — Source classification
-
Scan the described MDL for parser-incompatible patterns AND for non-bundled pluggable widgets (see ~/.claude/wiki/pages/mendix-cli/clone-page-via-mdl.md for the full failure analysis):
- Bare-keyword native widgets (e.g.
BLUEMATRIXREACTNAVSELECTOR reactNavSelector1). Pattern: line starts with an UPPERCASE-only token followed by an identifier, not preceded by PLUGGABLEWIDGET. The grammar rejects these.
- Comment-placeholder widgets (e.g.
-- Forms$TabControl (tabContainer1) inside braces). Pattern: line starting with -- inside any container scope.
- Inner DataViews without DataSource (e.g.
DATAVIEW dvSomething { ... } with no (DataSource: ...) property). The reference checker flags these post-strip.
- Non-bundled pluggable widgets (NEW 2026-04-28). Any
PLUGGABLEWIDGET 'fully.qualified.id' ... whose ID is NOT in mxcli's bundled template set. The bundled set at v0.7.0 is approximately 30 stock widgets (combobox, datagrid, gallery, image, datagrid-*-filter, etc. — see mxcli extract-templates output for the exact list). Custom-vendor pluggables like bluematrix.widget.fancybutton.FancyButton will fail mxcli exec with template not found: <NAME>. mxcli widget init writing .def.json files does NOT fix this — those are MDL parser hints, not the BSON templates exec reads. See ~/.claude/wiki/pages/mendix-cli/clone-page-via-mdl.md for the 3-tier lookup explanation.
-
Classify:
- SIMPLE = 0 parser-incompatible patterns AND 0 non-bundled pluggables. Proceed via shadow-write exec.
- COMPLEX = ≥1 parser-incompatible pattern (lines we can strip + handoff back). Require
--allow-lossy. Strip the lines, exec the rest, emit a /mx-handoff sheet.
- EXEC-INCOMPATIBLE = ≥1 non-bundled pluggable widget.
exec is a known v0.7.0 limitation; attempting it wastes 8-12 min per cycle on shadow-discard. Skip exec entirely; emit a full /mx-handoff sheet (the cloned MDL serves as the spec the human pastes into Studio Pro). Pivot to a handoff-only flow until mxcli ships first-class project-template support OR Neo manually extract-templates + copies to <project>/.mxcli/widgets/<name>.json (bleeding-edge BSON path; CE0463 risk; not the supported flow). Reverify on every mxcli upgrade.
- UNCLONEABLE = the source is mostly third-party widgets, OR the structural pattern count exceeds
floor(line_count * 0.30) (a heuristic for "too lossy to be useful"). Refuse with explicit reason.
Phase 3 — Edit
-
Strip the unparseable lines (only on COMPLEX; SIMPLE has zero):
- Remove
BLUEMATRIXREACTNAVSELECTOR-style bare-keyword lines.
- Remove
-- comment-placeholder lines inside braces.
- Track exactly what was stripped for the handoff sheet.
-
Text-transform Module.Source → Module.Target throughout the file. Also rename URL slug if present (e.g. 'contentportal' → 'contentportalv2' based on a deterministic slug rule). Keep GRANT VIEW ON PAGE clauses targeting the new page.
-
Pre-validate (read-only) against the LIVE project:
mxcli check $scratch/clone.mdl -p $proj --references --format json
Don't fail-fast on every reference error — some are spurious for clones (the new page's name will trigger "doesn't exist" until exec runs). Inspect the JSON output: only block on errors that aren't "target page doesn't exist yet" or similar self-referential.
Phase 4 — Apply (route depends on classification)
For SIMPLE classification (only bundled pluggables; ~30 stock widget IDs):
13a. Route through the shadow-write engine for actual exec. This is non-negotiable. The script-block MUST do TWO operations against the shadow, in order:
1. mxcli widget init -p $ShadowMpr — registers .def.json so the MDL parser recognizes pluggable widget IDs in the source MDL. Without this, parse fails with "no definition for widget X (run 'mxcli widget init -p app.mpr')". Discovered 2026-04-27.
2. mxcli exec $cloneMdl -p $ShadowMpr — applies the clone MDL using bundled BSON templates (sufficient for SIMPLE classification because all referenced widgets are bundled).
For EXEC-INCOMPATIBLE classification (any non-bundled pluggable like FANCYBUTTON):
13b. Skip exec entirely. mxcli exec will fail with "template not found: " because the BSON templates for non-bundled pluggables aren't in the v0.7.0 binary's embedded set, AND mxcli widget init does NOT fix this — those .def.json files are MDL parser hints, not the BSON templates exec reads. Discovered 2026-04-28 via mendix-expert agent investigation. Instead:
- Treat the cloned MDL as the SPEC the human will recreate in Studio Pro.
- Auto-emit a full /mx-handoff sheet listing: target page name, source page reference, exact OS2 classes from the source (validated via validate_classes), exact widget tree, and the manual Studio Pro steps (right-click → Duplicate Page → rename → rebind entity).
- Skip Phases 5+6 (no shadow-write means nothing to audit yet; the audit happens after the human reports done).
For COMPLEX classification (parser-incompatible lines + only bundled pluggables):
13c. Strip the parser-incompatible lines, run 13a, AND emit a /mx-handoff for the stripped widgets to be re-grafted manually.
Prefer a tiny per-attempt .ps1 wrapper over an inline script-block — nested pwsh -Command escaping mangles error messages and makes failures harder to read. Pattern:
powershell # sandbox/<run-id>/clone-attempt.ps1 param([Parameter(Mandatory)][string]$ShadowMpr) $ErrorActionPreference = 'Stop' & mxcli widget init -p $ShadowMpr if ($LASTEXITCODE -ne 0) { throw "widget init failed with $LASTEXITCODE" } & mxcli exec $cloneMdl -p $ShadowMpr if ($LASTEXITCODE -ne 0) { throw "exec failed with $LASTEXITCODE" }
Then call:
powershell Invoke-MendixShadowWrite -ProjectRoot $proj -OperationName "clone-$Source-to-$Target" -MxBinary $mx -Script { param($ShadowMpr) & "$scratch\clone-attempt.ps1" -ShadowMpr $ShadowMpr }
The engine handles Pillars 1-7. The user-supplied script runs against the SHADOW only; live .mpr is untouched until shadow validates.
- If the engine reports BLOCKED at any pillar, surface the failure block to the user and stop. The Pillar 2 snapshot path will be in the report; the live project is untouched.
Phase 5 — Post-clone verification (after shadow swap)
-
Run /mx-page-audit Module.Target automatically. The cloned page must pass with zero BLOCKs. If a BLOCK appears (hallucinated class, SmartHub residue), surface it but DO NOT auto-restore — the audit BLOCK is informational; the .mpr is healthy. The user can decide to keep, delete, or fix.
-
Skip Playwright smoke by default. The testbed runtime needs a Studio Pro reload to expose new pages, and /mx-test-page against the root URL fails the fingerprint guard anyway (see mx-test-page "Root-URL gotcha" section). Note this in the report.
Phase 6 — Reporting
## /mx-page-clone-from-template: SUCCESS | BLOCKED | UNCLONEABLE
**Source:** <Module.Source> (gold-page: yes/no)
**Target:** <Module.Target>
**Classification:** SIMPLE | COMPLEX (allow-lossy) | UNCLONEABLE
**Pillar 2 snapshot:** ~/.claude/backups/<project>/<ts>/<project>.mpr
**In-project mpr backup:** <path>.bak.<ts>
**mx check:** baseline N E/W -> post N E/W (no regression | regression detected)
**/mx-page-audit verdict:** READY-TO-MERGE | NEEDS-WORK (link to audit findings)
**Stripped widgets (COMPLEX only — re-graft via /mx-handoff below):**
- <widget-class> <name> (line N of source)
- ...
**Studio Pro post-step:**
1. Reopen TestOSApp3 in Studio Pro.
2. Navigate to <Module.Target> in the project tree.
3. Re-graft the stripped widgets per the /mx-handoff sheet (auto-emitted above for COMPLEX clones).
4. Save.
5. Optionally restart the runtime to expose the new page on http://localhost:8082/p/<target-slug>.
**Next step:** <e.g., "/mx-test-page http://localhost:8082/p/<target-slug>" once Studio Pro reload is done>
Args
--allow-lossy — proceed on a COMPLEX classification. Required when the source has any parser-incompatible widgets.
--target-url <slug> — override the auto-derived URL slug. Default: <source-slug>v2 if source had a slug.
--no-audit — skip the post-clone /mx-page-audit (only use when chaining into a follow-up audit yourself).
Failure modes & how the skill handles them
| Failure | Pillar | Recovery |
|---|
| Studio Pro open on project | 1 | Skill refuses; suggest /close-studio-pro <project>. |
| Project allowlist miss | 0 (allowlist) | Skill refuses with standard testbed-refusal block. |
| Source page is UNCLONEABLE | classification | Skill refuses with explicit "too many parser-incompatible patterns" reason. |
Source page is COMPLEX, no --allow-lossy | classification | Skill refuses; reports specific patterns + invites --allow-lossy. |
mxcli check --references blocks (non-spurious) | pre-exec | Skill refuses; reports the reference errors + suggests fixes. |
mxcli exec against shadow fails | 5/6 | Engine discards the shadow; live .mpr untouched. |
Shadow mx check regresses | 6 | Engine discards the shadow; live untouched. |
| Atomic swap fails mid-write | 7 (catastrophic) | Engine restores live from Pillar 2 snapshot; reports the location. |
Post-clone /mx-page-audit flags BLOCK | post | Surface to user; do NOT auto-restore. The .mpr is healthy; the page just has a doctrine issue worth fixing manually. |
Cross-reference
- Sandbox findings:
~/.claude/wiki/pages/mendix-cli/clone-page-via-mdl.md (full round-trip failure analysis from 2026-04-27).
- Safety pillars:
~/.claude/rules/mendix-safety.md (the 7 pillars).
- Shadow-write engine:
~/.claude/lib/Invoke-MendixShadowWrite.ps1 (the implementation).
- Testbed allowlist:
~/.claude/rules/mendix-testbed.md.
- OS2 doctrine:
~/.claude/rules/mendix-os2.md.
- Gold pages registry:
mendix-context.gold_pages MCP tool.
- Post-clone audit:
/mx-page-audit.
- Page rendering test:
/mx-test-page (with the root-URL gotcha caveat).
- Companion skills (planned):
/mx-page-scaffold (Skill 2), /mx-page-from-prototype (Skill 3).