Skip to content
Open
17 changes: 17 additions & 0 deletions .claude/skills/mendix/write-microflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,23 @@ end case;

`(empty)` represents an unset enumeration value. Multiple values can share one `when` branch by separating them with commas. Case values are bare identifiers — do **not** quote them.

### Type Split And Cast Statements

Use `split type` when a microflow branches on an object's runtime specialization.
Use `cast` inside a type branch to create the specialized variable used by the branch body.

```mdl
split type $Input
case Sample.SpecializedInput
cast $SpecificInput;
return true;
else
return false;
end split;
```

`case` values are qualified entity names. The optional `else` branch handles objects that do not match any listed specialization.

### LOOP Statements

```mdl
Expand Down
6 changes: 6 additions & 0 deletions cmd/mxcli/tui/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"
"path/filepath"
"sync"
"sync/atomic"
"time"

tea "github.com/charmbracelet/bubbletea"
Expand Down Expand Up @@ -78,6 +79,7 @@ func newWatcher(mprPath, contentsDir string, sender MsgSender) (*Watcher, error)

func (w *Watcher) run(sender MsgSender) {
var debounceTimer *time.Timer
var debounceSeq atomic.Uint64

for {
select {
Expand Down Expand Up @@ -110,7 +112,11 @@ func (w *Watcher) run(sender MsgSender) {
if debounceTimer != nil {
debounceTimer.Stop()
}
seq := debounceSeq.Add(1)
debounceTimer = time.AfterFunc(watchDebounce, func() {
if debounceSeq.Load() != seq {
return
}
sender.Send(MprChangedMsg{})
})

Expand Down
5 changes: 3 additions & 2 deletions cmd/mxcli/tui/watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ func TestWatcherDebounce(t *testing.T) {
}
defer w.Close()

// Rapidly write 5 times — should debounce into a single message
// Rapidly write 5 times — should debounce into a single message.
// Keep the burst tighter than the debounce window so slow CI machines do
// not accidentally let an intermediate timer fire.
for i := range 5 {
_ = os.WriteFile(unitFile, []byte{byte('a' + i)}, 0644)
time.Sleep(50 * time.Millisecond)
}

// Wait for debounce to fire (500ms + margin)
Expand Down
2 changes: 2 additions & 0 deletions docs/01-project/MDL_QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ authentication basic, session
| Free annotation | `@annotation 'text'` before `@position(...)` | Free-floating visual note preserved by order |
| IF | `if condition then ... [else ...] end if;` | |
| Enum split | `case $Var when Value then ... end case;` | Enumeration decision branches |
| Type split | `split type $Var case Module.Entity ... end split;` | Runtime specialization branches |
| Cast | `cast $SpecificVar;` | Downcast inside a type split branch |
| LOOP | `loop $item in $list begin ... end loop;` | FOR EACH over list |
| WHILE | `while condition begin ... end while;` | Condition-based loop |
| Return | `return $value;` | Required at end of every flow path |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Proposal: Microflow Inheritance Split And Cast Statements

Status: Draft

## Summary

Add round-trip MDL support for type-based microflow decisions and cast actions:

```mdl
split type $Input
case Sample.SpecializedInput
cast $SpecificInput;
else
return false;
end split;
```

## Motivation

Studio Pro represents specialization/type decisions as `InheritanceSplit` objects and stores downcasts as `CastAction` activities. Without first-class MDL statements, `describe` can only emit unsupported comments or incomplete split output, and `exec` cannot rebuild the same graph.

## Semantics

`split type $Var` evaluates the runtime specialization of an object variable. Each `case Module.Entity` branch corresponds to an outgoing sequence flow with an `InheritanceCase`. The optional `else` branch maps to the outgoing flow without an inheritance case.

`cast $Output` emits a `CastAction` that produces the downcast variable. `$Output = cast $Input` is accepted for source-preserving authoring, but current Mendix BSON stores the generated cast variable as the primary persisted field.

## Tests And Examples

`mdl-examples/doctype-tests/inheritance_split_statement.test.mdl` demonstrates the syntax. Go regression tests cover parser construction, builder output, describer output, validation recursion, and BSON writer support for inheritance case values and cast actions.

## Open Questions

- Should `exec` validate `case Module.Entity` against the project's specialization hierarchy when connected?
- Should the source-preserving `$Output = cast $Input` form round-trip both variable names once the underlying BSON fields are confirmed for all supported Mendix versions?
1 change: 1 addition & 0 deletions docs/11-proposals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ BSON schema Registry ◄──── multi-version Support
| [XPath Gaps](xpath-gaps-proposal.md) | Partial | XPath constraint support gap analysis. ~85% complete, association paths and nested predicates remain | — |
| [Microflow ENUM SPLIT Statement](PROPOSAL_microflow_enum_split_statement.md) | Implemented | Enumeration decision splits via `case $Var when Value then … end case;` | — |
| [Microflow CHANGE Refresh Modifier](PROPOSAL_microflow_change_refresh_modifier.md) | Draft | Preserve `RefreshInClient` on change-object actions | — |
| [Microflow Inheritance Split And Cast Statements](PROPOSAL_microflow_inheritance_split_statement.md) | Draft | Preserve type-based microflow decisions and cast actions in round-trips | — |
| [LLM MDL Assistance](PROPOSAL_llm_mdl_assistance.md) | Proposed | Enhanced error messages with examples, reorganized skills by use case | — |

### Testing & Evaluation
Expand Down
Loading