Skip to content

Commit 9623ccb

Browse files
authored
Merge pull request #313 from hjotha/submit/validate-skip-excluded-microflows
fix: skip reference checks for excluded microflows
2 parents ac2a5bc + 8a29914 commit 9623ccb

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
-- ============================================================================
2+
-- Bug #312: Reference validation flagged broken calls inside excluded microflows
3+
-- ============================================================================
4+
--
5+
-- Symptom (before fix):
6+
-- `mxcli check --references` walked every microflow body — including ones
7+
-- marked `@excluded` — and reported missing microflow / page / java-action
8+
-- references in them. Excluded documents are not part of the build, and
9+
-- Studio Pro tolerates dangling references in them, so this produced
10+
-- false positives during agentic workflows that legitimately stash
11+
-- broken intermediate state inside excluded scaffolding.
12+
--
13+
-- Root cause:
14+
-- The validator collected references from every CreateMicroflowStmt
15+
-- without checking the Excluded flag.
16+
--
17+
-- After fix:
18+
-- `validate.go` skips reference collection for excluded microflows.
19+
-- Included microflows are still validated normally, so the negative
20+
-- case (typo in a real call) still fails the check.
21+
--
22+
-- Usage:
23+
-- mxcli check --references mdl-examples/bug-tests/312-validate-skip-excluded-microflows.mdl -p app.mpr
24+
-- The check must succeed: the excluded microflow's broken call to
25+
-- BugTest312.NoSuchTarget is ignored.
26+
--
27+
-- To verify the negative case still triggers, remove `@excluded` from
28+
-- the second microflow and re-run — `mxcli check --references` should
29+
-- then report the missing reference.
30+
-- ============================================================================
31+
32+
create module BugTest312;
33+
34+
create microflow BugTest312.MF_RealTarget ()
35+
returns string as $msg
36+
begin
37+
return 'real target';
38+
end;
39+
/
40+
41+
-- Excluded scaffolding that contains a broken call. mxcli check --references
42+
-- must NOT report this as an error.
43+
@excluded
44+
create microflow BugTest312.MF_ExcludedWithBrokenCall ()
45+
returns string as $msg
46+
begin
47+
declare $msg string = empty;
48+
$msg = call microflow BugTest312.NoSuchTarget();
49+
return $msg;
50+
end;
51+
/
52+
53+
-- Negative control: an INCLUDED microflow whose call is valid. Together with
54+
-- the excluded one above, the check confirms the validator still walks
55+
-- non-excluded microflows.
56+
create microflow BugTest312.MF_IncludedValid ()
57+
returns string as $msg
58+
begin
59+
declare $msg string = empty;
60+
$msg = call microflow BugTest312.MF_RealTarget();
61+
return $msg;
62+
end;
63+
/

mdl/executor/bugfix_regression_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,3 +696,68 @@ func TestCallMicroflowUnknownResultTypeStillDeclaresVariable(t *testing.T) {
696696
t.Fatal("expected Result to remain declared after unresolved call return type")
697697
}
698698
}
699+
700+
func TestValidateMicroflowReferencesSkipsExcludedMicroflow(t *testing.T) {
701+
moduleID := model.ID("module-1")
702+
backend := &mock.MockBackend{
703+
IsConnectedFunc: func() bool { return true },
704+
ListModulesFunc: func() ([]*model.Module, error) {
705+
return []*model.Module{{
706+
BaseElement: model.BaseElement{ID: moduleID},
707+
Name: "SyntheticAudit",
708+
}}, nil
709+
},
710+
ListMicroflowsFunc: func() ([]*microflows.Microflow, error) {
711+
return nil, nil
712+
},
713+
}
714+
ctx, _ := newMockCtx(t, withBackend(backend))
715+
716+
stmt := &ast.CreateMicroflowStmt{
717+
Excluded: true,
718+
Name: ast.QualifiedName{Module: "SyntheticAudit", Name: "ExcludedLegacyFlow"},
719+
Body: []ast.MicroflowStatement{
720+
&ast.CallMicroflowStmt{
721+
MicroflowName: ast.QualifiedName{Module: "SyntheticAudit", Name: "DeletedScaffoldFlow"},
722+
},
723+
},
724+
}
725+
726+
if err := validate(ctx, stmt); err != nil {
727+
t.Fatalf("excluded microflow reference validation returned error: %v", err)
728+
}
729+
}
730+
731+
func TestValidateMicroflowReferencesReportsIncludedMissingMicroflow(t *testing.T) {
732+
moduleID := model.ID("module-1")
733+
backend := &mock.MockBackend{
734+
IsConnectedFunc: func() bool { return true },
735+
ListModulesFunc: func() ([]*model.Module, error) {
736+
return []*model.Module{{
737+
BaseElement: model.BaseElement{ID: moduleID},
738+
Name: "SyntheticAudit",
739+
}}, nil
740+
},
741+
ListMicroflowsFunc: func() ([]*microflows.Microflow, error) {
742+
return nil, nil
743+
},
744+
}
745+
ctx, _ := newMockCtx(t, withBackend(backend))
746+
747+
stmt := &ast.CreateMicroflowStmt{
748+
Name: ast.QualifiedName{Module: "SyntheticAudit", Name: "IncludedFlow"},
749+
Body: []ast.MicroflowStatement{
750+
&ast.CallMicroflowStmt{
751+
MicroflowName: ast.QualifiedName{Module: "SyntheticAudit", Name: "DeletedScaffoldFlow"},
752+
},
753+
},
754+
}
755+
756+
err := validate(ctx, stmt)
757+
if err == nil {
758+
t.Fatal("expected missing microflow reference error")
759+
}
760+
if !strings.Contains(err.Error(), "microflow not found: SyntheticAudit.DeletedScaffoldFlow") {
761+
t.Fatalf("unexpected validation error: %v", err)
762+
}
763+
}

mdl/executor/validate.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,12 @@ func validateMicroflowReferences(ctx *ExecContext, s *ast.CreateMicroflowStmt, s
396396
if !ctx.Connected() || len(s.Body) == 0 {
397397
return nil
398398
}
399+
if s.Excluded {
400+
// Studio Pro allows excluded documents to keep stale references. Reference
401+
// checks should not fail a roundtrip audit for microflows that are not part
402+
// of the runnable app.
403+
return nil
404+
}
399405

400406
// Collect all references from the microflow body
401407
refs := &microflowRefCollector{}

0 commit comments

Comments
 (0)