Skip to content

feat(grammar)!: require semicolons on microflow and nanoflow statements - #66

Merged
ako merged 1 commit into
mainfrom
claude/microflow-mandatory-semicolons
Aug 1, 2026
Merged

feat(grammar)!: require semicolons on microflow and nanoflow statements#66
ako merged 1 commit into
mainfrom
claude/microflow-mandatory-semicolons

Conversation

@ako

@ako ako commented Aug 1, 2026

Copy link
Copy Markdown
Owner

BREAKING CHANGE. Every statement inside a microflow or nanoflow body must now end with ;, including block terminators (end if;, end loop;, end while;, end case;). Omitting one is a parse error where it was previously accepted.

Why

Semicolons were optional on all 54 body-statement alternatives, which leaves statement boundaries ambiguous whenever a keyword can both continue a statement and start one. The concrete case that surfaced it — adding a trailing commit modifier to create/change for mendixlabs#779:

$a = create Mod.E
commit $b;

ANTLR is greedy, so commit binds to the create rather than starting a commit activity. Diagnosing that per keyword does not scale; requiring the terminator removes the class.

Scope

Deliberately limited to microflow/nanoflow bodies. The document terminator (end;, or end followed by /) stays optional, as it is for pages and every other document type.

That boundary is measured, not assumed. Tightening the top-level statement rule instead:

Change pass / fail newly broken
baseline 234 / 39
microflow-body ; mandatory (this PR) 234 / 39 2
top-level ; mandatory 146 / 127 88
top-level: require ; or / 157 / 116 77

Of those 77, 75 are page / snippet / layout and 0 are domain models. Block-bodied documents have never required a terminator, and three styles coexist in the corpus — 168 definitions ending on a bare }, 56 on };, 62 on } then /. Tightening that is a separate and much larger decision, worth an ADR if it is wanted at all.

Migration

2 of 273 example files needed fixing, all genuine omissions:

  end loop        →  end loop;             ledger-52-break-in-conditional
  log trace …''   →  log trace …'';        02-microflow-examples (×3)
  raise error     →  raise error;          inside an on-error block

Example corpus is back at exact baseline parity — 234 pass / 39 fail, 0 regressions.

Test fixtures embedding MDL needed the same treatment: enginecompare (8 — RETURN true ENDRETURN true; END, end loop ENDend loop; END), validate_microflow_hints_test.go (8), visitor_test.go (1).

Docs needed no migration — verified, not assumed

I extracted every self-contained create microflow/nanoflow … end block from .claude/skills/, docs-site/src/ and the quick reference, then parse-checked all of them against both the old and the new binary. Identical results, 0 regressions. (An earlier line-based scan flagged 39 doc lines; those were false positives — multi-line statements whose semicolon sits on a continuation line.)

The rule is now documented in write-microflows.md, write-nanoflows.md and MDL_QUICK_REFERENCE.md, each stating explicitly that the definition terminator remains optional — otherwise agents will "helpfully" add ; after end too.

Diagnostics

Already precise for this case, so no error-reporting work was needed:

line 143:2 missing ';' at 'return'

🤖 Generated with Claude Code

https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA


Generated by Claude Code

BREAKING CHANGE: every statement inside a microflow or nanoflow body must now
end with `;`, including block terminators (`end if;`, `end loop;`, `end while;`,
`end case;`). Omitting one is a parse error, where it was previously accepted.

Semicolons were optional on all 54 body-statement alternatives, which made
statement boundaries ambiguous whenever a keyword can both continue a statement
and start one. The concrete case that surfaced it: adding a trailing `commit`
modifier to create/change for mendixlabs#779 would let

    $a = create Mod.E
    commit $b;

bind `commit` to the create rather than starting a commit activity. Diagnosing
that per keyword does not scale; requiring the terminator removes the class.

Scoped deliberately to microflow/nanoflow BODIES. The document terminator
(`end;` / `end` then `/`) stays optional, as it is for pages and every other
document type — measured, that variant breaks 88 of 234 example files, 75 of
them pages, because block-bodied documents have never required a terminator and
three styles coexist in the corpus (168 bare `}`, 56 `};`, 62 `}` + `/`).
Tightening that is a separate, much larger decision.

Impact measured over all 273 files in mdl-examples: 2 needed fixing, both
genuine omissions (`end loop` with no `;`, a `log` line with no `;`), plus a
`raise error` inside an on-error block. Example corpus is back at baseline
parity (234 pass / 39 fail, 0 regressions). Test fixtures embedding MDL needed
the same treatment in enginecompare, executor and visitor. Doc and skill code
blocks were extracted and parse-checked against both the old and new parser: 0
regressions, so no documentation migration was required.

Diagnostics were already precise for this ("line 143:2 missing ';' at 'return'"),
so no error-reporting work was needed.

Documented in write-microflows.md, write-nanoflows.md and MDL_QUICK_REFERENCE.md,
each stating that the definition terminator remains optional.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
@ako
ako merged commit e0e4883 into main Aug 1, 2026
3 checks passed
ako pushed a commit that referenced this pull request Aug 1, 2026
Resolves the recurring conflict in .claude/skills/fix-issue.md: every fix appends
a symptom row at the same anchor, so any two branches that both add one collide.
Both sides kept — this branch's mendixlabs#812 row plus the two loop rows that arrived from
#63.

Main has since gained #64 (runUpdateWidgets) and #66 (mandatory microflow
semicolons). Parser regenerated against the new grammar; full suite green, and
the example corpus is unchanged at 235/39 including this branch's repro script.
ako pushed a commit that referenced this pull request Aug 1, 2026
Same recurring conflict as #68: .claude/skills/fix-issue.md, where every fix
appends a symptom row at the identical anchor. Both sides kept.

check.go auto-merged cleanly — #64's runUpdateWidgets and this branch's
localMxForVersion touch different functions. Parser regenerated against #66's
grammar; full suite green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants