Examples
Rules a syntax linter cannot see
Write the invariant in English. IntentLint evaluates every pull request against it — including changes written by coding agents. These are the kinds of rules teams check into .intentlint.yml.
Idempotent workers
- id: jobs/idempotent-workers
description: >
Background workers that make external
side effects must be idempotent.
Retries must not duplicate irreversible
work such as charges, emails, or remote
writes.
Why it’s powerful
Idempotency is a behavioral property, not a syntax pattern. Traditional linters can require a decorator or ban a helper, but they cannot tell whether a worker is actually safe to run twice. IntentLint reads the rule and the diff and decides whether retries would duplicate a side effect.
Tribal knowledge
- id: inventory/reserve-only-writes
description: >
All mutations to inventory_units must
go through Inventory::Reserve. Direct
SQL or ActiveRecord updates skip the
warehouse lock and can oversell during
flash sales.
Why it’s powerful
This is a quirk of how this particular system works. Every pull request has to respect it, or a perfectly ordinary UPDATE can lose money. Checking that knowledge into version control means new engineers and coding agents are held to the same production constraint as the people who learned it the hard way.
Architecture / responsibility boundaries
- id: architecture/thin-controllers
description: >
Controllers should only authenticate,
validate input, and orchestrate calls.
Business rules and complex data
transformations belong in domain
services.
Why it’s powerful
Traditional linters can enforce dependency direction or file structure, but they cannot reliably decide whether a block of perfectly valid code is business logic. IntentLint can enforce architectural intent rather than syntax.
Safe database rollout patterns
- id: migrations/expand-and-contract
description: >
Changes to large production tables must
be backwards-compatible with the currently
deployed application. New required columns
must be introduced using an expand-and-contract
migration rather than added as immediately
required.
Why it’s powerful
Whether a migration is operationally safe depends on what the migration is doing and how the application will behave during a rolling deploy. This catches dangerous changes that can look completely reasonable to a schema linter.
Business invariants that span implementation details
- id: billing/paid-feature-after-payment
description: >
A user must never receive access to a
paid feature until payment has been
successfully recorded. Changes to
checkout, webhook, subscription, or
entitlement code must preserve this
invariant.
Why it’s powerful
The rule describes the behavior the system must preserve, not a particular function call or code pattern. Engineers can refactor the implementation freely while IntentLint checks that the underlying product invariant still holds.
In-progress work and refactors
- id: auth/rbac-not-legacy-acl
description: >
New authorization checks must use the
RBAC system. Do not add calls to the
legacy permissioning helpers
(LegacyACL, can_access?, or
Permission.for). Existing legacy checks
may remain until the migration is done.
Why it’s powerful
A temporary rule can name the refactor and require new code to use RBAC instead of the old permissioning system. Traditional linters can ban a symbol, but they cannot tell whether a change is extending the legacy path or merely touching a file that still contains it. When the migration is done, delete the rule.
Enforce rules with dynamic exceptions
- id: i18n/no-direct-fr-de-edits
description: >
French and German translation YAML files
must not be edited by hand. Translated
strings come from the translation service.
Removing a string entirely or moving a
file is allowed. Changing translation
values or adding new translated copy in
these files is not.
Why it’s powerful
CODEOWNERS can require a review on every fr.yml or de.yml change, and a path linter can forbid those files entirely. Neither can tell a hand-edited French string from a legitimate key deletion or a file move. IntentLint evaluates what the change is doing, not only which path it touches.
Rules about data exposure
- id: product/no-shadow-ban-disclosure
description: >
In this application, users can be
shadow-banned. Users who are
shadow-banned should not be told that
they are shadow-banned, and the API
must never expose this information in
its responses.
Why it’s powerful
Whether a response leaks a hidden status is a product invariant, not a syntax pattern. A traditional linter can ban a field name, but it cannot tell whether a new serializer, error message, or API shape would let a shadow-banned user discover that they are banned. IntentLint reads the rule and the change and flags accidental disclosure.