MoonGuard is a MoonBit public API compatibility and SemVer guard. It compares
two MoonBit interface snapshots, reports public API additions, removals, and
signature changes, then recommends whether the next release should be patch,
minor, or major.
The project is being built for the MoonBit open-source ecosystem competition as
engineering infrastructure for package authors and CI workflows.
Review Materials
The competition submission, validation evidence, design report, compatibility
rules, and package-release checklist are indexed in
docs/README.md. This index is the recommended starting point
for reviewers.
Current Features
Parse public declarations from .mbti-style interface text.
Normalize whitespace and declaration order.
Track public fn, type, typealias, struct, enum, trait, impl,
let, and const declarations.
Track common generated .mbti members including struct fields, enum
constructors, trait methods, generic methods, suberror, and pub using
type or trait exports.
Classify public API changes:
added top-level public API -> minor
added struct fields, enum/suberror constructors, or required trait methods
-> major (conservative)
removed public API -> major
changed signature -> major
unchanged public API -> patch
Render Markdown and JSON compatibility reports for PR comments, release
notes, and downstream tooling.
Validate whether a proposed SemVer bump satisfies the recommended impact for
single-file or package-directory comparisons.
Evaluate compatibility exceptions with auditable policy rules that retain the
original report, accepted changes, reasons, deadlines, match budgets, and
diagnostics. Legacy ignore rules remain available for compatibility.
Share repeated CLI defaults through simple config files.
Compare package directories, detect duplicate symbols and directory
diagnostics, ignore generated/vendor/tool directories, and render snapshot
inventories.
Identify directory APIs by parent-directory package scope plus symbol, so
moving a declaration between .mbti files inside one package is not reported
as an API change while same-named symbols in different packages remain
distinct.
Read .mbti files and directories from the CLI when running on the JS
backend.
Render package release plans that combine API impact, diagnostics, SemVer
validation, and maintainer checklist items for release PRs.
Installation
Install the library from mooncakes.io:
moon add 918154429/moonguard@0.2.0
Install the command-line package:
moon install 918154429/moonguard/cmd/main@0.2.0
For source development, install the MoonBit toolchain, then clone this
repository:
git clone https://github.com/918154429/moonguard.git
cd moonguard
moon check
moon test
moon test --target js
Library Usage
let old_api = "pub fn render(String) -> String"
let new_api = "pub fn render(String, Options) -> String"
let report = @moonguard.diff_interfaces(old_api, new_api)
let markdown = @moonguard.render_markdown_report(report)
The report recommendation is patch, minor, or major.
CLI Usage
Compare two .mbti files with the default Markdown report:
moon run --target js cmd/main -- report fixtures/old.mbti fixtures/new.mbti
The release-plan command is intended for application-facing package workflows:
it prints whether a release is ready, blocked by snapshot diagnostics, or needs
a larger version bump, then includes checklist items for release notes and
migration review.
Ignore files filter report changes without changing the parsed API model:
ignore fn debug_tmp temporary demo API
ignore field Options.experimental
ignore * pkg.generated.mbti::internal_*
The syntax is allow CHANGE_KIND ITEM_KIND NAME [until VERSION] [max_matches N] reason TEXT.... CHANGE_KIND is added, removed,
changed, or *; item kind and name also support * matching. A non-empty
reason is mandatory and max_matches defaults to 1. Expired rules, malformed
rules, missing policy versions, and exceeded budgets fail closed with exit code
2. Unmatched and overlapping rules remain visible as warnings.
Policy output preserves both original_report and effective_report, plus the
accepted changes and rule diagnostics. check, check-dir, and release-plan
use --next as the policy version when --policy-version is omitted.
--ignore-file and --policy-file are mutually exclusive, and
inventory-dir does not accept policy files.
File and directory commands currently require the JS backend because the CLI
uses Node fs.readFileSync and directory APIs through MoonBit JS externs.
For strict CI exit-code checks, run the generated JS with Node directly after a
JS-target command has built it:
MoonGuard can compare a committed release baseline with interfaces generated in
a pull request, fail on an insufficient version bump, and upload a JSON release
plan. See docs/github-actions.md for a complete
downstream workflow pinned to the v0.1.0 release.
Compatibility Rules
MoonGuard starts with conservative rules:
Removing a public declaration is a breaking change.
Changing a public signature is a breaking change.
Adding an ordinary top-level public declaration is minor-compatible.
Adding a struct field, enum/suberror constructor, or required trait method is
conservatively breaking because it can invalidate construction, exhaustive
matching, or existing trait implementations.
Reordering declarations, changing comments, or changing whitespace does not
affect the public API model.
The parser intentionally covers high-frequency .mbti declarations first,
including common nested members from generated interface files. Any
unrecognized pub line is retained as unknown so that public surface changes
remain visible instead of being silently ignored.
For directory snapshots, a symbol’s logical identity is its parent-directory
scope plus its declaration identity; the .mbti filename is provenance, not
identity. Therefore moving pkg/a.mbti::render to pkg/b.mbti::render produces
no compatibility change. Moving it to another package scope does change its
logical name, and duplicate definitions within one package are still diagnosed.
The repository includes 15 pinned public pkg.generated.mbti samples from
official and community MoonBit projects. MoonGuard currently extracts 6819 API
items from this corpus with zero unknown declarations and zero snapshot
diagnostics. All 15 samples are fully modeled, including one historical sample
whose generated interface contains 119 associated fn/impl lines without a
public visibility prefix. Legacy inference is restricted to files identified
by the moon info generator header and package declaration.
The implementation is split by responsibility: api_model.mbt, parser.mbt,
snapshot.mbt, diff.mbt, semver.mbt, policy.mbt,
report_markdown.mbt, and report_json.mbt. The CLI is likewise separated
into argument parsing, configuration, commands, output, snapshot I/O, and
backend-specific file I/O under cmd/main.
Current validation result: 164/164 default-target tests and 165/165 JS-target
tests pass.
Generated pkg.generated.mbti files are kept in the repository so interface
changes are reviewable after moon info.
Competition source-line tracking counts repository .mbt source files and
excludes generated _build output. The current tracked source total is 8580
lines, including 5215 non-test lines. The confirmed competition threshold is
4000 lines.
MoonGuard
MoonGuard is a MoonBit public API compatibility and SemVer guard. It compares two MoonBit interface snapshots, reports public API additions, removals, and signature changes, then recommends whether the next release should be patch, minor, or major.
The project is being built for the MoonBit open-source ecosystem competition as engineering infrastructure for package authors and CI workflows.
Review Materials
The competition submission, validation evidence, design report, compatibility rules, and package-release checklist are indexed in docs/README.md. This index is the recommended starting point for reviewers.
Current Features
.mbti-style interface text.fn,type,typealias,struct,enum,trait,impl,let, andconstdeclarations..mbtimembers including struct fields, enum constructors, trait methods, generic methods,suberror, andpub usingtype or trait exports..mbtifiles inside one package is not reported as an API change while same-named symbols in different packages remain distinct..mbtifiles and directories from the CLI when running on the JS backend.Installation
Install the library from mooncakes.io:
Install the command-line package:
For source development, install the MoonBit toolchain, then clone this repository:
Library Usage
The report recommendation is
patch,minor, ormajor.CLI Usage
Compare two
.mbtifiles with the default Markdown report:Output:
Render the same report as JSON:
Check whether a planned version bump is sufficient:
Use a config file for repeated CI defaults:
Compare package directories and inspect a generated interface inventory:
The release-plan command is intended for application-facing package workflows: it prints whether a release is ready, blocked by snapshot diagnostics, or needs a larger version bump, then includes checklist items for release notes and migration review.
Ignore files filter report changes without changing the parsed API model:
For release governance, prefer an auditable policy file:
The syntax is
allow CHANGE_KIND ITEM_KIND NAME [until VERSION] [max_matches N] reason TEXT....CHANGE_KINDisadded,removed,changed, or*; item kind and name also support*matching. A non-empty reason is mandatory andmax_matchesdefaults to1. Expired rules, malformed rules, missing policy versions, and exceeded budgets fail closed with exit code2. Unmatched and overlapping rules remain visible as warnings.Policy output preserves both
original_reportandeffective_report, plus the accepted changes and rule diagnostics.check,check-dir, andrelease-planuse--nextas the policy version when--policy-versionis omitted.--ignore-fileand--policy-fileare mutually exclusive, andinventory-dirdoes not accept policy files.File and directory commands currently require the JS backend because the CLI uses Node
fs.readFileSyncand directory APIs through MoonBit JS externs. For strict CI exit-code checks, run the generated JS with Node directly after a JS-target command has built it:For quick demos without files, use
report-text:GitHub Actions
MoonGuard can compare a committed release baseline with interfaces generated in a pull request, fail on an insufficient version bump, and upload a JSON release plan. See docs/github-actions.md for a complete downstream workflow pinned to the
v0.1.0release.Compatibility Rules
MoonGuard starts with conservative rules:
The parser intentionally covers high-frequency
.mbtideclarations first, including common nested members from generated interface files. Any unrecognizedpubline is retained asunknownso that public surface changes remain visible instead of being silently ignored.For directory snapshots, a symbol’s logical identity is its parent-directory scope plus its declaration identity; the
.mbtifilename is provenance, not identity. Therefore movingpkg/a.mbti::rendertopkg/b.mbti::renderproduces no compatibility change. Moving it to another package scope does change its logical name, and duplicate definitions within one package are still diagnosed.The complete rule table and rationale are in docs/api-compat-rules.md.
Real-World Validation
The repository includes 15 pinned public
pkg.generated.mbtisamples from official and community MoonBit projects. MoonGuard currently extracts 6819 API items from this corpus with zero unknown declarations and zero snapshot diagnostics. All 15 samples are fully modeled, including one historical sample whose generated interface contains 119 associatedfn/impllines without a public visibility prefix. Legacy inference is restricted to files identified by themoon infogenerator header and package declaration.Development
The implementation is split by responsibility:
api_model.mbt,parser.mbt,snapshot.mbt,diff.mbt,semver.mbt,policy.mbt,report_markdown.mbt, andreport_json.mbt. The CLI is likewise separated into argument parsing, configuration, commands, output, snapshot I/O, and backend-specific file I/O undercmd/main.Current validation result: 164/164 default-target tests and 165/165 JS-target tests pass.
Common checks:
Generated
pkg.generated.mbtifiles are kept in the repository so interface changes are reviewable aftermoon info.Competition source-line tracking counts repository
.mbtsource files and excludes generated_buildoutput. The current tracked source total is 8580 lines, including 5215 non-test lines. The confirmed competition threshold is 4000 lines.See docs/competition-plan.md for the competition plan.