Manifest linting with monochange check
monochange can lint monorepo package manifests through monochange check, using rules configured under [lints] in monochange.toml.
Use this guide when the task is to configure or explain monochange’s lint rules.
These are the rules that run through monochange check and are configured in monochange.toml under the top-level [lints] section. They are separate from Rust compiler or Clippy lints used to develop monochange itself.
This page is the human-readable companion to the live lint catalog. For machine-readable output or to verify the exact catalog in the installed binary, run:
monochange lint list --format json
monochange lint explain <rule-or-preset-id>
What monochange check does
monochange check runs two phases:
- normal workspace validation, similar to
monochange step validate - changeset and manifest lint rules for configured package ecosystems
Common commands:
monochange check
monochange check --fix
monochange check --format json
monochange lint list
monochange lint explain cargo/recommended
Use --fix when you want monochange to apply auto-fixes where a rule supports them. Rules that are not autofixable still report diagnostics and suggested remediation.
Autofixes never destroy surrounding content: rules that rewrite a whole manifest do so by serializing a mutated copy of the parsed document, and every whole-file rewrite is validated against the target ecosystem’s manifest parser before it is written. If a rewrite would produce an unparseable manifest, the fix is skipped and the original file is kept.
Where lint rules live
Configure presets, global rules, and scoped overrides in the top-level [lints] section of monochange.toml:
[lints]
use = [
"changesets/recommended",
"cargo/recommended",
"npm/recommended",
"dart/recommended",
]
exclude = ["fixtures/**"]
[lints.rules]
"cargo/internal-dependency-workspace" = "error"
"npm/workspace-protocol" = "error"
"dart/sdk-constraint-modern" = { level = "warning", minimum = "3.6.0", require_upper_bound = false }
"dart/no-unexpected-dependency-overrides" = { level = "warning", allow_for_private = true, allow_packages = ["app_shell"] }
[[lints.scopes]]
name = "published cargo packages"
match = { ecosystems = ["cargo"], managed = true, publishable = true }
rules = { "cargo/required-package-fields" = "error" }
Rule configuration supports two forms:
- simple severity:
"rule-id" = "error","warning", or"off" - detailed config:
{ level = "error", ...rule_specific_options }
Preset rules provide the baseline. Explicit entries in [lints.rules] override that baseline. Scoped rules let a subset of packages be stricter or looser than the workspace default.
Presets
| Preset | What it is for | Rules enabled |
|---|---|---|
changesets/recommended | Baseline changeset hygiene. | changesets/summary = error with an H1 heading, changesets/summary-description = error, changesets/prefer-inline = error |
cargo/recommended | Balanced Cargo manifest policy for most workspaces. | cargo/internal-dependency-workspace = error, cargo/publishable-dependencies = error, cargo/required-package-fields = error, cargo/dependency-field-order = warning, cargo/sorted-dependencies = warning, cargo/unlisted-package-private = warning |
cargo/strict | Cargo policy with style rules promoted to errors. | Same as cargo/recommended, but cargo/dependency-field-order and cargo/sorted-dependencies are error. |
npm/recommended | Balanced npm-family manifest policy. | npm/workspace-protocol = error, npm/no-duplicate-dependencies = error, npm/required-package-fields = error, npm/root-no-prod-deps = error, npm/sorted-dependencies = warning, npm/unlisted-package-private = warning |
npm/strict | npm-family policy with dependency sorting promoted to an error. | Same as npm/recommended, but npm/sorted-dependencies is error. |
dart/recommended | Baseline Dart metadata, publishability, and SDK hygiene. | dart/sdk-constraint-present = error, dart/required-package-fields = error, dart/no-git-dependencies-in-published-packages = error, dart/unlisted-package-private = error, dart/dependency-sorted = warning |
dart/strict | Dart policy with workspace and Flutter policy rules enforced. | Everything in dart/recommended, plus dart/sdk-constraint-modern, dart/no-unexpected-dependency-overrides, dart/internal-path-dependency-policy, dart/workspace-internal-version-consistency, dart/flutter-package-metadata-consistent, and dart/assets-sorted as errors; dart/dependency-sorted is promoted to error. |
Available rules at a glance
| Rule id | Ecosystem | Category | Autofix | Summary |
|---|---|---|---|---|
changesets/summary | changesets | correctness | no | Requires a changeset body to start with a summary heading. |
changesets/summary-description | changesets | style | no | Requires the first description sentence to add information instead of repeating the summary. |
changesets/no_section_headings | changesets | correctness | no | Rejects change-type headings inside changeset bodies. |
changesets/prefer-inline | changesets | style | yes | Rewrites object change entries that repeat what the inline form already implies. |
changesets/bump/none | changesets | correctness | no | Applies scoped body policy to none bump entries. |
changesets/bump/patch | changesets | correctness | no | Applies scoped body policy to patch bump entries. |
changesets/bump/minor | changesets | correctness | no | Applies scoped body policy to minor bump entries. |
changesets/bump/major | changesets | correctness | no | Applies scoped body policy to major bump entries. |
changesets/types/<type> | changesets | correctness | no | Applies scoped body policy to a configured changelog type. |
changesets/duplicate | changesets | correctness | no | Recognized compatibility switch for duplicate target validation; workspace validation rejects duplicate package entries regardless. |
cargo/dependency-field-order | Cargo | style | yes | Orders keys inside inline dependency tables. |
cargo/internal-dependency-workspace | Cargo | correctness | yes | Requires internal crate dependencies to use workspace = true. |
cargo/publishable-dependencies | Cargo | correctness | no | Prevents publishable crates from depending on unpublished workspace crates. |
cargo/required-package-fields | Cargo | correctness | no | Requires selected [package] metadata fields. |
cargo/sorted-dependencies | Cargo | style | yes | Sorts dependency tables alphabetically. |
cargo/unlisted-package-private | Cargo | correctness | yes | Requires unmanaged crates to set publish = false. |
cargo/manifest-repository | Cargo | correctness | yes | Requires package.repository to point at the root repository or the package subdirectory URL. |
npm/workspace-protocol | npm-family | correctness | yes | Requires internal dependencies to use workspace: ranges. |
npm/sorted-dependencies | npm-family | style | yes | Sorts dependency sections alphabetically. |
npm/required-package-fields | npm-family | correctness | no | Requires selected package.json metadata fields. |
npm/root-no-prod-deps | npm-family | best practice | yes | Keeps production dependencies out of the workspace root package. |
npm/no-duplicate-dependencies | npm-family | correctness | yes | Prevents the same dependency from appearing in multiple dependency sections. |
npm/unlisted-package-private | npm-family | correctness | yes | Requires unmanaged packages to set private: true. |
npm/manifest-repository | npm-family | correctness | yes | Requires repository in package.json to point at the root repository or the package subdirectory URL. |
dart/sdk-constraint-present | Dart | correctness | no | Requires environment.sdk in pubspec.yaml. |
dart/sdk-constraint-modern | Dart | best practice | no | Enforces a modern SDK lower bound and, by default, an upper bound. |
dart/dependency-sorted | Dart | style | yes | Sorts dependency sections in pubspec.yaml. |
dart/required-package-fields | Dart | correctness | no | Requires selected pubspec.yaml metadata fields. |
dart/no-git-dependencies-in-published-packages | Dart | correctness | no | Blocks git: dependencies in publishable packages unless allowed. |
dart/unlisted-package-private | Dart | correctness | yes | Requires unmanaged packages to set publish_to: none. |
dart/no-unexpected-dependency-overrides | Dart | best practice | no | Allows dependency_overrides only in approved packages. |
dart/internal-path-dependency-policy | Dart | best practice | no | Enforces one policy for internal Dart dependency references. |
dart/workspace-internal-version-consistency | Dart | correctness | no | Requires internal hosted dependency ranges to match workspace package versions. |
dart/flutter-package-metadata-consistent | Dart / Flutter | correctness | no | Requires Flutter packages to declare the Flutter SDK dependency consistently. |
dart/assets-sorted | Dart / Flutter | style | yes | Sorts Flutter assets and fonts. |
dart/manifest-repository | Dart | correctness | yes | Requires repository in pubspec.yaml to point at the root repository or the package subdirectory URL. |
Changeset lint rules
Changeset lint rules use the same [lints.rules] table as manifest rules. They are evaluated while markdown changesets are loaded by validation and release workflows.
[lints]
use = ["changesets/recommended"]
[lints.rules]
"changesets/no_section_headings" = "error"
"changesets/summary" = { level = "error", required = true, heading_level = 1, min_length = 12, max_length = 80, forbid_trailing_period = true, forbid_conventional_commit_prefix = true, require_description = true }
"changesets/summary-description" = "error"
"changesets/bump/major" = { level = "error", required_sections = ["Impact", "Migration"], min_body_chars = 120, require_code_block = true }
"changesets/types/breaking" = { level = "error", forbidden_headings = ["Breaking", "Breaking changes"], required_sections = ["Impact", "Migration"], required_bump = "major" }
changesets/summary
Why: every changeset should be understandable from a compact, release-note-ready heading.
What it checks: the first heading in a changeset body. It can require a heading, constrain its level and length, ban trailing periods, ban conventional-commit prefixes, and require descriptive body text after the heading.
The changesets/recommended preset requires an H1 summary. The release-note renderer chooses the final heading depth, so authors do not need to anticipate whether an entry will be rendered compactly or expanded.
Useful options:
required: require the summary heading.heading_level: require a Markdown heading level from1to6.min_length/max_length: constrain summary text length.forbid_trailing_period: reject summaries ending in..forbid_conventional_commit_prefix: reject summaries such asfeat: add parser.require_description: require a non-empty paragraph after the heading.
changesets/summary-description
Why: repeating the headline as the first sentence makes a changeset look longer without telling the reader anything new.
What it checks: after normalizing case, punctuation, and whitespace, the first description sentence must differ from the summary. Use the description to explain impact, behavior, or required action.
Without the rule:
# Show publish results clearly
Show publish results clearly.
With the rule:
# Show publish results clearly
Publish now ends with package counts and one outcome row per package, so CI logs expose the result without requiring debug output.
changesets/no_section_headings
Why: change types already come from the changeset entries. Repeating them as body headings creates noisy generated changelogs.
With the rule: headings that duplicate configured changelog types, such as ## Breaking or ## Fix, are rejected.
changesets/prefer-inline
Why: change entries read best in the inline target: type form. Writing an object that only repeats what the inline form already implies adds noise for agents and humans authoring changesets.
What it checks: object (table) change entries whose fields are exactly equivalent to the inline form:
typealone (core: { type: "feat" }),typeplus abumpthat the type already implies (core: { type: "feat", bump: "minor" }, sincefeatimpliesminor), and- a bare
bumpwhose keyword is also a configured change type that implies the same bump (core: { bump: "minor" }, sinceminoris a change type with default bumpminor; the inline entry keeps the bump and gains the type).
Entries with a version, a caused_by, an unknown field, an unknown change type, or a bump that disagrees with the type default are left untouched, because the inline form cannot express them without changing meaning. A bare bump: none is also left alone: changeset validation rejects it outright.
Before:
---
"@monochange/cli":
bump: minor
type: feat
---
After (monochange check --fix):
---
"@monochange/cli": feat
---
The rule is on by default for every project and included in changesets/recommended.
changesets/bump/<severity>
Why: different bump severities can require different explanation standards. A major bump often needs impact and migration notes, while a patch bump may only need a concise description.
Supported severities: none, patch, minor, and major.
Useful options:
required_sections: headings that must appear in the body.forbidden_headings: headings that must not appear in the body.min_body_chars/max_body_chars: body length bounds.require_code_block: require a fenced code block.required_bump: require entries governed by this rule to use a specific bump severity.
changesets/types/<type>
Why: changelog types can carry their own policy. For example, a breaking type can require migration notes even if a repository has multiple bump severities.
The <type> segment must match a configured changelog type. It accepts the same scoped options as changesets/bump/<severity>.
changesets/duplicate
Why: a changeset should not target the same effective package more than once.
Duplicate package entries are rejected by workspace validation. The rule id remains recognized in [lints.rules] for compatibility with existing configurations that explicitly turn it on or off.
Cargo manifest lint rules
Cargo rules apply to discovered Cargo.toml package manifests and, where needed, the workspace package graph.
cargo/dependency-field-order
Why: keeps inline dependency tables visually consistent.
What it checks: preferred key order inside dependency tables:
workspaceorversiondefault-features/default_featuresfeatures- other keys like
optional,path,registry,package,git,branch,tag,rev
Without the rule:
serde = { features = ["derive"], workspace = true }
With the rule:
serde = { workspace = true, features = ["derive"] }
Options:
fix: defaults totrue; rewrites the dependency entry when safe.
cargo/internal-dependency-workspace
Why: internal workspace dependencies should usually be declared through the workspace rather than carrying their own explicit version strings.
Without the rule:
[dependencies]
monochange_core = { path = "../monochange_core", version = "0.1.0" }
With the rule:
[dependencies]
monochange_core = { workspace = true }
When to use it: when the repository wants one workspace-owned version source for internal crates.
Options:
require_workspace: defaults totrue; require internal dependencies to useworkspace = true.fix: defaults totrue; rewrites safe internal dependency entries.
cargo/publishable-dependencies
Why: a crate that can be published should not depend on an internal workspace crate that cannot be published. That leaves registry consumers unable to resolve the dependency.
What it checks: publishable Cargo packages and their internal Cargo dependencies. If the dependent package is publishable, any internal dependency it relies on must also be publishable.
Without the rule:
# crates/app/Cargo.toml
[package]
name = "app"
version = "0.1.0"
[dependencies]
internal_helper = { workspace = true }
# crates/internal_helper/Cargo.toml
[package]
name = "internal_helper"
version = "0.1.0"
publish = false
With the rule: either make internal_helper publishable, remove the dependency from the publishable crate, or mark the depending crate private too.
Autofix: no. This is a release policy decision, so monochange reports the dependency chain instead of changing publishability for you.
cargo/required-package-fields
Why: published crates should consistently carry the metadata your repository expects.
Default required fields:
descriptionlicenserepository
Without the rule:
[package]
name = "example"
version = "0.1.0"
With the rule: monochange reports the missing fields so package metadata stays consistent.
Options:
fields: replace the default required-field list.
Example:
[lints.rules]
"cargo/required-package-fields" = { level = "error", fields = ["description", "license"] }
cargo/sorted-dependencies
Why: alphabetized dependency tables are easier to review and reduce noisy diffs.
Without the rule:
[dependencies]
zzzz = "1.0"
aaaa = "1.0"
mmmm = "1.0"
With the rule:
[dependencies]
aaaa = "1.0"
mmmm = "1.0"
zzzz = "1.0"
Options:
fix: defaults totrue; rewrites dependency sections in sorted order.
cargo/unlisted-package-private
Why: a Cargo package that is not listed in monochange.toml should not be accidentally publishable.
With the rule: monochange requires either:
- adding the package to
monochange.toml, or - marking it private with
publish = false.
Without the rule:
[package]
name = "experimental-crate"
version = "0.1.0"
With the rule:
[package]
name = "experimental-crate"
version = "0.1.0"
publish = false
Options:
fix: defaults totrue; insertspublish = falsewhen safe.
cargo/manifest-repository
Why: package registry pages should send readers to the exact source directory for the package they are using. In monorepos, a root repository URL is correct for root-level packages, but packages under subdirectories should link to that subdirectory on the configured default branch.
What it checks: the rule compares [package].repository with the repository URL derived from [source] in monochange.toml:
- root-level packages must use the base repository URL, such as
https://github.com/acme/widgets - subdirectory packages must use
{repo_url}/tree/{default_branch}/{relative_package_dir}, such ashttps://github.com/acme/widgets/tree/main/crates/widget_core - if
[source]is missing, the rule skips because monochange cannot derive the canonical repository URL
Cargo manifests may also use repository = { workspace = true }. By default, this rule resolves that inheritance from the root Cargo.toml’s [workspace.package].repository, falling back to root [package].repository. If the inherited root value does not point at the package subdirectory, the rule reports the package manifest and can replace the inherited inline table with an explicit repository URL.
Without the rule:
[package]
name = "widget_core"
version = "0.1.0"
repository = "https://github.com/acme/widgets"
With the rule:
[package]
name = "widget_core"
version = "0.1.0"
repository = "https://github.com/acme/widgets/tree/main/crates/widget_core"
Configuration:
[lints.rules]
"cargo/manifest-repository" = "error"
Set allow_workspace_inheritance = true only when you intentionally want to permit repository = { workspace = true } without resolving it against the package path:
[lints.rules]
"cargo/manifest-repository" = { level = "error", allow_workspace_inheritance = true }
Autofix: run monochange check --fix to insert a missing repository, replace an incorrect value, or convert repository = { workspace = true } into the explicit URL required for the package directory. There is no per-rule fix option; applying fixes is controlled by the CLI flag.
npm-family manifest lint rules
npm-family rules apply to package.json manifests discovered through npm, pnpm, yarn, Bun, and Deno/npm-style package graphs.
npm/workspace-protocol
Why: internal workspace dependencies should use the workspace: protocol so local workspace intent is explicit.
Without the rule:
{
"dependencies": {
"@acme/shared": "^1.2.0"
}
}
With the rule:
{
"dependencies": {
"@acme/shared": "workspace:*"
}
}
When to use it: npm, pnpm, yarn, and Bun workspaces where internal packages should not drift to plain registry ranges.
Options:
require_for_private: defaults tofalse; also enforce the rule for private packages.fix: defaults totrue; rewrites internal dependency ranges toworkspace:ranges.
npm/sorted-dependencies
Why: alphabetized dependency sections reduce review noise and make package diffs easier to scan.
Without the rule:
{
"dependencies": {
"zod": "^4.0.0",
"chalk": "^5.0.0"
}
}
With the rule:
{
"dependencies": {
"chalk": "^5.0.0",
"zod": "^4.0.0"
}
}
Options:
fix: defaults totrue; rewrites dependency sections in sorted order.
npm/required-package-fields
Why: package metadata should stay consistent across publishable npm packages.
Default required fields:
descriptionrepositorylicense
Without the rule:
{
"name": "@acme/app",
"version": "1.0.0"
}
With the rule: monochange reports the missing metadata fields.
Options:
fields: replace the default required-field list.
npm/root-no-prod-deps
Why: the workspace root package.json is usually orchestration-only and should keep runtime dependencies out of the root package.
Without the rule:
{
"dependencies": {
"react": "^19.0.0"
}
}
With the rule: move those to devDependencies when the root package is only a workspace manager.
Options:
fix: defaults totrue; moves rootdependenciesintodevDependencies.
npm/no-duplicate-dependencies
Why: the same dependency should not appear in multiple dependency sections unless the repository has a very deliberate reason.
Without the rule:
{
"dependencies": {
"typescript": "^5.0.0"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
With the rule: monochange reports the duplicate and can remove redundant entries from later sections when safe.
Options:
fix: defaults totrue; removes duplicate entries from later sections.
npm/unlisted-package-private
Why: a package not declared in monochange.toml should not remain publishable by accident.
With the rule: monochange requires either:
- adding the package to
monochange.toml, or - marking it private in
package.json.
Without the rule:
{
"name": "@acme/experimental",
"version": "0.1.0"
}
With the rule:
{
"name": "@acme/experimental",
"private": true,
"version": "0.1.0"
}
Options:
fix: defaults totrue; insertsprivate: truewhen safe.
npm/manifest-repository
Why: npm package metadata should link users to the exact source folder for that package. In monorepos, a root repository URL is correct only for root-level packages; packages in subdirectories should link directly to their package directory on the configured default branch.
What it checks: the rule compares repository in package.json with the repository URL derived from [source] in monochange.toml:
- root-level packages must use the base repository URL, such as
https://github.com/acme/widgets - subdirectory packages must use
{repo_url}/tree/{default_branch}/{relative_package_dir}, such ashttps://github.com/acme/widgets/tree/main/packages/widget-core - if
[source]is missing, the rule skips because monochange cannot derive the canonical repository URL
Without the rule:
{
"name": "@acme/widget-core",
"version": "0.1.0",
"repository": "https://github.com/acme/widgets"
}
With the rule:
{
"name": "@acme/widget-core",
"version": "0.1.0",
"repository": "https://github.com/acme/widgets/tree/main/packages/widget-core"
}
Configuration:
[lints.rules]
"npm/manifest-repository" = "error"
Autofix: run monochange check --fix to insert a missing repository or replace an incorrect value. There is no per-rule fix option; applying fixes is controlled by the CLI flag.
Dart manifest lint rules
Dart rules apply to pubspec.yaml manifests, including Flutter packages when a pubspec has Flutter-specific metadata.
dart/sdk-constraint-present
Why: every managed Dart package should declare the SDK range it expects rather than inheriting whatever the developer machine happens to provide.
With the rule: monochange reports any pubspec.yaml that omits environment.sdk.
Without the rule:
name: app
version: 1.0.0
With the rule:
name: app
version: 1.0.0
environment:
sdk: ">=3.6.0 <4.0.0"
dart/sdk-constraint-modern
Why: old or overly broad SDK ranges quietly expand your support policy and make releases harder to reason about.
Default policy:
- minimum lower bound:
3.0.0 - upper bound required by default
Options:
minimum: override the minimum lower bound for your workspace.require_upper_bound: set tofalseif your policy intentionally omits an upper bound.
Example:
[lints.rules]
"dart/sdk-constraint-modern" = { level = "warning", minimum = "3.6.0", require_upper_bound = false }
dart/dependency-sorted
Why: alphabetized dependencies, dev_dependencies, and dependency_overrides blocks reduce review noise and make Dart manifest diffs easier to scan.
Without the rule:
dependencies:
zeta: ^1.0.0
alpha: ^1.0.0
With the rule:
dependencies:
alpha: ^1.0.0
zeta: ^1.0.0
Options:
fix: defaults totrue; rewrites dependency sections in sorted order.
dart/required-package-fields
Why: managed publishable Dart packages should carry the metadata your repository expects before release.
Default required fields:
descriptionrepositorylicense
Without the rule:
name: app
version: 1.0.0
With the rule: monochange reports missing metadata fields for publishable packages.
Options:
fields: replace the default required-field list.
Example:
[lints.rules]
"dart/required-package-fields" = { level = "error", fields = ["description", "repository"] }
dart/no-git-dependencies-in-published-packages
Why: published Dart packages should resolve from hosted dependencies, not source-control dependencies, unless the repository explicitly allows an exception.
Without the rule:
dependencies:
shared:
git:
url: https://github.com/acme/shared.git
With the rule: monochange reports git: dependencies in publishable packages unless the dependency name appears in the allow list.
Options:
allow: list dependency names that may usegit:sources.
Example:
[lints.rules]
"dart/no-git-dependencies-in-published-packages" = { level = "error", allow = ["shared"] }
dart/unlisted-package-private
Why: a Dart package that is not listed in monochange.toml should not be accidentally publishable.
With the rule: monochange requires either:
- adding the package to
monochange.toml, or - marking it private with
publish_to: none.
Without the rule:
name: experimental
version: 0.1.0
With the rule:
name: experimental
version: 0.1.0
publish_to: none
Options:
fix: defaults totrue; insertspublish_to: nonewhen safe.
dart/no-unexpected-dependency-overrides
Why: dependency_overrides are sometimes necessary, but they should usually be limited to private packages or a small allow list of explicitly approved packages.
With the rule: monochange reports dependency_overrides unless they are allowed by privacy or package name.
Options:
allow_for_private: defaults totrue; allow overrides in private packages.allow_packages: list package names that may keepdependency_overrides.
Example:
[lints.rules]
"dart/no-unexpected-dependency-overrides" = { level = "warning", allow_for_private = true, allow_packages = ["app_shell"] }
dart/internal-path-dependency-policy
Why: monorepos usually want one consistent policy for how internal Dart packages reference each other.
Default policy: strict mode expects internal packages to use path: references unless the pubspec declares resolution: workspace.
With Dart workspace resolution, Dart resolves versioned internal dependencies to local workspace packages automatically. In that mode, monochange requires version constraints and reports path: references with the message “use version constraints (not path:) when resolution is workspace”.
Options:
mode: choose"path"or"hosted"for packages that do not useresolution: workspace.
Example:
[lints.rules]
"dart/internal-path-dependency-policy" = { level = "error", mode = "hosted" }
dart/workspace-internal-version-consistency
Why: when workspace packages reference each other with hosted version ranges, those ranges should not drift away from the current workspace version.
With the rule: monochange compares internal dependency version references against the discovered workspace package version and reports mismatches. Use monochange versions --dry-run to preview automatic repairs for supported manifests, then rerun without --dry-run to update supported internal dependency references.
dart/flutter-package-metadata-consistent
Why: packages with a flutter section should declare the Flutter SDK dependency consistently so they are unmistakably Flutter packages.
With the rule: monochange requires dependencies.flutter = { sdk = flutter } in pubspec.yaml terms, expressed as the YAML mapping form.
Without the rule:
name: widgets
flutter:
assets:
- assets/logo.png
With the rule:
name: widgets
dependencies:
flutter:
sdk: flutter
flutter:
assets:
- assets/logo.png
dart/assets-sorted
Why: stable ordering for flutter.assets and flutter.fonts reduces noisy diffs in Flutter packages.
Without the rule:
flutter:
assets:
- assets/zeta.png
- assets/alpha.png
With the rule:
flutter:
assets:
- assets/alpha.png
- assets/zeta.png
Options:
fix: defaults totrue; rewrites Flutter assets and fonts in sorted order.
dart/manifest-repository
Why: Dart and Flutter package metadata should link users to the exact source folder for that package. In monorepos, a root repository URL is correct only for root-level packages; packages in subdirectories should link directly to their package directory on the configured default branch.
What it checks: the rule compares repository in pubspec.yaml with the repository URL derived from [source] in monochange.toml:
- root-level packages must use the base repository URL, such as
https://github.com/acme/widgets - subdirectory packages must use
{repo_url}/tree/{default_branch}/{relative_package_dir}, such ashttps://github.com/acme/widgets/tree/main/packages/widget_core - if
[source]is missing, the rule skips because monochange cannot derive the canonical repository URL
Without the rule:
name: widget_core
version: 0.1.0
repository: https://github.com/acme/widgets
With the rule:
name: widget_core
version: 0.1.0
repository: https://github.com/acme/widgets/tree/main/packages/widget_core
Configuration:
[lints.rules]
"dart/manifest-repository" = "error"
Autofix: run monochange check --fix to insert a missing repository or replace an incorrect value. There is no per-rule fix option; applying fixes is controlled by the CLI flag.
What monochange check looks like in practice
Use plain text for local review:
monochange check
Apply safe auto-fixes where possible:
monochange check --fix
Use JSON for CI or MCP-style tooling:
monochange check --format json
monochange check fails when lint errors are present, so it is appropriate for CI gates.
Recommended workflow
For repository work:
monochange step validate
monochange check
monochange step prepare-release --dry-run --diff
If you changed shared docs too:
devenv shell docs:check