Linting a domain model
Run the linter and interpret it for the user. The point is not a clean bill of
health — it's making gaps visible so the user can decide whether they matter.
Run it
First confirm modelith is installed (modelith --version); if it is not on
PATH, tell the user to install it before going further — don't eyeball the
YAML in its place:
go install github.com/stacklok/modelith/cmd/modelith@latest
# or download a binary from https://github.com/stacklok/modelith/releases
modelith lint <file>... # human-readable
modelith lint --format json <file> # when you need to parse findings
Use --completeness error only if the user wants gaps to be treated as hard
failures (e.g. a strict CI gate). It promotes the completeness warnings (the
advisory gaps listed below) to blocking errors. It does not affect semantic
errors — those (including a dangling invariants_touched id) block regardless of
the flag.
Interpret the three layers
- Structural (error). The file violates the JSON Schema — wrong type,
missing required field, bad
cardinality — or names a cross-model reference
(scope.Name) in relationship.entity or subtypeOf, which is not
supported anywhere but an attribute type. Must be fixed; the model won't
parse cleanly otherwise.
- Semantic (error or warning). Errors (always block, flag-independent): a
relationship points at an entity that doesn't exist; a scenario's
invariants_touched or an action's preserves names an invariant id no entity
or top-level invariants entry declares; the same invariant id is declared
twice (entity-level and top-level invariants share one id namespace); reciprocal cardinalities
that aren't inverses; reciprocal declarations that both claim
ownership: owned; an imports entry that's absolute, unreadable, not a
domain model, binds a scope another entry already bound, resolves outside
the repository holding this model, is a bare path whose filename yields no
valid scope slug, has a path containing a control character, or declares a
schema version this modelith doesn't support; an attribute type containing
a dot that isn't a well-formed scope.Name (a dot is always reserved for a
cross-model reference — decimal(10.2) and google.protobuf.Timestamp are
errors, not primitives); a scope.Name type whose scope no import binds, or
that names no enum in the model it resolves to. Warnings: a backticked term
resolves to no entity, role, or actor; an action actor that's neither an entity nor a
glossary term; a PascalCase attribute type that names no defined enum —
usually a typo or a concept that was never named; a relationship role
written as prose, which belongs in note; an ambiguous reciprocal pairing,
where one end declares the same relationship twice and the other declares it
back. Decide which it is and propose the fix.
- Completeness (advisory warning). Gaps, not bugs: an entity with no
invariants, an entity no scenario exercises, a glossary term defined but
never referenced, an enum no attribute uses, or an import nothing
references.
shared: true on the model being linted relaxes the two
vocabulary-only checks (the unused glossary term and unused enum) — it does
not relax the invariant or scenario-coverage checks, and it does nothing for
an unreferenced import (that check is about this model's own imports,
not about being imported). These are what --completeness error promotes to
blocking. For each, ask whether it's a real gap (write the missing invariant
or scenario, or drop the unused import) or genuinely fine — a
shared: true model in particular.
How to report back
- Lead with errors — these block. Give the fix for each.
- Then semantic warnings — likely typos or missing definitions.
- Then completeness gaps, framed as questions: "Nothing exercises
Policy
in a scenario — should there be one, or is it intentionally a supporting
concept?"
Don't just dump the linter output. Prioritize, explain why each matters, and
propose concrete edits. If the user agrees, use the domain-model-author skill
to make them, then re-lint and re-render.
1---2name: domain-model-lint3description: Run modelith lint on a domain model and explain the findings — a read-only review pass. Use when the user asks to check, validate, or review a *.modelith.yaml file, or wants to know what is missing or inconsistent. Turns the linter output into a prioritized, actionable summary. To actually change the model (add or edit entities, fix the gaps), use domain-model-author.4---56# Linting a domain model78Run the linter and interpret it for the user. The point is not a clean bill of9health — it's making gaps *visible* so the user can decide whether they matter.1011## Run it1213First confirm `modelith` is installed (`modelith --version`); if it is not on14`PATH`, tell the user to install it before going further — don't eyeball the15YAML in its place:1617```sh18go install github.com/stacklok/modelith/cmd/modelith@latest19# or download a binary from https://github.com/stacklok/modelith/releases20```2122```sh23modelith lint <file>... # human-readable24modelith lint --format json <file> # when you need to parse findings25```2627Use `--completeness error` only if the user wants gaps to be treated as hard28failures (e.g. a strict CI gate). It promotes the **completeness** warnings (the29advisory gaps listed below) to blocking errors. It does **not** affect semantic30errors — those (including a dangling `invariants_touched` id) block regardless of31the flag.3233## Interpret the three layers3435- **Structural (error).** The file violates the JSON Schema — wrong type,36 missing required field, bad `cardinality` — or names a cross-model reference37 (`scope.Name`) in `relationship.entity` or `subtypeOf`, which is not38 supported anywhere but an attribute `type`. Must be fixed; the model won't39 parse cleanly otherwise.40- **Semantic (error or warning).** Errors (always block, flag-independent): a41 relationship points at an entity that doesn't exist; a scenario's42 `invariants_touched` or an action's `preserves` names an invariant id no entity43 or top-level `invariants` entry declares; the same invariant id is declared44 twice (entity-level and top-level invariants share one id namespace); reciprocal cardinalities45 that aren't inverses; reciprocal declarations that both claim46 `ownership: owned`; an `imports` entry that's absolute, unreadable, not a47 domain model, binds a scope another entry already bound, resolves outside48 the repository holding this model, is a bare path whose filename yields no49 valid scope slug, has a path containing a control character, or declares a50 schema version this modelith doesn't support; an attribute `type` containing51 a dot that isn't a well-formed `scope.Name` (a dot is always reserved for a52 cross-model reference — `decimal(10.2)` and `google.protobuf.Timestamp` are53 errors, not primitives); a `scope.Name` type whose scope no import binds, or54 that names no enum in the model it resolves to. Warnings: a backticked term55 resolves to no entity, role, or actor; an action `actor` that's neither an entity nor a56 glossary term; a PascalCase attribute `type` that names no defined enum —57 usually a typo or a concept that was never named; a relationship `role`58 written as prose, which belongs in `note`; an ambiguous reciprocal pairing,59 where one end declares the same relationship twice and the other declares it60 back. Decide which it is and propose the fix.61- **Completeness (advisory warning).** Gaps, not bugs: an entity with no62 invariants, an entity no scenario exercises, a glossary term defined but63 never referenced, an enum no attribute uses, or an import nothing64 references. `shared: true` on the model being linted relaxes the two65 vocabulary-only checks (the unused glossary term and unused enum) — it does66 not relax the invariant or scenario-coverage checks, and it does nothing for67 an unreferenced import (that check is about *this* model's own `imports`,68 not about being imported). These are what `--completeness error` promotes to69 blocking. For each, ask whether it's a real gap (write the missing invariant70 or scenario, or drop the unused import) or genuinely fine — a71 `shared: true` model in particular.7273## How to report back74751. Lead with **errors** — these block. Give the fix for each.762. Then **semantic warnings** — likely typos or missing definitions.773. Then **completeness gaps**, framed as questions: "Nothing exercises `Policy`78 in a scenario — should there be one, or is it intentionally a supporting79 concept?"8081Don't just dump the linter output. Prioritize, explain *why* each matters, and82propose concrete edits. If the user agrees, use the `domain-model-author` skill83to make them, then re-lint and re-render.