Skip to content
Merged
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
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
64 changes: 64 additions & 0 deletions mdl-examples/bug-tests/330-preserve-loop-body-annotations.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
-- ============================================================================
-- Bug #330: Annotations inside loop bodies disappeared after describe/exec
-- ============================================================================
--
-- Symptom (before fix):
-- `@annotation 'note'` attached to a statement nested inside `loop ... end loop;`
-- (or `while ... end while;`) survived the first execution but disappeared
-- on the next describe → exec roundtrip. The annotation existed in the
-- loop's local object collection but the parent microflow graph never
-- saw it, so the next describer pass dropped it. Annotations on nested
-- decisions (IF inside a LOOP) were the most visible casualty — the
-- notes that explain WHY a loop exists were silently lost.
--
-- Root cause:
-- The microflow builder copied nested loop sequence flows back to the
-- parent graph but did not copy nested ANNOTATION flows. The describer
-- then collected annotation captions only from the top-level object
-- collection, ignoring captions stored inside nested loop collections.
--
-- After fix:
-- - Builder: loop/while statement handlers also copy
-- `nested.AnnotationFlows` into the parent graph.
-- - Describer: `collectAnnotationCaptions` walks recursively into
-- nested loop object collections, and `emitLoopBody` merges the
-- loop-local annotation map into the per-body traversal.
--
-- Scope note:
-- The Go-side regression `TestLoopBodyIfAnnotationPromotedToParentFlows`
-- covers the AST→BSON build path. This MDL script reproduces the
-- describer side: after exec, the annotation must appear in the
-- describe output. A full describe → exec → describe FIXPOINT for
-- nested IF inside LOOP additionally depends on the nearest split-merge
-- pairing fix (issue #326 / PR #327); on a branch that includes both
-- fixes, this script round-trips cleanly.
--
-- Usage:
-- mxcli exec mdl-examples/bug-tests/330-preserve-loop-body-annotations.mdl -p app.mpr
-- mxcli -p app.mpr -c "describe microflow BugTest330.MF_LoopWithAnnotations"
-- The describe output must contain `@annotation 'note on nested if'`
-- on the IF inside the loop body.
-- ============================================================================

create module BugTest330;

create entity BugTest330.Item (
Name : string(100)
);
/

-- LOOP body containing an annotated IF — the case that originally lost the
-- annotation. The note on the IF must appear in the describe output.
create microflow BugTest330.MF_LoopWithAnnotations (
$Items: list of BugTest330.Item
)
begin
loop $Item in $Items
begin
@annotation 'note on nested if'
if $Item/Name != empty then
log info node 'BugTest330' 'has name';
end if;
end loop;
end;
/
Loading
Loading