PropertyDamage.Invariants.Invariant (PropertyDamage v0.2.0)
View SourceA first-class invariant: the named property a set of assertions verifies (DR-026).
An assertion does not stand alone; it checks an invariant. The invariant is
the stable, named thing a model promises to uphold, and one invariant may be
checked by more than one assertion (for example a synchronous @trigger and a
temporal @poll_state). Giving invariants identity lets the framework build a
single authoritative catalog and report which invariants a run actually
exercised (anti-vacuity coverage).
Fields
:id- the canonical, unique identifier. This is the linking, uniqueness, lookup, and coverage-rollup key. It is an atom and unique within a projection.:name- a human-readable display label. Defaults to:id; it earns its keep only whenidis opaque (e.g. a ticket key like:"JIRA-1234").:description- an optional sentence describing the property.
There is deliberately no :kind field. Safety-versus-liveness is a property of
a check, not of an invariant (one invariant may have both a synchronous and a
polling check); it is surfaced in the catalog, not stored here.
Declaration
Invariants are declared per projection, either centrally with an accumulating
module attribute whose value is new!/1's own argument list:
@invariant id: :balance_nonneg, description: "Balance never drops below zero"or inline on the assertion that checks them:
@trigger every: 1, id: :balance_nonneg, description: "..."
def assert_balance(state, _), do: ...Other assertions link to a declared invariant with validates: :id. An
assertion with neither id: nor validates: validates an invariant whose id
is the assertion's own (assert_-stripped) name, so every existing assertion
owns a same-named invariant by default.
Summary
Functions
Resolve the %Invariant{} for an id.
Build an %Invariant{} from a keyword list, validating shape.
Types
Functions
Resolve the %Invariant{} for an id.
By default reads the owning projection's compile-time registry
(projection.__invariants__/0), found under ctx.projection. Raises on an
unknown id -- a condition compile-time validation makes unreachable
post-compile, so failing fast is correct.
This is the single resolution seam. A configurable resolver MAY later override the description (and only the description) at report time, best-effort with fallback to the local description; that config plumbing is deferred (DR-026 §9). Resolution is lazy (report/catalog time only): never compile-time, never on the run hot path, and it never feeds generation, shrinking, or assertion logic.
Build an %Invariant{} from a keyword list, validating shape.
:id(required) must be a non-nil atom.:name(optional) must be an atom; defaults toid.:description(optional) must be a binary ornil.
Raises ArgumentError on malformed input. This is the single validating code
path: both the centralized (@invariant) and inline (@trigger ... id:)
declaration sites build through it.