Skip to main content
Amended by 0133: the HasUserStamps trait joins the trait table.

Context

Models opt into behaviour such as timestamps, delete policy, audit, search and encryption (0046). Each package that adds behaviour to a model or another class needs a name for it that tells a reader what it does. Laravel’s mix of traits and “concerns” with inconsistent names is the example to avoid.

Decision

  1. Reusable class behaviour is a trait, created with createTrait from @stackra/model. There are no “concerns”.
  2. Names follow one grammar:
  3. SoftDeletes is the one exception, kept for recognition.
  4. The trait list, each owned by one package:
  5. A new trait is added to this list in the change that creates it.

Consequences

  • A trait’s name tells a reader whether it adds columns, exposes a capability, or adapts a role.
  • @stackra/model owns the trait registry, so traits from many packages compose on one class.
  • Which package owns Auditable is the least certain row: audit is a service fed by events, and the trait only publishes.

Enforcement

Planned: a lint rule that every createTrait call’s name matches the grammar or is SoftDeletes, and a test that every trait is listed in this page’s table. Nothing checks this today.

Supersedes and sources

Supersedes: none. Sources: recommendation by the coordinating agent, 2026-10-19, accepted by the owner in Round 10; the content traits come from the owner’s CT2 and CT3 decisions in Round 11. The list is derived from 0046 and 0084.